View Javadoc
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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 }