1 /* 2 * Copyright (C) 2012 Alberto Irurueta Carro (alberto@irurueta.com) 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.irurueta.algebra; 17 18 /** 19 * Computes Singular Value matrix decomposition, which consists 20 * on factoring provided input matrix into three factors consisting of 2 21 * unary matrices and 1 diagonal matrix containing singular values, 22 * following next expression: A = U * S * V'. 23 * Where A is provided input matrix of size m-by-n, U is an m-by-n unary 24 * matrix, S is an n-by-n diagonal matrix containing singular values, and V' 25 * denotes the transpose/conjugate of V and is an n-by-n unary matrix, for 26 * m < n. 27 */ 28 @SuppressWarnings("DuplicatedCode") 29 public class SingularValueDecomposer extends Decomposer { 30 31 /** 32 * Constant defining default number of iterations to obtain convergence on 33 * singular value estimation. 34 */ 35 public static final int DEFAULT_MAX_ITERS = 30; 36 37 /** 38 * Constant defining minimum number of iterations allowed to obtain 39 * convergence on singular value estimation. 40 */ 41 public static final int MIN_ITERS = 1; 42 43 /** 44 * Constant defining minimum allowed value as threshold to determine 45 * whether a singular value is negligible or not. 46 */ 47 public static final double MIN_THRESH = 0.0; 48 49 /** 50 * Constant defining machine precision. 51 */ 52 public static final double EPS = 1e-12; 53 54 /** 55 * Internal storage of U. 56 */ 57 private Matrix u; 58 59 /** 60 * Internal storage of V. 61 */ 62 private Matrix v; 63 64 /** 65 * Internal storage of singular values. 66 */ 67 private double[] w; 68 69 /** 70 * Contains epsilon value, which indicates an estimation of numerical 71 * precision given by this machine. 72 */ 73 private final double eps; 74 75 /** 76 * Contains threshold used to determine whether a singular value can be 77 * neglected or not due to numerical precision errors. This can be used to 78 * determine effective rank of input matrix. 79 */ 80 private double tsh; 81 82 /** 83 * Member containing maximum number of iterations to obtain convergence of 84 * singular values estimation. 85 * If singular values do not converge on provided maximum number of 86 * iterations, then a NoConvergenceException will be thrown when calling 87 * decompose() method. 88 */ 89 private int maxIters; 90 91 /** 92 * Constructor of this class. 93 */ 94 public SingularValueDecomposer() { 95 super(); 96 maxIters = DEFAULT_MAX_ITERS; 97 u = v = null; 98 w = null; 99 eps = EPS; 100 } 101 102 /** 103 * Constructor of this class. 104 * 105 * @param maxIters Determines maximum number of iterations to be done when 106 * decomposing input matrix into singular values so that singular values 107 * converge properly. 108 */ 109 public SingularValueDecomposer(final int maxIters) { 110 super(); 111 this.maxIters = maxIters; 112 u = v = null; 113 w = null; 114 eps = EPS; 115 } 116 117 /** 118 * Constructor of this class. 119 * 120 * @param inputMatrix Reference to input matrix to be decomposed. 121 */ 122 public SingularValueDecomposer(final Matrix inputMatrix) { 123 super(inputMatrix); 124 maxIters = DEFAULT_MAX_ITERS; 125 u = v = null; 126 w = null; 127 eps = EPS; 128 } 129 130 /** 131 * Constructor of this class. 132 * 133 * @param inputMatrix Reference to input matrix to be decomposed. 134 * @param maxIters Determines maximum number of iterations to be done when 135 * decomposing input matrix into singular value so that singular values 136 * converge properly. 137 */ 138 public SingularValueDecomposer(final Matrix inputMatrix, final int maxIters) { 139 super(inputMatrix); 140 this.maxIters = maxIters; 141 u = v = null; 142 w = null; 143 eps = EPS; 144 } 145 146 /** 147 * Returns decomposer type corresponding to Singular Value decomposition. 148 * 149 * @return Decomposer type. 150 */ 151 @Override 152 public DecomposerType getDecomposerType() { 153 return DecomposerType.SINGULAR_VALUE_DECOMPOSITION; 154 } 155 156 /** 157 * Sets reference to input matrix to be decomposed. 158 * 159 * @param inputMatrix Reference to input matrix to be decomposed. 160 * @throws LockedException Exception thrown if attempting to call this 161 * method while this instance remains locked. 162 */ 163 @Override 164 public void setInputMatrix(final Matrix inputMatrix) throws LockedException { 165 super.setInputMatrix(inputMatrix); 166 u = v = null; 167 w = null; 168 } 169 170 /** 171 * Returns boolean indicating whether decomposition has been computed and 172 * results can be retrieved. 173 * Attempting to retrieve decomposition results when not available, will 174 * probably raise a NotAvailableException 175 * 176 * @return Boolean indicating whether decomposition has been computed and 177 * results can be retrieved. 178 */ 179 @Override 180 public boolean isDecompositionAvailable() { 181 return u != null || v != null || w != null; 182 } 183 184 /** 185 * This method computes Singular Value matrix decomposition, which consists 186 * on factoring provided input matrix into three factors consisting of 2 187 * unary matrices and 1 diagonal matrix containing singular values, 188 * following next expression: A = U * S * V'. 189 * Where A is provided input matrix of size m-by-n, U is an m-by-n unary 190 * matrix, S is an n-by-n diagonal matrix containing singular values, and V' 191 * denotes the transpose/conjugate of V and is an n-by-n unary matrix, for 192 * m < n. 193 * Note: Factors U, S and V will be accessible once Singular Value 194 * decomposition has been computed. 195 * Note: During execution of this method, Singular Value decomposition will 196 * be available and operations such as retrieving matrix factors, or 197 * computing rank of matrices among others will be able to be done. 198 * Attempting to call any of such operations before calling this method will 199 * raise a NotAvailableException because they require computation of 200 * SingularValue decomposition first. 201 * 202 * @throws NotReadyException Exception thrown if attempting to call this 203 * method when this instance is not ready (i.e. no input matrix has been 204 * provided). 205 * @throws LockedException Exception thrown if this decomposer is already 206 * locked before calling this method. Notice that this method will actually 207 * lock this instance while it is being executed. 208 * @throws DecomposerException Exception thrown if for any reason 209 * decomposition fails while being executed, like when convergence of 210 * results cannot be obtained, etc. 211 */ 212 @Override 213 public void decompose() throws NotReadyException, LockedException, DecomposerException { 214 215 if (!isReady()) { 216 throw new NotReadyException(); 217 } 218 if (isLocked()) { 219 throw new LockedException(); 220 } 221 222 locked = true; 223 224 final var m = inputMatrix.getRows(); 225 final var n = inputMatrix.getColumns(); 226 227 // copy input matrix into U 228 u = new Matrix(inputMatrix); 229 w = new double[n]; 230 try { 231 v = new Matrix(n, n); 232 } catch (final WrongSizeException ignore) { 233 // never happens 234 } 235 try { 236 internalDecompose(); 237 reorder(); 238 setNegligibleSingularValueThreshold(0.5 * Math.sqrt(m + n + 1.0) * w[0] * eps); 239 locked = false; 240 } catch (final DecomposerException e) { 241 u = v = null; 242 w = null; 243 locked = false; 244 throw e; 245 } 246 } 247 248 /** 249 * Returns maximum number of iterations to be done in order to obtain 250 * convergence of singular values when computing input matrix Singular 251 * Value Decomposition. 252 * 253 * @return Maximum number of iterations to obtain singular values 254 * convergence. 255 */ 256 public int getMaxIterations() { 257 return maxIters; 258 } 259 260 /** 261 * SSets maximum number of iterations to be done in order to obtain 262 * convergence of singular values when computing input matrix Singular 263 * Value Decomposition. 264 * Note: This parameter should rarely be modified because default value 265 * is usually good enough. 266 * Note: If convergence of singular values is not achieved within provided 267 * maximum number of iterations, a DecomposerException will be thrown when 268 * calling decompose(); 269 * 270 * @param maxIters Maximum number of iterations to obtain convergence of 271 * singular values. Provided value must be 1 or greater, otherwise an 272 * IllegalArgumentException will be thrown. 273 * @throws LockedException Exception thrown if attempting to call this 274 * method while this instance remains locked. 275 * @throws IllegalArgumentException Exception thrown if provided value 276 * for maxIters is out of valid range of values. 277 */ 278 public void setMaxIterations(final int maxIters) throws LockedException { 279 if (isLocked()) { 280 throw new LockedException(); 281 } 282 if (maxIters < MIN_ITERS) { 283 throw new IllegalArgumentException(); 284 } 285 286 this.maxIters = maxIters; 287 } 288 289 /** 290 * Returns threshold to be used for determining whether a singular value is 291 * negligible or not. 292 * This threshold can be used to consider a singular value as zero or not, 293 * since small singular values might appear in places where they should be 294 * zero because of rounding errors and machine precision. 295 * Singular values considered as zero determine aspects such as rank, 296 * nullability, null-space or range space. 297 * 298 * @return Threshold to be used for determining whether a singular value 299 * is negligible or not. 300 * @throws NotAvailableException Exception thrown if attempting to call 301 * this method before computing Singular Value decomposition. 302 * To avoid this exception call decompose() method first. 303 */ 304 public double getNegligibleSingularValueThreshold() throws NotAvailableException { 305 if (!isDecompositionAvailable()) { 306 throw new NotAvailableException(); 307 } 308 return tsh; 309 } 310 311 /** 312 * Returns a new matrix instance containing the left singular vector (U 313 * factor) from Singular Value matrix decomposition, which consists 314 * on decomposing a matrix using the following expression: 315 * A = U * S * V'. 316 * Where A is provided input matrix of size m-by-n and U is an m-by-n 317 * unary matrix for m < n. 318 * 319 * @return Matrix instance containing the left singular vectors from a 320 * Singular Value decomposition. 321 * @throws NotAvailableException Exception thrown if attempting to call 322 * this method before computing Singular Value decomposition. To avoid 323 * this exception call decompose() method first. 324 * @see #decompose() 325 */ 326 public Matrix getU() throws NotAvailableException { 327 if (!isDecompositionAvailable()) { 328 throw new NotAvailableException(); 329 } 330 return u; 331 } 332 333 /** 334 * Returns a new matrix instance containing the right singular vectors 335 * (V factor) from Singular Value matrix decomposition, which consists on 336 * decomposing a matrix using the following expression: 337 * A = U * S * V', 338 * Where A is provided input matrix of size m-by-n and V' denotes the 339 * transpose/conjugate of V, which is an n-by-n unary matrix for m < n. 340 * 341 * @return Matrix instance containing the right singular vectors from a 342 * Singular Value decomposition. 343 * @throws NotAvailableException Exception thrown if attempting to call 344 * this method before computing Singular Value decomposition. To avoid 345 * this exception call decompose() method first. 346 * @see #decompose() 347 */ 348 public Matrix getV() throws NotAvailableException { 349 if (!isDecompositionAvailable()) { 350 throw new NotAvailableException(); 351 } 352 return v; 353 } 354 355 /** 356 * Returns a new vector instance containing all singular values after 357 * decomposition. 358 * Returned vector is equal to the diagonal of S matrix within expression: 359 * A = U * S * V' where A is provided input matrix and S is a diagonal 360 * matrix containing singular values on its diagonal. 361 * 362 * @return singular values. 363 * @throws NotAvailableException if decomposition has not yet been computed. 364 */ 365 public double[] getSingularValues() throws NotAvailableException { 366 if (!isDecompositionAvailable()) { 367 throw new NotAvailableException(); 368 } 369 return w; 370 } 371 372 /** 373 * Copies diagonal matrix into provided instance containing all singular 374 * values on its diagonal after Singular Value matrix decomposition, which 375 * consists on decomposing a matrix using the following expression: 376 * A = U * S * V'. 377 * Where A is provided input matrix of size m-by-n and S is a diagonal 378 * matrix of size n-by-n for m < n. 379 * 380 * @param m matrix instance containing all singular values on its 381 * diagonal after execution of this method. 382 * @throws NotAvailableException Exception thrown if attempting to call 383 * this method before computing Singular Value decomposition. To avoid 384 * this exception call decompose() method first. 385 * @throws WrongSizeException if provided matrix does not have size n-by-n. 386 * @see #decompose() 387 */ 388 public void getW(final Matrix m) throws NotAvailableException, WrongSizeException { 389 if (!isDecompositionAvailable()) { 390 throw new NotAvailableException(); 391 } 392 if (m.getRows() != w.length || m.getColumns() != w.length) { 393 throw new WrongSizeException(); 394 } 395 Matrix.diagonal(w, m); 396 } 397 398 /** 399 * Returns a new diagonal matrix instance containing all singular values on 400 * its diagonal after Singular Value matrix decomposition, which consists 401 * on decomposing a matrix using the following expression: A = U * S * V'. 402 * Where A is provided input matrix of size m-by-n and S is a diagonal 403 * matrix of size n-by-n for m < n. 404 * 405 * @return Returned matrix instance containing all singular values on its 406 * diagonal. 407 * @throws NotAvailableException Exception thrown if attempting to call 408 * this method before computing Singular Value decomposition. To avoid 409 * this exception call decompose() method first. 410 * @see #decompose() 411 */ 412 public Matrix getW() throws NotAvailableException { 413 if (!isDecompositionAvailable()) { 414 throw new NotAvailableException(); 415 } 416 417 // copy S array into a new diagonal matrix 418 return Matrix.diagonal(w); 419 } 420 421 /** 422 * Returns the 2-norm of provided input matrix, which is equal to the highest 423 * singular value found after decomposition. This is also called the Ky Fan 424 * 1-norm. 425 * This norm is also equal to the square root of Frobenius norm of the 426 * squared provided input matrix. In other words: 427 * sqrt(norm(A' * A, 'fro')) in Matlab notation. 428 * Where A is provided input matrix and A' is its transpose, and hence 429 * A' * A can be considered the squared matrix of A, and Frobenius norm 430 * is defined as the square root of the sum of the squared elements of a 431 * matrix: sqr(sum(A(:).^2)) 432 * 433 * @return The 2-norm of provided input matrix, which is equal to the 434 * highest singular value. 435 * @throws NotAvailableException Exception thrown if attempting to call this 436 * method before computing Singular Value decomposition. To avoid this 437 * exception call decompose() method first. 438 * @see #decompose() 439 */ 440 public double getNorm2() throws NotAvailableException { 441 if (!isDecompositionAvailable()) { 442 throw new NotAvailableException(); 443 } 444 return w[0]; 445 } 446 447 /** 448 * Returns the condition number of provided input matrix found after 449 * decomposition. 450 * The condition number of a matrix measures the sensitivity of the 451 * solution of a system of linear equations to errors in the data. 452 * It gives an indication of the accuracy of the results from matrix 453 * inversion and the solution of a linear system of equations. 454 * A problem with a low condition number is said to be well-conditioned, 455 * whereas a problem with a high condition number is said to be 456 * ill-conditioned. 457 * The condition number is a property of a matrix and is not related to the 458 * algorithm or floating point accuracy of a machine to solve a linear 459 * system of equations or make matrix inversion. 460 * When solving a linear system of equations (A * X = b), one should think 461 * of the condition number as being (very roughly) the rate at which the 462 * solution x will change with respect to a change in b. 463 * Thus, if the condition number is large, even a small error in b may 464 * cause a large error in x. On the other hand, if the condition number is 465 * small then the error in x will not be much bigger than the error in b. 466 * One way to find the condition number is by using the ration of the 467 * maximal and minimal singular values of a matrix, which is what this 468 * method returns. 469 * 470 * @return The condition number of provided input matrix. 471 * @throws NotAvailableException Exception thrown if attempting to call this 472 * method before computing Singular Value decomposition. To avoid this 473 * exception call decompose() method first. 474 * @see #decompose() 475 */ 476 public double getConditionNumber() throws NotAvailableException { 477 return 1.0 / getReciprocalConditionNumber(); 478 } 479 480 /** 481 * Returns the inverse of the condition number, i.e. 1.0 / condition number. 482 * Hence, when reciprocal condition number is close to zero, input matrix 483 * will be ill-conditioned. 484 * For more information see getConditionNumber() 485 * 486 * @return Inverse of the condition number. 487 * @throws NotAvailableException Exception thrown if attempting to call 488 * this method before computing Singular Value decomposition. To avoid this 489 * exception call decompose() method first. 490 * @see #decompose() 491 * @see #getConditionNumber() 492 */ 493 public double getReciprocalConditionNumber() throws NotAvailableException { 494 if (!isDecompositionAvailable()) { 495 throw new NotAvailableException(); 496 } 497 498 final var columns = inputMatrix.getColumns(); 499 return (w[0] <= 0.0 || w[columns - 1] <= 0.0) ? 0.0 : w[columns - 1] / w[0]; 500 } 501 502 /** 503 * Returns effective numerical matrix rank. 504 * By definition rank of a matrix can be found as the number of non-zero 505 * singular values of such matrix found after decomposition. 506 * However, rounding error and machine precision may lead to small but non-zero 507 * singular values in a rank deficient matrix. 508 * This method tries to cope with such rounding errors by taking into 509 * account only those non-negligible singular values to determine input 510 * matrix rank. 511 * The Rank-nullity theorem states that for a matrix A of size m-by-n then: 512 * rank(A) + nullity(A) = n 513 * Where A is input matrix and n is the number of columns of such matrix. 514 * 515 * @param singularValueThreshold Threshold used to determine whether a 516 * singular value is negligible or not. 517 * @return Effective numerical matrix rank. 518 * @throws NotAvailableException Exception thrown if attempting to call this 519 * method before computing Singular Value decomposition. To avoid this 520 * exception call decompose() method first. 521 * @throws IllegalArgumentException Exception thrown if provided singular 522 * value threshold is negative. Returned singular values after decomposition 523 * are always positive, and hence, provided threshold should be a positive 524 * value close to zero. 525 * @see #decompose() 526 */ 527 public int getRank(final double singularValueThreshold) throws NotAvailableException { 528 if (!isDecompositionAvailable()) { 529 throw new NotAvailableException(); 530 } 531 if (singularValueThreshold < MIN_THRESH) { 532 throw new IllegalArgumentException(); 533 } 534 535 var r = 0; 536 for (var aW : w) { 537 if (aW > singularValueThreshold) { 538 r++; 539 } 540 } 541 return r; 542 } 543 544 /** 545 * Returns effective numerical matrix rank. 546 * By definition rank of a matrix can be found as the number of non-zero 547 * singular values of such matrix found after decomposition. 548 * However, rounding error and machine precision may lead to small but non-zero 549 * singular values in a rank deficient matrix. 550 * This method tries to cope with such rounding error by taking into account 551 * only those non-negligible singular values to determine input matrix rank. 552 * The Rank-nullity theorem states that for a matrix A of size m-by-n then: 553 * rank(A) + nullity(A) = n 554 * Where A is input matrix and n is number of columns of such matrix. 555 * Note: This method makes same actions as int getRank(double) 556 * except that singular value threshold is automatically computed by taking 557 * into account input matrix size, maximal singular value and machine 558 * precision. This threshold is good enough for most situations, and hence 559 * we discourage setting it manually. 560 * 561 * @return Effective numerical matrix rank. 562 * @throws NotAvailableException Exception thrown if attempting to call this 563 * method before computing Singular Value decomposition. To avoid this 564 * exception call decompose() method first. 565 * @see #decompose() 566 */ 567 public int getRank() throws NotAvailableException { 568 return getRank(getNegligibleSingularValueThreshold()); 569 } 570 571 /** 572 * Returns effective numerical matrix nullity. 573 * By definition nullity of a matrix can be found as the number of zero or 574 * negligible singular values of provided input matrix after decomposition. 575 * Rounding error and machine precision may lead to small but non-zero 576 * singular values in a rank deficient matrix. 577 * This method tries to cope with such rounding error by taking into account 578 * only those negligible singular values to determine input matrix nullity. 579 * The Rank-nullity theorem states that for a matrix A of size m-by-n then: 580 * rank(A) + nullity(A) = n 581 * Where A is input matrix and n is number of columns of such matrix. 582 * 583 * @param singularValueThreshold Threshold used to determine whether a 584 * singular value is negligible or not. 585 * @return Effective numerical matrix nullity. 586 * @throws NotAvailableException Exception thrown if attempting to call 587 * this method before computing Singular Value decomposition. 588 * @throws IllegalArgumentException Exception thrown if provided singular 589 * value threshold is negative. Returned singular values after decomposition 590 * are always positive, and hence, provided threshold should be a positive 591 * value close to zero. 592 * @see #decompose() 593 */ 594 public int getNullity(final double singularValueThreshold) throws NotAvailableException { 595 final var n = inputMatrix.getColumns(); 596 return n - getRank(singularValueThreshold); 597 } 598 599 /** 600 * Returns effective numerical matrix nullity. 601 * By definition nullity of a matrix can be found as the number of zero or 602 * negligible singular values of provided input matrix after decomposition. 603 * Rounding error and machine precision may lead to small but non-zero 604 * singular values in a rank deficient matrix. 605 * This method tries to cope with such rounding error by taking into account 606 * only those negligible singular values to determine input matrix nullity. 607 * The Rank-nullity theorem states that for a matrix A of size m-by-n then: 608 * rank(A) + nullity(A) = n 609 * Where A is input matrix and n is number of columns of such matrix. 610 * Note: This method makes the same actions as int getNullity(double) except 611 * that singular value threshold is automatically computed by taking into 612 * account input matrix size, maximal singular value and machine precision. 613 * This threshold is good enough for most situations, and hence we 614 * discourage setting it manually. 615 * 616 * @return Effective numerical matrix nullity. 617 * @throws NotAvailableException Exception thrown if attempting to call this 618 * method before computing Singular Value decomposition. To avoid this 619 * exception call decompose() method first. 620 * @see #decompose() 621 */ 622 public int getNullity() throws NotAvailableException { 623 return getNullity(getNegligibleSingularValueThreshold()); 624 } 625 626 /** 627 * Sets into provided range matrix the Range space of provided input matrix, 628 * which spans a subspace of dimension equal to the rank of input matrix. 629 * Range space is equal to the columns of U corresponding to non-negligible 630 * singular values. 631 * 632 * @param singularValueThreshold Threshold used to determine whether a 633 * singular value is negligible or not. 634 * @param range Matrix containing Range space of provided input matrix. 635 * @throws NotAvailableException Exception thrown if input matrix has rank 636 * zero or also if attempting to call this method before computing Singular 637 * Value decomposition. To avoid this exception call decompose() method 638 * first and make sure that input matrix has non-zero rank. 639 * @throws IllegalArgumentException Exception thrown if provided singular 640 * value threshold is negative. Returned singular values after decomposition 641 * are always positive, and hence, provided threshold should be a positive 642 * near to zero value. 643 */ 644 public void getRange(final double singularValueThreshold, final Matrix range) throws NotAvailableException { 645 if (!isDecompositionAvailable()) { 646 throw new NotAvailableException(); 647 } 648 final var rank = getRank(singularValueThreshold); 649 internalGetRange(rank, singularValueThreshold, range); 650 } 651 652 /** 653 * Sets into provided range matrix the Range space of provided input matrix, 654 * which spans a subspace of dimension equal to the rank of input matrix. 655 * Range space is equal to the columns of U corresponding to non-negligible 656 * singular values. 657 * This method performs same actions as getRange(double, Matrix) except that 658 * singular value threshold is automatically computed by taking into account 659 * input matrix size, maximal singular value and machine precision. 660 * This threshold is good enough for most situations, and hence we 661 * discourage setting it manually. 662 * 663 * @param range Matrix containing Range space of provided input matrix. 664 * @throws NotAvailableException Exception thrown if input matrix has rank 665 * zero or also if attempting to call this method before computing Singular 666 * Value decomposition. To avoid this exception call decompose() method 667 * first and make sure that input matrix has non-zero rank. 668 * @throws IllegalArgumentException Exception thrown if provided singular 669 * value threshold is negative. Returned singular values after decomposition 670 * are always positive, and hence, provided threshold should be a positive 671 * near to zero value. 672 */ 673 public void getRange(final Matrix range) throws NotAvailableException { 674 getRange(getNegligibleSingularValueThreshold(), range); 675 } 676 677 /** 678 * Returns matrix containing Range space of provided input matrix, which 679 * spans a subspace of dimension equal to the rank of input matrix. 680 * Range space is equal to the columns of U corresponding to non-negligible 681 * singular values. 682 * 683 * @param singularValueThreshold Threshold used to determine whether a 684 * singular value is negligible or not. 685 * @return Matrix containing Range space of provided input matrix. 686 * @throws NotAvailableException Exception thrown if input matrix has rank 687 * zero or also if attempting to call this method before computing Singular 688 * Value decomposition. To avoid this exception call decompose() method 689 * first and make sure that input matrix has non-zero rank. 690 * @throws IllegalArgumentException Exception thrown if provided singular 691 * value threshold is negative. Returned singular values after decomposition 692 * are always positive, and hence, provided threshold should be a positive 693 * near to zero value. 694 */ 695 public Matrix getRange(final double singularValueThreshold) throws NotAvailableException { 696 if (!isDecompositionAvailable()) { 697 throw new NotAvailableException(); 698 } 699 final var rows = inputMatrix.getRows(); 700 final var rank = getRank(singularValueThreshold); 701 702 Matrix out; 703 try { 704 out = new Matrix(rows, rank); 705 } catch (final WrongSizeException e) { 706 throw new NotAvailableException(e); 707 } 708 internalGetRange(rank, singularValueThreshold, out); 709 return out; 710 } 711 712 /** 713 * Return matrix containing Range space of provided input matrix, which 714 * spans a subspace of dimension equal to the rank of input matrix. 715 * Range space is equal to the columns of U corresponding to non-negligible 716 * singular values. 717 * This method performs same actions as Matrix getRange(double) except that 718 * singular value threshold is automatically computed by taking into account 719 * input matrix size, maximal singular value and machine precision. 720 * This threshold is good enough for most situations, and hence we 721 * discourage setting it manually. 722 * 723 * @return Matrix containing Range space of provided input matrix 724 * @throws NotAvailableException Exception thrown if input matrix has rank 725 * zero or also if attempting to call this method before computing Singular 726 * Value decomposition. To avoid this exception call decompose() method 727 * first and make sure that input matrix has non-zero rank. 728 */ 729 public Matrix getRange() throws NotAvailableException { 730 return getRange(getNegligibleSingularValueThreshold()); 731 } 732 733 /** 734 * Sets into provided matrix null-space of provided input matrix, which spans 735 * a subspace of dimension equal to the nullity of input matrix. Null-space 736 * is equal to the columns of V corresponding to negligible singular values. 737 * 738 * @param singularValueThreshold Threshold used to determine whether a 739 * singular value is negligible or not. 740 * @param nullspace Matrix containing null-space of provided input matrix. 741 * @throws NotAvailableException Exception thrown if input matrix has full 742 * rank, and hence its nullity is zero, or also if attempting to call this 743 * method before computing Singular Value decomposition. To avoid this 744 * exception call decompose() method first and make sure that input matrix 745 * is rank deficient. 746 * @throws IllegalArgumentException Exception thrown if provided singular 747 * value threshold is negative. Returned singular values after decomposition 748 * are always positive, and hence, provided threshold should be a positive 749 * near to zero value. 750 */ 751 public void getNullspace(final double singularValueThreshold, final Matrix nullspace) 752 throws NotAvailableException { 753 if (!isDecompositionAvailable()) { 754 throw new NotAvailableException(); 755 } 756 final var nullity = getNullity(singularValueThreshold); 757 internalGetNullspace(nullity, singularValueThreshold, nullspace); 758 } 759 760 /** 761 * Sets into provided matrix null-space of provided input matrix, which spans 762 * a subspace of dimension equal to the nullity of input matrix. Null-space 763 * is equal to the columns of V corresponding to negligible singular values. 764 * This method performs same actions as getNullspace(double, Matrix) except 765 * that singular value threshold is automatically computed by taking into 766 * account input matrix size, maximal singular value and machine precision. 767 * This threshold is good enough for most situations, and hence we 768 * discourage setting it manually. 769 * 770 * @param nullspace Matrix containing null-space of provided input matrix. 771 * @throws NotAvailableException Exception thrown if input matrix has full 772 * rank, and hence its nullity is zero, or also if attempting to call this 773 * method before computing Singular Value decomposition. To avoid this 774 * exception call decompose() method first and make sure that input matrix 775 * is rank deficient. 776 * @throws IllegalArgumentException Exception thrown if provided singular 777 * value threshold is negative. Returned singular values after decomposition 778 * are always positive, and hence, provided threshold should be a positive 779 * near to zero value. 780 */ 781 public void getNullspace(final Matrix nullspace) throws NotAvailableException { 782 getNullspace(getNegligibleSingularValueThreshold(), nullspace); 783 } 784 785 /** 786 * Returns matrix containing null-space of provided input matrix, which spans 787 * a subspace of dimension equal to the nullity of input matrix. Null-space 788 * is equal to the columns of V corresponding to negligible singular values. 789 * 790 * @param singularValueThreshold Threshold used to determine whether a 791 * singular value is negligible or not. 792 * @return Matrix containing null-space of provided input matrix. 793 * @throws NotAvailableException Exception thrown if input matrix has full 794 * rank, and hence its nullity is zero, or also if attempting to call this 795 * method before computing Singular Value decomposition. To avoid this 796 * exception call decompose() method first and make sure that input matrix 797 * is rank deficient. 798 * @throws IllegalArgumentException Exception thrown if provided singular 799 * value threshold is negative. Returned singular values after decomposition 800 * are always positive, and hence, provided threshold should be a positive 801 * near to zero value. 802 */ 803 public Matrix getNullspace(final double singularValueThreshold) throws NotAvailableException { 804 if (!isDecompositionAvailable()) { 805 throw new NotAvailableException(); 806 } 807 808 final var columns = inputMatrix.getColumns(); 809 final var nullity = getNullity(singularValueThreshold); 810 Matrix out; 811 try { 812 out = new Matrix(columns, nullity); 813 } catch (final WrongSizeException e) { 814 throw new NotAvailableException(e); 815 } 816 internalGetNullspace(nullity, singularValueThreshold, out); 817 return out; 818 } 819 820 /** 821 * Returns matrix containing null-space of provided input matrix, which 822 * spans a subspace of dimension equal to the nullity of input matrix. 823 * Null-space is equal to the columns of V corresponding to negligible 824 * singular values. 825 * 826 * @return Matrix containing null-space of provided input matrix. 827 * This method performs same actions as Matrix {@link #getNullspace(double)} except 828 * that singular value threshold is automatically computed by taking into 829 * account input matrix size, maximal singular value and machine precision. 830 * This threshold is good enough for most situations, and hence we 831 * discourage setting it manually. 832 * @throws NotAvailableException Exception thrown if input matrix has full 833 * rank, and hence its nullity is zero, or also if attempting to call this 834 * method before computing Singular Value decomposition. To avoid this 835 * exception call decompose() method first and make sure that input matrix 836 * is rank deficient. 837 */ 838 public Matrix getNullspace() throws NotAvailableException { 839 return getNullspace(getNegligibleSingularValueThreshold()); 840 } 841 842 /** 843 * Solves a linear system of equations of the following form: A * X = B 844 * using the pseudo-inverse to find the least squares solution. 845 * Where A is the input matrix provided for Singular Value decomposition, 846 * X is the solution to the system of equations, and B is the parameters 847 * matrix. 848 * Note: This method can be reused for different b matrices without having 849 * to recompute Singular Value decomposition on the same input matrix. 850 * Note: Provided b matrix must have the same number of rows as provided 851 * input matrix A, otherwise a WrongSizeException will be raised. 852 * Note: In order to execute this method, a Singular Value decomposition 853 * must be available, otherwise a NotAvailableException will be raised. In 854 * order to avoid this exception call decompose() method first. 855 * Note: Provided result matrix will be resized if needed 856 * 857 * @param b Parameters matrix that determine a linear system of equations. 858 * Provided matrix must have the same number of rows as provided input 859 * matrix for Singular Value decomposition. Besides, each column on 860 * parameters matrix will represent a new system of equations, whose 861 * solution will be returned on appropriate column as an output of this 862 * method. 863 * @param singularValueThreshold Threshold used to determine whether a 864 * singular value is negligible or not. 865 * @param result Matrix containing least squares solution of linear system 866 * of equations on each column for each column of provided parameters matrix 867 * b. 868 * @throws NotAvailableException Exception thrown if attempting to call this 869 * method before computing Singular Value decomposition. To avoid this 870 * exception call decompose() method first. 871 * @throws WrongSizeException Exception thrown if provided parameters matrix 872 * (b) does not have the same number of rows as input matrix being Singular 873 * Value decomposed. 874 * @throws IllegalArgumentException Exception thrown if provided singular 875 * value threshold is lower than minimum allowed value (MIN_THRESH). 876 * @see #decompose() 877 */ 878 public void solve(final Matrix b, final double singularValueThreshold, final Matrix result) 879 throws NotAvailableException, WrongSizeException { 880 if (!isDecompositionAvailable()) { 881 throw new NotAvailableException(); 882 } 883 884 if (b.getRows() != inputMatrix.getRows()) { 885 throw new WrongSizeException(); 886 } 887 888 if (singularValueThreshold < MIN_THRESH) { 889 throw new IllegalArgumentException(); 890 } 891 892 final var m = inputMatrix.getRows(); 893 final var n = inputMatrix.getColumns(); 894 final var p = b.getColumns(); 895 896 final var bcol = new double[m]; 897 double[] xx; 898 899 // resize result matrix if needed 900 if (result.getRows() != n || result.getColumns() != p) { 901 result.resize(n, p); 902 } 903 904 for (var j = 0; j < p; j++) { 905 for (var i = 0; i < m; i++) { 906 bcol[i] = b.getElementAt(i, j); 907 } 908 909 xx = solve(bcol, singularValueThreshold); 910 // set column j of X using values in vector xx 911 result.setSubmatrix(0, j, n - 1, j, xx); 912 } 913 } 914 915 /** 916 * Solves a linear system of equations of the following form: A * X = B 917 * using the pseudo-inverse to find the least squares solution. 918 * Where A is the input matrix provided for Singular Value decomposition, 919 * X is the solution to the system of equations, and B is the parameter 920 * matrix. 921 * Note: This method can be reused for different b matrices without having 922 * to recompute Singular Value decomposition on the same input matrix. 923 * Note: Provided b matrix must have the same number of rows as provided 924 * input matrix A, otherwise a WrongSizeException will be raised. 925 * Note: In order to execute this method, a Singular Value decomposition 926 * must be available, otherwise a NotAvailableException will be raised. In 927 * order to avoid this exception call decompose() method first. 928 * Note: This method performs same actions as Matrix solve(Matrix, double) 929 * except that singular value threshold is automatically computed by taking 930 * into account input matrix size, maximal singular value and machine 931 * precision. 932 * This threshold is good enough for most situations, and hence we 933 * discourage setting it manually. 934 * Note: Provided result matrix will be resized if needed 935 * 936 * @param b Parameters matrix that determines a linear system of equations. 937 * Provided matrix must have the same number of rows as provided input 938 * matrix for Singular Value decomposition. Besides, each column on 939 * parameters matrix will represent a new system of equations, whose 940 * solution will be returned on appropriate column as an output of this method. 941 * @param result Matrix containing least squares solution of linear system 942 * of equations on each column for each column of provided parameters matrix 943 * b. 944 * @throws NotAvailableException Exception thrown if attempting to call this 945 * method before computing Singular Value decomposition. To avoid this 946 * exception call decompose() method first. 947 * @throws WrongSizeException Exception thrown if provided parameters matrix 948 * (b) does not have the same number of rows as input matrix being Singular 949 * Value decomposed. 950 * @see #decompose() 951 */ 952 public void solve(final Matrix b, final Matrix result) throws NotAvailableException, WrongSizeException { 953 solve(b, getNegligibleSingularValueThreshold(), result); 954 } 955 956 /** 957 * Solves a linear system of equations of the following form: A * X = B 958 * using the pseudo-inverse to find the least squares solution. 959 * Where A is the input matrix provided for Singular Value decomposition, 960 * X is the solution to the system of equations, and B is the parameter 961 * matrix. 962 * Note: This method can be reused for different b matrices without having 963 * to recompute Singular Value decomposition on the same input matrix. 964 * Note: Provided b matrix must have the same number of rows as provided 965 * input matrix A, otherwise a WrongSizeException will be raised. 966 * Note: In order to execute this method, a Singular Value decomposition 967 * must be available, otherwise a NotAvailableException will be raised. In 968 * order to avoid this exception call decompose() method first. 969 * 970 * @param b Parameters matrix that determine a linear system of equations. 971 * Provided matrix must have the same number of rows as provided input 972 * matrix for Singular Value decomposition. Besides, each column on 973 * parameters matrix will represent a new system of equations, whose 974 * solution will be returned on appropriate column as an output of this 975 * method. 976 * @param singularValueThreshold Threshold used to determine whether a 977 * singular value is negligible or not. 978 * @return Matrix containing least squares solution of linear system of 979 * equations on each column for each column of provided parameters matrix b. 980 * @throws NotAvailableException Exception thrown if attempting to call this 981 * method before computing Singular Value decomposition. To avoid this 982 * exception call decompose() method first. 983 * @throws WrongSizeException Exception thrown if provided parameters matrix 984 * (b) does not have the same number of rows as input matrix being Singular 985 * Value decomposed. 986 * @throws IllegalArgumentException Exception thrown if provided singular 987 * value threshold is lower than minimum allowed value (MIN_THRESH). 988 * @see #decompose() 989 */ 990 public Matrix solve(final Matrix b, final double singularValueThreshold) 991 throws NotAvailableException, WrongSizeException { 992 final var n = inputMatrix.getColumns(); 993 final var p = b.getColumns(); 994 final var x = new Matrix(n, p); 995 solve(b, singularValueThreshold, x); 996 return x; 997 } 998 999 /** 1000 * Solves a linear system of equations of the following form: A * X = B 1001 * using the pseudo-inverse to find the least squares solution. 1002 * Where A is the input matrix provided for Singular Value decomposition, 1003 * X is the solution to the system of equations, and B is the parameter 1004 * matrix. 1005 * Note: This method can be reused for different b matrices without having 1006 * to recompute Singular Value decomposition on the same input matrix. 1007 * Note: Provided b matrix must have the same number of rows as provided 1008 * input matrix A, otherwise a WrongSizeException will be raised. 1009 * Note: In order to execute this method, a Singular Value decomposition 1010 * must be available, otherwise a NotAvailableException will be raised. In 1011 * order to avoid this exception call decompose() method first. 1012 * Note: This method performs same actions as Matrix solve(Matrix, double) 1013 * except that singular value threshold is automatically computed by taking 1014 * into account input matrix size, maximal singular value and machine 1015 * precision. 1016 * This threshold is good enough for most situations, and hence we 1017 * discourage setting it manually. 1018 * 1019 * @param b Parameters matrix that determines a linear system of equations. 1020 * Provided matrix must have the same number of rows as provided input 1021 * matrix for Singular Value decomposition. Besides, each column on 1022 * parameters matrix will represent a new system of equations, whose 1023 * solution will be returned on appropriate column as an output of this method. 1024 * @return Matrix containing least squares solution of linear system of 1025 * equations on each column for each column of provided parameters matrix b. 1026 * @throws NotAvailableException Exception thrown if attempting to call this 1027 * method before computing Singular Value decomposition. To avoid this 1028 * exception call decompose() method first. 1029 * @throws WrongSizeException Exception thrown if provided parameters matrix 1030 * (b) does not have the same number of rows as input matrix being Singular 1031 * Value decomposed. 1032 * @see #decompose() 1033 */ 1034 public Matrix solve(final Matrix b) throws NotAvailableException, 1035 WrongSizeException { 1036 return solve(b, getNegligibleSingularValueThreshold()); 1037 } 1038 1039 /** 1040 * Solves a linear system of equations of the following form: A * X = B 1041 * using the pseudo-inverse to find the least squares solution. 1042 * Where A i s the input matrix provided for Singular Value decomposition, 1043 * X is the solution to the system of equations, and B is the parameters 1044 * array. 1045 * Note: This method can be reused for different b arrays without having 1046 * to recompute Singular Value decomposition on the same input matrix. 1047 * Note: Provided b array must have the same length as the number of rows 1048 * on provided input matrix A, otherwise a WrongSizeException will be 1049 * raised. 1050 * Note: In order to execute this method, a Singular Value decomposition 1051 * must be available, otherwise a NotAvailableException will be raised. In 1052 * order to avoid this exception call decompose() method first. 1053 * 1054 * @param b Parameters array that determines a linear system of equations. 1055 * Provided array must have the same length as number of rows on provided 1056 * input matrix for Singular Value decomposition. 1057 * @param singularValueThreshold Threshold used to determine whether a 1058 * singular value is negligible or not. 1059 * @param result Vector where least squares solution of linear system of 1060 * equations for provided parameters array b will be stored. 1061 * @throws NotAvailableException Exception thrown if attempting to call this 1062 * method before computing SingularValue decomposition. 1063 * To avoid this exception call decompose() method first. 1064 * @throws WrongSizeException Exception thrown if provided parameters array 1065 * (b) does not have the same length as number of rows on input matrix being 1066 * Singular Value decomposed or if provided result array does not have the 1067 * same length as the number of columns on input matrix. 1068 * @throws IllegalArgumentException Exception thrown if provided singular 1069 * value threshold is lower than minimum allowed value (MIN_THRESH). 1070 * @see #decompose() 1071 */ 1072 public void solve(final double[] b, final double singularValueThreshold, 1073 final double[] result) throws NotAvailableException, WrongSizeException { 1074 if (!isDecompositionAvailable()) { 1075 throw new NotAvailableException(); 1076 } 1077 1078 if (b.length != inputMatrix.getRows()) { 1079 throw new WrongSizeException(); 1080 } 1081 1082 if (singularValueThreshold < MIN_THRESH) { 1083 throw new IllegalArgumentException(); 1084 } 1085 1086 final var m = inputMatrix.getRows(); 1087 final var n = inputMatrix.getColumns(); 1088 1089 if (result.length != n) { 1090 throw new WrongSizeException(); 1091 } 1092 1093 double s; 1094 final var tmp = new double[n]; 1095 1096 for (var j = 0; j < n; j++) { 1097 s = 0.0; 1098 if (w[j] > singularValueThreshold) { 1099 for (var i = 0; i < m; i++) { 1100 s += u.getElementAt(i, j) * b[i]; 1101 } 1102 s /= w[j]; 1103 } 1104 tmp[j] = s; 1105 } 1106 for (var j = 0; j < n; j++) { 1107 s = 0.0; 1108 for (var jj = 0; jj < n; jj++) { 1109 s += v.getElementAt(j, jj) * tmp[jj]; 1110 } 1111 result[j] = s; 1112 } 1113 } 1114 1115 /** 1116 * Solves a linear system of equations of the following form: A * X = B 1117 * using the pseudo-inverse to find the least squares solution. 1118 * Where A i s the input matrix provided for Singular Value decomposition, 1119 * X is the solution to the system of equations, and B is the parameters 1120 * array. 1121 * Note: This method can be reused for different b arrays without having 1122 * to recompute Singular Value decomposition on the same input matrix. 1123 * Note: Provided b array must have the same length as the number of rows 1124 * on provided input matrix A, otherwise a WrongSizeException will be 1125 * raised. 1126 * Note: In order to execute this method, a Singular Value decomposition 1127 * must be available, otherwise a NotAvailableException will be raised. In 1128 * order to avoid this exception call decompose() method first. 1129 * Note: this method performs same actions as double[] solve(double[], 1130 * double) except that singular value threshold is automatically computed by 1131 * taking into account input matrix size, maximal singular value and machine 1132 * precision. 1133 * This threshold is good enough for most situations, and hence we discourage 1134 * setting it manually. 1135 * 1136 * @param b Parameters array that determines a linear system of equations. 1137 * Provided array must have the same length as number of rows on provided 1138 * input matrix for Singular Value decomposition. 1139 * @param result Vector where least squares solution of linear system of 1140 * equations for provided parameters array b will be stored. 1141 * @throws NotAvailableException Exception thrown if attempting to call this 1142 * method before computing SingularValue decomposition. 1143 * To avoid this exception call decompose() method first. 1144 * @throws WrongSizeException Exception thrown if provided parameters array 1145 * (b) does not have the same length as number of rows on input matrix being 1146 * Singular Value decomposed or if provided result array does not have the 1147 * same length as the number of columns on input matrix. 1148 * @throws IllegalArgumentException Exception thrown if provided singular 1149 * value threshold is lower than minimum allowed value (MIN_THRESH). 1150 * @see #decompose() 1151 */ 1152 public void solve(final double[] b, final double[] result) throws NotAvailableException, WrongSizeException { 1153 solve(b, getNegligibleSingularValueThreshold(), result); 1154 } 1155 1156 /** 1157 * Solves a linear system of equations of the following form: A * X = B 1158 * using the pseudo-inverse to find the least squares solution. 1159 * Where A i s the input matrix provided for Singular Value decomposition, 1160 * X is the solution to the system of equations, and B is the parameters 1161 * array. 1162 * Note: This method can be reused for different b arrays without having 1163 * to recompute Singular Value decomposition on the same input matrix. 1164 * Note: Provided b array must have the same length as the number of rows 1165 * on provided input matrix A, otherwise a WrongSizeException will be 1166 * raised. 1167 * Note: In order to execute this method, a Singular Value decomposition 1168 * must be available, otherwise a NotAvailableException will be raised. In 1169 * order to avoid this exception call decompose() method first. 1170 * 1171 * @param b Parameters array that determines a linear system of equations. 1172 * Provided array must have the same length as number of rows on provided 1173 * input matrix for Singular Value decomposition. 1174 * @param singularValueThreshold Threshold used to determine whether a 1175 * singular value is negligible or not. 1176 * @return Vector containing least squares solution of linear system of 1177 * equations for provided parameters array b. 1178 * @throws NotAvailableException Exception thrown if attempting to call this 1179 * method before computing SingularValue decomposition. 1180 * To avoid this exception call decompose() method first. 1181 * @throws WrongSizeException Exception thrown if provided parameters array 1182 * (b) does not have the same length as number of rows on input matrix being 1183 * Singular Value decomposed. 1184 * @throws IllegalArgumentException Exception thrown if provided singular 1185 * value threshold is lower than minimum allowed value (MIN_THRESH). 1186 * @see #decompose() 1187 */ 1188 public double[] solve(final double[] b, final double singularValueThreshold) throws NotAvailableException, 1189 WrongSizeException { 1190 final var n = inputMatrix.getColumns(); 1191 1192 final var x = new double[n]; 1193 solve(b, singularValueThreshold, x); 1194 return x; 1195 } 1196 1197 /** 1198 * Solves a linear system of equations of the following form: A * X = B 1199 * using the pseudo-inverse to find the least squares solution. 1200 * Where A i s the input matrix provided for Singular Value decomposition, 1201 * X is the solution to the system of equations, and B is the parameters 1202 * array. 1203 * Note: This method can be reused for different b arrays without having 1204 * to recompute Singular Value decomposition on the same input matrix. 1205 * Note: Provided b array must have the same length as the number of rows 1206 * on provided input matrix A, otherwise a WrongSizeException will be 1207 * raised. 1208 * Note: In order to execute this method, a Singular Value decomposition 1209 * must be available, otherwise a NotAvailableException will be raised. In 1210 * order to avoid this exception call decompose() method first. 1211 * Note: this method performs same actions as double[] solve(double[], 1212 * double) except that singular value threshold is automatically computed by 1213 * taking into account input matrix size, maximal singular value and machine 1214 * precision. 1215 * This threshold is good enough for most situations, and hence we discourage 1216 * setting it manually. 1217 * 1218 * @param b Parameters array that determines a linear system of equations. 1219 * Provided array must have the same length as number of rows on provided 1220 * input matrix for Singular Value decomposition. 1221 * @return Vector containing least squares solution of linear system of 1222 * equations for provided parameters array b. 1223 * @throws NotAvailableException Exception thrown if attempting to call this 1224 * method before computing SingularValue decomposition. 1225 * To avoid this exception call decompose() method first. 1226 * @throws WrongSizeException Exception thrown if provided parameters array 1227 * (b) does not have the same length as number of rows on input matrix being 1228 * Singular Value decomposed. 1229 * @see #decompose() 1230 */ 1231 public double[] solve(final double[] b) throws NotAvailableException, WrongSizeException { 1232 return solve(b, getNegligibleSingularValueThreshold()); 1233 } 1234 1235 /** 1236 * This method is called internally by decompose(), and actually computes 1237 * Singular Value Decomposition. 1238 * However, algorithm implemented in this algorithm does not ensure that 1239 * singular values are ordered from maximal to minimal, and hence reorder() 1240 * method is called next within decompose() as well. 1241 * 1242 * @throws NoConvergenceException Exception thrown if singular value 1243 * estimation does not converge within provided number of maximum 1244 * iterations. 1245 */ 1246 @SuppressWarnings("DuplicatedCode") 1247 private void internalDecompose() throws NoConvergenceException { 1248 final var m = inputMatrix.getRows(); 1249 final var n = inputMatrix.getColumns(); 1250 1251 boolean flag; 1252 int i; 1253 int its; 1254 int j; 1255 int jj; 1256 int k; 1257 var l = 0; 1258 var nm = 0; 1259 double anorm; 1260 double c; 1261 double f; 1262 double g; 1263 double h; 1264 double s; 1265 double scale; 1266 double x; 1267 double y; 1268 double z; 1269 var rv1 = new double[n]; 1270 1271 // Householder reduction to bi-diagonal form 1272 g = scale = anorm = 0.0; 1273 for (i = 0; i < n; i++) { 1274 l = i + 2; 1275 rv1[i] = scale * g; 1276 g = s = scale = 0.0; 1277 if (i < m) { 1278 for (k = i; k < m; k++) { 1279 scale += Math.abs(u.getElementAt(k, i)); 1280 } 1281 if (scale != 0.0) { 1282 for (k = i; k < m; k++) { 1283 u.setElementAt(k, i, u.getElementAt(k, i) / scale); 1284 s += Math.pow(u.getElementAt(k, i), 2.0); 1285 } 1286 f = u.getElementAt(i, i); 1287 g = -sign(Math.sqrt(s), f); 1288 h = f * g - s; 1289 u.setElementAt(i, i, f - g); 1290 for (j = l - 1; j < n; j++) { 1291 for (s = 0.0, k = i; k < m; k++) { 1292 s += u.getElementAt(k, i) * u.getElementAt(k, j); 1293 } 1294 f = s / h; 1295 for (k = i; k < m; k++) { 1296 u.setElementAt(k, j, u.getElementAt(k, j) + f * u.getElementAt(k, i)); 1297 } 1298 } 1299 for (k = i; k < m; k++) { 1300 u.setElementAt(k, i, u.getElementAt(k, i) * scale); 1301 } 1302 } 1303 } 1304 w[i] = scale * g; 1305 g = s = scale = 0.0; 1306 if (i + 1 <= m && i + 1 != n) { 1307 for (k = l - 1; k < n; k++) { 1308 scale += Math.abs(u.getElementAt(i, k)); 1309 } 1310 if (scale != 0.0) { 1311 for (k = l - 1; k < n; k++) { 1312 u.setElementAt(i, k, u.getElementAt(i, k) / scale); 1313 s += Math.pow(u.getElementAt(i, k), 2.0); 1314 } 1315 f = u.getElementAt(i, l - 1); 1316 g = -sign(Math.sqrt(s), f); 1317 h = f * g - s; 1318 u.setElementAt(i, l - 1, f - g); 1319 for (k = l - 1; k < n; k++) { 1320 rv1[k] = u.getElementAt(i, k) / h; 1321 } 1322 for (j = l - 1; j < m; j++) { 1323 for (s = 0.0, k = l - 1; k < n; k++) { 1324 s += u.getElementAt(j, k) * u.getElementAt(i, k); 1325 } 1326 for (k = l - 1; k < n; k++) { 1327 u.setElementAt(j, k, u.getElementAt(j, k) + s * rv1[k]); 1328 } 1329 } 1330 for (k = l - 1; k < n; k++) { 1331 u.setElementAt(i, k, u.getElementAt(i, k) * scale); 1332 } 1333 } 1334 } 1335 anorm = Math.max(anorm, Math.abs(w[i]) + Math.abs(rv1[i])); 1336 } 1337 1338 // Accumulation of right-hand transformations 1339 for (i = n - 1; i >= 0; i--) { 1340 if (i < (n - 1)) { 1341 if (g != 0.0) { 1342 // Double division to avoid possible underflow. 1343 for (j = l; j < n; j++) { 1344 v.setElementAt(j, i, u.getElementAt(i, j) / u.getElementAt(i, l) / g); 1345 } 1346 for (j = l; j < n; j++) { 1347 for (s = 0.0, k = l; k < n; k++) { 1348 s += u.getElementAt(i, k) * v.getElementAt(k, j); 1349 } 1350 for (k = l; k < n; k++) { 1351 v.setElementAt(k, j, v.getElementAt(k, j) + s * v.getElementAt(k, i)); 1352 } 1353 } 1354 } 1355 for (j = l; j < n; j++) { 1356 v.setElementAt(i, j, 0.0); 1357 v.setElementAt(j, i, 0.0); 1358 } 1359 } 1360 v.setElementAt(i, i, 1.0); 1361 g = rv1[i]; 1362 l = i; 1363 } 1364 1365 // Accumulation of left-hand transformations 1366 for (i = Math.min(m, n) - 1; i >= 0; i--) { 1367 l = i + 1; 1368 g = w[i]; 1369 for (j = l; j < n; j++) { 1370 u.setElementAt(i, j, 0.0); 1371 } 1372 if (g != 0.0) { 1373 g = 1.0 / g; 1374 for (j = l; j < n; j++) { 1375 for (s = 0.0, k = l; k < m; k++) { 1376 s += u.getElementAt(k, i) * u.getElementAt(k, j); 1377 } 1378 f = (s / u.getElementAt(i, i)) * g; 1379 for (k = i; k < m; k++) { 1380 u.setElementAt(k, j, u.getElementAt(k, j) + f * u.getElementAt(k, i)); 1381 } 1382 } 1383 for (j = i; j < m; j++) { 1384 u.setElementAt(j, i, u.getElementAt(j, i) * g); 1385 } 1386 } else { 1387 for (j = i; j < m; j++) { 1388 u.setElementAt(j, i, 0.0); 1389 } 1390 } 1391 u.setElementAt(i, i, u.getElementAt(i, i) + 1.0); 1392 } 1393 1394 // Diagonalization of the bi-diagonal form: Loop over singular values and 1395 // over allowed iterations. 1396 for (k = n - 1; k >= 0; k--) { 1397 for (its = 0; its < maxIters; its++) { 1398 flag = true; 1399 // Test for splitting 1400 // Note that rrv1[0] is always zero 1401 for (l = k; l >= 0; l--) { 1402 nm = l - 1; 1403 if (l == 0 || Math.abs(rv1[l]) <= eps * anorm) { 1404 flag = false; 1405 } 1406 1407 if (!flag || Math.abs(w[nm]) <= eps * anorm) { 1408 break; 1409 } 1410 } 1411 // Cancellation of rv1[0] if l > 1 1412 if (flag) { 1413 c = 0.0; 1414 s = 1.0; 1415 for (i = l; i < k + 1; i++) { 1416 f = s * rv1[i]; 1417 rv1[i] = c * rv1[i]; 1418 if (Math.abs(f) <= eps * anorm) { 1419 break; 1420 } 1421 g = w[i]; 1422 h = pythag(f, g); 1423 w[i] = h; 1424 if (h != 0.0) { 1425 h = 1.0 / h; 1426 } else { 1427 h = Double.MAX_VALUE; 1428 } 1429 c = g * h; 1430 s = -f * h; 1431 for (j = 0; j < m; j++) { 1432 y = u.getElementAt(j, nm); 1433 z = u.getElementAt(j, i); 1434 u.setElementAt(j, nm, y * c + z * s); 1435 u.setElementAt(j, i, z * c - y * s); 1436 } 1437 } 1438 } 1439 z = w[k]; 1440 // Convergence. 1441 if (l == k) { 1442 // Singular value is made non-negative 1443 if (z < 0.0) { 1444 w[k] = -z; 1445 for (j = 0; j < n; j++) { 1446 v.setElementAt(j, k, -v.getElementAt(j, k)); 1447 } 1448 } 1449 break; 1450 } 1451 if (its == maxIters - 1) { 1452 throw new NoConvergenceException(); 1453 } 1454 1455 // Shift from bottom 2-by-2 minor. 1456 x = w[l]; 1457 nm = k - 1; 1458 y = w[nm]; 1459 g = rv1[nm]; 1460 h = rv1[k]; 1461 f = ((y - z) * (y + z) + (g - h) * (g + h)) / (2.0 * h * y); 1462 g = pythag(f, 1.0); 1463 f = ((x - z) * (x + z) + h * ((y / (f + sign(g, f))) - h)) / x; 1464 c = s = 1.0; 1465 // Next QR transformation 1466 for (j = l; j <= nm; j++) { 1467 i = j + 1; 1468 g = rv1[i]; 1469 y = w[i]; 1470 h = s * g; 1471 g = c * g; 1472 z = pythag(f, h); 1473 rv1[j] = z; 1474 if (z != 0.0) { 1475 c = f / z; 1476 s = h / z; 1477 } else { 1478 c = Math.signum(f) * Double.MAX_VALUE; 1479 s = Math.signum(h) * Double.MAX_VALUE; 1480 } 1481 f = x * c + g * s; 1482 g = g * c - x * s; 1483 h = y * s; 1484 y *= c; 1485 for (jj = 0; jj < n; jj++) { 1486 x = v.getElementAt(jj, j); 1487 z = v.getElementAt(jj, i); 1488 v.setElementAt(jj, j, x * c + z * s); 1489 v.setElementAt(jj, i, z * c - x * s); 1490 } 1491 z = pythag(f, h); 1492 // Rotation can be arbitrary if z = 0 1493 w[j] = z; 1494 if (z != 0.0) { 1495 z = 1.0 / z; 1496 c = f * z; 1497 s = h * z; 1498 } 1499 f = c * g + s * y; 1500 x = c * y - s * g; 1501 for (jj = 0; jj < m; jj++) { 1502 y = u.getElementAt(jj, j); 1503 z = u.getElementAt(jj, i); 1504 u.setElementAt(jj, j, y * c + z * s); 1505 u.setElementAt(jj, i, z * c - y * s); 1506 } 1507 } 1508 rv1[l] = 0.0; 1509 rv1[k] = f; 1510 w[k] = x; 1511 } 1512 } 1513 } 1514 1515 /** 1516 * Reorders singular values from maximal to minimal, and also reorders 1517 * columns and rows of U and V to ensure that Singular Value Decomposition 1518 * still remains valid. 1519 */ 1520 private void reorder() { 1521 final var m = inputMatrix.getRows(); 1522 final var n = inputMatrix.getColumns(); 1523 1524 int i; 1525 int j; 1526 int k; 1527 int s; 1528 var inc = 1; 1529 double sw; 1530 final var su = new double[m]; 1531 final var sv = new double[n]; 1532 1533 do { 1534 inc *= 3; 1535 inc++; 1536 } while (inc <= n); 1537 do { 1538 inc /= 3; 1539 for (i = inc; i < n; i++) { 1540 sw = w[i]; 1541 for (k = 0; k < m; k++) { 1542 su[k] = u.getElementAt(k, i); 1543 } 1544 for (k = 0; k < n; k++) { 1545 sv[k] = v.getElementAt(k, i); 1546 } 1547 j = i; 1548 while (w[j - inc] < sw) { 1549 w[j] = w[j - inc]; 1550 for (k = 0; k < m; k++) { 1551 u.setElementAt(k, j, u.getElementAt(k, j - inc)); 1552 } 1553 for (k = 0; k < n; k++) { 1554 v.setElementAt(k, j, v.getElementAt(k, j - inc)); 1555 } 1556 j -= inc; 1557 if (j < inc) { 1558 break; 1559 } 1560 } 1561 w[j] = sw; 1562 for (k = 0; k < m; k++) { 1563 u.setElementAt(k, j, su[k]); 1564 } 1565 for (k = 0; k < n; k++) { 1566 v.setElementAt(k, j, sv[k]); 1567 } 1568 } 1569 } while (inc > 1); 1570 1571 for (k = 0; k < n; k++) { 1572 s = 0; 1573 for (i = 0; i < m; i++) { 1574 if (u.getElementAt(i, k) < 0.0) { 1575 s++; 1576 } 1577 } 1578 for (j = 0; j < n; j++) { 1579 if (v.getElementAt(j, k) < 0.0) { 1580 s++; 1581 } 1582 } 1583 if (s > (m + n) / 2) { 1584 for (i = 0; i < m; i++) { 1585 u.setElementAt(i, k, -u.getElementAt(i, k)); 1586 } 1587 for (j = 0; j < n; j++) { 1588 v.setElementAt(j, k, -v.getElementAt(j, k)); 1589 } 1590 } 1591 } 1592 } 1593 1594 /** 1595 * Sets threshold to be used to determine whether a singular value is 1596 * negligible or not. 1597 * This threshold can be used to consider a singular value as zero or not, 1598 * since small singular values might appear in places where they should be 1599 * zero because of rounding errors and machine precision. 1600 * Singular values considered as zero determine aspects such as rank, 1601 * nullability, null-space or range space. 1602 * 1603 * @param threshold Threshold to be used to determine whether a singular 1604 * value is negligible or not. 1605 */ 1606 private void setNegligibleSingularValueThreshold(final double threshold) { 1607 tsh = threshold; 1608 } 1609 1610 /** 1611 * Computes norm of a vector of 2 components 'a' and 'b' as 1612 * sqrt(pow(a, 2.0) + pow(b, 2.0)) without destructive underflow or 1613 * overflow, that is when a or b are close to maximum or minimum values 1614 * allowed by machine precision, computing the previous expression might 1615 * lead to highly inaccurate results. 1616 * This method implements previous expression to avoid this effect as 1617 * much as possible and increase accuracy. 1618 * 1619 * @param a 1st value 1620 * @param b 2nd value 1621 * @return Norm of (a, b). 1622 */ 1623 private double pythag(final double a, final double b) { 1624 final var absa = Math.abs(a); 1625 final var absb = Math.abs(b); 1626 1627 if (absa > absb) { 1628 return absa * Math.sqrt(1.0 + (absb / absa) * (absb / absa)); 1629 } else { 1630 return (absb == 0.0 ? 0.0 : absb * Math.sqrt(1.0 + (absa / absb) * (absa / absb))); 1631 } 1632 } 1633 1634 /** 1635 * Returns a or -a depending on b sign. If b is positive, this method 1636 * returns "a", otherwise it returns -a 1637 * 1638 * @param a 1st value 1639 * @param b 2nd value 1640 * @return a or -a depending on b sign. 1641 */ 1642 private double sign(final double a, final double b) { 1643 if (b >= 0.0) { 1644 return a >= 0.0 ? a : -a; 1645 } else { 1646 return a >= 0.0 ? -a : a; 1647 } 1648 } 1649 1650 /** 1651 * Internal method to copy range space vector values into provided matrix. 1652 * Provided matrix will be resized if needed 1653 * 1654 * @param rank Rank of range space 1655 * @param singularValueThreshold Threshold to determine whether a singular 1656 * value is null 1657 * @param range Matrix where range space vector values are stored. 1658 */ 1659 private void internalGetRange(final int rank, final double singularValueThreshold, final Matrix range) { 1660 final var rows = inputMatrix.getRows(); 1661 final var columns = inputMatrix.getColumns(); 1662 1663 if (range.getRows() != rows || range.getColumns() != rank) { 1664 try { 1665 range.resize(rows, rank); 1666 } catch (final WrongSizeException ignore) { 1667 // never happens 1668 } 1669 } 1670 1671 var nr = 0; 1672 for (var j = 0; j < columns; j++) { 1673 if (w[j] > singularValueThreshold) { 1674 // copy column j of U matrix into column nr of out matrix 1675 range.setSubmatrix(0, nr, rows - 1, nr, u, 0, j, 1676 rows - 1, j); 1677 nr++; 1678 } 1679 } 1680 } 1681 1682 /** 1683 * Internal method to copy null-space vector values into provided matrix. 1684 * Provided matrix will be resized if needed 1685 * 1686 * @param nullity Nullity of null-space 1687 * @param singularValueThreshold Threshold to determine whether a singular 1688 * value is null 1689 * @param nullspace Matrix where null-space vector values are stored. 1690 */ 1691 private void internalGetNullspace(final int nullity, final double singularValueThreshold, 1692 final Matrix nullspace) { 1693 final int columns = inputMatrix.getColumns(); 1694 1695 if (nullspace.getRows() != columns || nullspace.getColumns() != nullity) { 1696 try { 1697 nullspace.resize(columns, nullity); 1698 } catch (final WrongSizeException ignore) { 1699 // never happens 1700 } 1701 } 1702 1703 var nn = 0; 1704 for (var j = 0; j < columns; j++) { 1705 if (w[j] <= singularValueThreshold) { 1706 // copy column j of U matrix into column nn of out matrix 1707 nullspace.setSubmatrix(0, nn, columns - 1, nn, v, 0, j, 1708 columns - 1, j); 1709 nn++; 1710 } 1711 } 1712 } 1713 }