View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  /*
19   * This is not the original file distributed by the Apache Software Foundation
20   * It has been modified by the Hipparchus project
21   */
22  package org.hipparchus.linear;
23  
24  import org.hipparchus.exception.LocalizedCoreFormats;
25  import org.hipparchus.exception.MathIllegalArgumentException;
26  import org.hipparchus.util.FastMath;
27  import org.hipparchus.util.Precision;
28  
29  /**
30   * Calculates the compact Singular Value Decomposition of a matrix.
31   * <p>
32   * The Singular Value Decomposition of matrix A is a set of three matrices: U,
33   * &Sigma; and V such that A = U &times; &Sigma; &times; V<sup>T</sup>. Let A be
34   * a m &times; n matrix, then U is a m &times; p orthogonal matrix, &Sigma; is a
35   * p &times; p diagonal matrix with positive or null elements, V is a p &times;
36   * n orthogonal matrix (hence V<sup>T</sup> is also orthogonal) where
37   * p=min(m,n).
38   * </p>
39   * <p>This class is similar to the class with similar name from the
40   * <a href="http://math.nist.gov/javanumerics/jama/">JAMA</a> library, with the
41   * following changes:</p>
42   * <ul>
43   *   <li>the {@code norm2} method which has been renamed as {@link #getNorm()
44   *   getNorm},</li>
45   *   <li>the {@code cond} method which has been renamed as {@link
46   *   #getConditionNumber() getConditionNumber},</li>
47   *   <li>the {@code rank} method which has been renamed as {@link #getRank()
48   *   getRank},</li>
49   *   <li>a {@link #getUT() getUT} method has been added,</li>
50   *   <li>a {@link #getVT() getVT} method has been added,</li>
51   *   <li>a {@link #getSolver() getSolver} method has been added,</li>
52   *   <li>a {@link #getCovariance(double) getCovariance} method has been added.</li>
53   * </ul>
54   * @see <a href="http://mathworld.wolfram.com/SingularValueDecomposition.html">MathWorld</a>
55   * @see <a href="http://en.wikipedia.org/wiki/Singular_value_decomposition">Wikipedia</a>
56   */
57  public class SingularValueDecomposition {
58      /** Relative threshold for small singular values. */
59      private static final double EPS = 0x1.0p-52;
60      /** Absolute threshold for small singular values. */
61      private static final double TINY = 0x1.0p-966;
62      /** Computed singular values. */
63      private final double[] singularValues;
64      /** max(row dimension, column dimension). */
65      private final int m;
66      /** min(row dimension, column dimension). */
67      private final int n;
68      /** Cached value of U matrix. */
69      private final RealMatrix cachedU;
70      /** Cached value of transposed U matrix. */
71      private RealMatrix cachedUt;
72      /** Cached value of S (diagonal) matrix. */
73      private RealMatrix cachedS;
74      /** Cached value of V matrix. */
75      private final RealMatrix cachedV;
76      /** Cached value of transposed V matrix. */
77      private RealMatrix cachedVt;
78      /**
79       * Tolerance value for small singular values, calculated once we have
80       * populated "singularValues".
81       **/
82      private final double tol;
83  
84      /**
85       * Calculates the compact Singular Value Decomposition of the given matrix.
86       *
87       * @param matrix Matrix to decompose.
88       */
89      public SingularValueDecomposition(final RealMatrix matrix) {
90          final double[][] A;
91  
92           // "m" is always the largest dimension.
93          final boolean transposed;
94          if (matrix.getRowDimension() < matrix.getColumnDimension()) {
95              transposed = true;
96              A = matrix.transpose().getData();
97              m = matrix.getColumnDimension();
98              n = matrix.getRowDimension();
99          } else {
100             transposed = false;
101             A = matrix.getData();
102             m = matrix.getRowDimension();
103             n = matrix.getColumnDimension();
104         }
105 
106         singularValues = new double[n];
107         final double[][] U = new double[m][n];
108         final double[][] V = new double[n][n];
109         final double[] e = new double[n];
110         final double[] work = new double[m];
111         // Reduce A to bidiagonal form, storing the diagonal elements
112         // in s and the super-diagonal elements in e.
113         final int nct = FastMath.min(m - 1, n);
114         final int nrt = FastMath.max(0, n - 2);
115         for (int k = 0; k < FastMath.max(nct, nrt); k++) {
116             if (k < nct) {
117                 // Compute the transformation for the k-th column and
118                 // place the k-th diagonal in s[k].
119                 // Compute 2-norm of k-th column without under/overflow.
120                 singularValues[k] = 0;
121                 for (int i = k; i < m; i++) {
122                     singularValues[k] = FastMath.hypot(singularValues[k], A[i][k]);
123                 }
124                 if (singularValues[k] != 0) {
125                     if (A[k][k] < 0) {
126                         singularValues[k] = -singularValues[k];
127                     }
128                     for (int i = k; i < m; i++) {
129                         A[i][k] /= singularValues[k];
130                     }
131                     A[k][k] += 1;
132                 }
133                 singularValues[k] = -singularValues[k];
134             }
135             for (int j = k + 1; j < n; j++) {
136                 if (k < nct &&
137                     singularValues[k] != 0) {
138                     // Apply the transformation.
139                     double t = 0;
140                     for (int i = k; i < m; i++) {
141                         t += A[i][k] * A[i][j];
142                     }
143                     t = -t / A[k][k];
144                     for (int i = k; i < m; i++) {
145                         A[i][j] += t * A[i][k];
146                     }
147                 }
148                 // Place the k-th row of A into e for the
149                 // subsequent calculation of the row transformation.
150                 e[j] = A[k][j];
151             }
152             if (k < nct) {
153                 // Place the transformation in U for subsequent back
154                 // multiplication.
155                 for (int i = k; i < m; i++) {
156                     U[i][k] = A[i][k];
157                 }
158             }
159             if (k < nrt) {
160                 // Compute the k-th row transformation and place the
161                 // k-th super-diagonal in e[k].
162                 // Compute 2-norm without under/overflow.
163                 e[k] = 0;
164                 for (int i = k + 1; i < n; i++) {
165                     e[k] = FastMath.hypot(e[k], e[i]);
166                 }
167                 if (e[k] != 0) {
168                     if (e[k + 1] < 0) {
169                         e[k] = -e[k];
170                     }
171                     for (int i = k + 1; i < n; i++) {
172                         e[i] /= e[k];
173                     }
174                     e[k + 1] += 1;
175                 }
176                 e[k] = -e[k];
177                 if (k + 1 < m &&
178                     e[k] != 0) {
179                     // Apply the transformation.
180                     for (int i = k + 1; i < m; i++) {
181                         work[i] = 0;
182                     }
183                     for (int j = k + 1; j < n; j++) {
184                         for (int i = k + 1; i < m; i++) {
185                             work[i] += e[j] * A[i][j];
186                         }
187                     }
188                     for (int j = k + 1; j < n; j++) {
189                         final double t = -e[j] / e[k + 1];
190                         for (int i = k + 1; i < m; i++) {
191                             A[i][j] += t * work[i];
192                         }
193                     }
194                 }
195 
196                 // Place the transformation in V for subsequent
197                 // back multiplication.
198                 for (int i = k + 1; i < n; i++) {
199                     V[i][k] = e[i];
200                 }
201             }
202         }
203         // Set up the final bidiagonal matrix or order p.
204         int p = n;
205         if (nct < n) {
206             singularValues[nct] = A[nct][nct];
207         }
208         if (m < p) {
209             singularValues[p - 1] = 0;
210         }
211         if (nrt + 1 < p) {
212             e[nrt] = A[nrt][p - 1];
213         }
214         e[p - 1] = 0;
215 
216         // Generate U.
217         for (int j = nct; j < n; j++) {
218             for (int i = 0; i < m; i++) {
219                 U[i][j] = 0;
220             }
221             U[j][j] = 1;
222         }
223         for (int k = nct - 1; k >= 0; k--) {
224             if (singularValues[k] != 0) {
225                 for (int j = k + 1; j < n; j++) {
226                     double t = 0;
227                     for (int i = k; i < m; i++) {
228                         t += U[i][k] * U[i][j];
229                     }
230                     t = -t / U[k][k];
231                     for (int i = k; i < m; i++) {
232                         U[i][j] += t * U[i][k];
233                     }
234                 }
235                 for (int i = k; i < m; i++) {
236                     U[i][k] = -U[i][k];
237                 }
238                 U[k][k] = 1 + U[k][k];
239                 for (int i = 0; i < k - 1; i++) {
240                     U[i][k] = 0;
241                 }
242             } else {
243                 for (int i = 0; i < m; i++) {
244                     U[i][k] = 0;
245                 }
246                 U[k][k] = 1;
247             }
248         }
249 
250         // Generate V.
251         for (int k = n - 1; k >= 0; k--) {
252             if (k < nrt &&
253                 e[k] != 0) {
254                 for (int j = k + 1; j < n; j++) {
255                     double t = 0;
256                     for (int i = k + 1; i < n; i++) {
257                         t += V[i][k] * V[i][j];
258                     }
259                     t = -t / V[k + 1][k];
260                     for (int i = k + 1; i < n; i++) {
261                         V[i][j] += t * V[i][k];
262                     }
263                 }
264             }
265             for (int i = 0; i < n; i++) {
266                 V[i][k] = 0;
267             }
268             V[k][k] = 1;
269         }
270 
271         // Main iteration loop for the singular values.
272         final int pp = p - 1;
273         while (p > 0) {
274             int k;
275             int kase;
276             // Here is where a test for too many iterations would go.
277             // This section of the program inspects for
278             // negligible elements in the s and e arrays.  On
279             // completion the variables kase and k are set as follows.
280             // kase = 1     if s(p) and e[k-1] are negligible and k<p
281             // kase = 2     if s(k) is negligible and k<p
282             // kase = 3     if e[k-1] is negligible, k<p, and
283             //              s(k), ..., s(p) are not negligible (qr step).
284             // kase = 4     if e(p-1) is negligible (convergence).
285             for (k = p - 2; k >= 0; k--) {
286                 final double threshold
287                     = TINY + EPS * (FastMath.abs(singularValues[k]) +
288                                     FastMath.abs(singularValues[k + 1]));
289 
290                 // the following condition is written this way in order
291                 // to break out of the loop when NaN occurs, writing it
292                 // as "if (FastMath.abs(e[k]) <= threshold)" would loop
293                 // indefinitely in case of NaNs because comparison on NaNs
294                 // always return false, regardless of what is checked
295                 // see issue MATH-947
296                 if (!(FastMath.abs(e[k]) > threshold)) { // NOPMD - as explained above, the way this test is written is correct
297                     e[k] = 0;
298                     break;
299                 }
300 
301             }
302 
303             if (k == p - 2) {
304                 kase = 4;
305             } else {
306                 int ks;
307                 for (ks = p - 1; ks >= k; ks--) {
308                     if (ks == k) {
309                         break;
310                     }
311                     final double t = (ks != p ? FastMath.abs(e[ks]) : 0) +
312                         (ks != k + 1 ? FastMath.abs(e[ks - 1]) : 0);
313                     if (FastMath.abs(singularValues[ks]) <= TINY + EPS * t) {
314                         singularValues[ks] = 0;
315                         break;
316                     }
317                 }
318                 if (ks == k) {
319                     kase = 3;
320                 } else if (ks == p - 1) {
321                     kase = 1;
322                 } else {
323                     kase = 2;
324                     k = ks;
325                 }
326             }
327             k++;
328             // Perform the task indicated by kase.
329             switch (kase) { // NOPMD - breaking this complex algorithm into functions just to keep PMD happy would be artificial
330                 // Deflate negligible s(p).
331                 case 1: {
332                     double f = e[p - 2];
333                     e[p - 2] = 0;
334                     for (int j = p - 2; j >= k; j--) {
335                         double t = FastMath.hypot(singularValues[j], f);
336                         final double cs = singularValues[j] / t;
337                         final double sn = f / t;
338                         singularValues[j] = t;
339                         if (j != k) {
340                             f = -sn * e[j - 1];
341                             e[j - 1] = cs * e[j - 1];
342                         }
343 
344                         for (int i = 0; i < n; i++) {
345                             t = cs * V[i][j] + sn * V[i][p - 1];
346                             V[i][p - 1] = -sn * V[i][j] + cs * V[i][p - 1];
347                             V[i][j] = t;
348                         }
349                     }
350                 }
351                 break;
352                 // Split at negligible s(k).
353                 case 2: {
354                     double f = e[k - 1];
355                     e[k - 1] = 0;
356                     for (int j = k; j < p; j++) {
357                         double t = FastMath.hypot(singularValues[j], f);
358                         final double cs = singularValues[j] / t;
359                         final double sn = f / t;
360                         singularValues[j] = t;
361                         f = -sn * e[j];
362                         e[j] = cs * e[j];
363 
364                         for (int i = 0; i < m; i++) {
365                             t = cs * U[i][j] + sn * U[i][k - 1];
366                             U[i][k - 1] = -sn * U[i][j] + cs * U[i][k - 1];
367                             U[i][j] = t;
368                         }
369                     }
370                 }
371                 break;
372                 // Perform one qr step.
373                 case 3: {
374                     // Calculate the shift.
375                     final double maxPm1Pm2 = FastMath.max(FastMath.abs(singularValues[p - 1]),
376                                                           FastMath.abs(singularValues[p - 2]));
377                     final double scale = FastMath.max(FastMath.max(FastMath.max(maxPm1Pm2,
378                                                                                 FastMath.abs(e[p - 2])),
379                                                                    FastMath.abs(singularValues[k])),
380                                                       FastMath.abs(e[k]));
381                     final double sp = singularValues[p - 1] / scale;
382                     final double spm1 = singularValues[p - 2] / scale;
383                     final double epm1 = e[p - 2] / scale;
384                     final double sk = singularValues[k] / scale;
385                     final double ek = e[k] / scale;
386                     final double b = ((spm1 + sp) * (spm1 - sp) + epm1 * epm1) / 2.0;
387                     final double c = (sp * epm1) * (sp * epm1);
388                     double shift = 0;
389                     if (b != 0 ||
390                         c != 0) {
391                         shift = FastMath.sqrt(b * b + c);
392                         if (b < 0) {
393                             shift = -shift;
394                         }
395                         shift = c / (b + shift);
396                     }
397                     double f = (sk + sp) * (sk - sp) + shift;
398                     double g = sk * ek;
399                     // Chase zeros.
400                     for (int j = k; j < p - 1; j++) {
401                         double t = FastMath.hypot(f, g);
402                         double cs = f / t;
403                         double sn = g / t;
404                         if (j != k) {
405                             e[j - 1] = t;
406                         }
407                         f = cs * singularValues[j] + sn * e[j];
408                         e[j] = cs * e[j] - sn * singularValues[j];
409                         g = sn * singularValues[j + 1];
410                         singularValues[j + 1] = cs * singularValues[j + 1];
411 
412                         for (int i = 0; i < n; i++) {
413                             t = cs * V[i][j] + sn * V[i][j + 1];
414                             V[i][j + 1] = -sn * V[i][j] + cs * V[i][j + 1];
415                             V[i][j] = t;
416                         }
417                         t = FastMath.hypot(f, g);
418                         cs = f / t;
419                         sn = g / t;
420                         singularValues[j] = t;
421                         f = cs * e[j] + sn * singularValues[j + 1];
422                         singularValues[j + 1] = -sn * e[j] + cs * singularValues[j + 1];
423                         g = sn * e[j + 1];
424                         e[j + 1] = cs * e[j + 1];
425                         if (j < m - 1) {
426                             for (int i = 0; i < m; i++) {
427                                 t = cs * U[i][j] + sn * U[i][j + 1];
428                                 U[i][j + 1] = -sn * U[i][j] + cs * U[i][j + 1];
429                                 U[i][j] = t;
430                             }
431                         }
432                     }
433                     e[p - 2] = f;
434                 }
435                 break;
436                 // Convergence.
437                 default: {
438                     // Make the singular values positive.
439                     if (singularValues[k] <= 0) {
440                         singularValues[k] = singularValues[k] < 0 ? -singularValues[k] : 0;
441 
442                         for (int i = 0; i <= pp; i++) {
443                             V[i][k] = -V[i][k];
444                         }
445                     }
446                     // Order the singular values.
447                     while (k < pp) {
448                         if (singularValues[k] >= singularValues[k + 1]) {
449                             break;
450                         }
451                         double t = singularValues[k];
452                         singularValues[k] = singularValues[k + 1];
453                         singularValues[k + 1] = t;
454                         if (k < n - 1) {
455                             for (int i = 0; i < n; i++) {
456                                 t = V[i][k + 1];
457                                 V[i][k + 1] = V[i][k];
458                                 V[i][k] = t;
459                             }
460                         }
461                         if (k < m - 1) {
462                             for (int i = 0; i < m; i++) {
463                                 t = U[i][k + 1];
464                                 U[i][k + 1] = U[i][k];
465                                 U[i][k] = t;
466                             }
467                         }
468                         k++;
469                     }
470                     p--;
471                 }
472                 break;
473             }
474         }
475 
476         // Set the small value tolerance used to calculate rank and pseudo-inverse
477         tol = FastMath.max(m * singularValues[0] * EPS,
478                            FastMath.sqrt(Precision.SAFE_MIN));
479 
480         if (!transposed) {
481             cachedU = MatrixUtils.createRealMatrix(U);
482             cachedV = MatrixUtils.createRealMatrix(V);
483         } else {
484             cachedU = MatrixUtils.createRealMatrix(V);
485             cachedV = MatrixUtils.createRealMatrix(U);
486         }
487     }
488 
489     /**
490      * Returns the matrix U of the decomposition.
491      * <p>U is an orthogonal matrix, i.e. its transpose is also its inverse.</p>
492      * @return the U matrix
493      * @see #getUT()
494      */
495     public RealMatrix getU() {
496         // return the cached matrix
497         return cachedU;
498 
499     }
500 
501     /**
502      * Returns the transpose of the matrix U of the decomposition.
503      * <p>U is an orthogonal matrix, i.e. its transpose is also its inverse.</p>
504      * @return the U matrix (or null if decomposed matrix is singular)
505      * @see #getU()
506      */
507     public RealMatrix getUT() {
508         if (cachedUt == null) {
509             cachedUt = getU().transpose();
510         }
511         // return the cached matrix
512         return cachedUt;
513     }
514 
515     /**
516      * Returns the diagonal matrix &Sigma; of the decomposition.
517      * <p>&Sigma; is a diagonal matrix. The singular values are provided in
518      * non-increasing order, for compatibility with Jama.</p>
519      * @return the &Sigma; matrix
520      */
521     public RealMatrix getS() {
522         if (cachedS == null) {
523             // cache the matrix for subsequent calls
524             cachedS = MatrixUtils.createRealDiagonalMatrix(singularValues);
525         }
526         return cachedS;
527     }
528 
529     /**
530      * Returns the diagonal elements of the matrix &Sigma; of the decomposition.
531      * <p>The singular values are provided in non-increasing order, for
532      * compatibility with Jama.</p>
533      * @return the diagonal elements of the &Sigma; matrix
534      */
535     public double[] getSingularValues() {
536         return singularValues.clone();
537     }
538 
539     /**
540      * Returns the matrix V of the decomposition.
541      * <p>V is an orthogonal matrix, i.e. its transpose is also its inverse.</p>
542      * @return the V matrix (or null if decomposed matrix is singular)
543      * @see #getVT()
544      */
545     public RealMatrix getV() {
546         // return the cached matrix
547         return cachedV;
548     }
549 
550     /**
551      * Returns the transpose of the matrix V of the decomposition.
552      * <p>V is an orthogonal matrix, i.e. its transpose is also its inverse.</p>
553      * @return the V matrix (or null if decomposed matrix is singular)
554      * @see #getV()
555      */
556     public RealMatrix getVT() {
557         if (cachedVt == null) {
558             cachedVt = getV().transpose();
559         }
560         // return the cached matrix
561         return cachedVt;
562     }
563 
564     /**
565      * Returns the n &times; n covariance matrix.
566      * <p>The covariance matrix is V &times; J &times; V<sup>T</sup>
567      * where J is the diagonal matrix of the inverse of the squares of
568      * the singular values.</p>
569      * @param minSingularValue value below which singular values are ignored
570      * (a 0 or negative value implies all singular value will be used)
571      * @return covariance matrix
572      * @exception IllegalArgumentException if minSingularValue is larger than
573      * the largest singular value, meaning all singular values are ignored
574      */
575     public RealMatrix getCovariance(final double minSingularValue) {
576         // get the number of singular values to consider
577         final int p = singularValues.length;
578         int dimension = 0;
579         while (dimension < p &&
580                singularValues[dimension] >= minSingularValue) {
581             ++dimension;
582         }
583 
584         if (dimension == 0) {
585             throw new MathIllegalArgumentException(LocalizedCoreFormats.TOO_LARGE_CUTOFF_SINGULAR_VALUE,
586                                                 minSingularValue, singularValues[0], true);
587         }
588 
589         final double[][] data = new double[dimension][p];
590         getVT().walkInOptimizedOrder(new DefaultRealMatrixPreservingVisitor() {
591             /** {@inheritDoc} */
592             @Override
593             public void visit(final int row, final int column,
594                     final double value) {
595                 data[row][column] = value / singularValues[row];
596             }
597         }, 0, dimension - 1, 0, p - 1);
598 
599         RealMatrix jv = new Array2DRowRealMatrix(data, false);
600         return jv.transposeMultiply(jv);
601     }
602 
603     /**
604      * Returns the L<sub>2</sub> norm of the matrix.
605      * <p>The L<sub>2</sub> norm is max(|A &times; u|<sub>2</sub> /
606      * |u|<sub>2</sub>), where |.|<sub>2</sub> denotes the vectorial 2-norm
607      * (i.e. the traditional euclidian norm).</p>
608      * @return norm
609      */
610     public double getNorm() {
611         return singularValues[0];
612     }
613 
614     /**
615      * Return the condition number of the matrix.
616      * @return condition number of the matrix
617      */
618     public double getConditionNumber() {
619         return singularValues[0] / singularValues[n - 1];
620     }
621 
622     /**
623      * Computes the inverse of the condition number.
624      * In cases of rank deficiency, the {@link #getConditionNumber() condition
625      * number} will become undefined.
626      *
627      * @return the inverse of the condition number.
628      */
629     public double getInverseConditionNumber() {
630         return singularValues[n - 1] / singularValues[0];
631     }
632 
633     /**
634      * Return the effective numerical matrix rank.
635      * <p>The effective numerical rank is the number of non-negligible
636      * singular values. The threshold used to identify non-negligible
637      * terms is max(m,n) &times; ulp(s<sub>1</sub>) where ulp(s<sub>1</sub>)
638      * is the least significant bit of the largest singular value.</p>
639      * @return effective numerical matrix rank
640      */
641     public int getRank() {
642         int r = 0;
643         for (int i = 0; i < singularValues.length; i++) {
644             if (singularValues[i] > tol) {
645                 r++;
646             }
647         }
648         return r;
649     }
650 
651     /**
652      * Get a solver for finding the A &times; X = B solution in least square sense.
653      * @return a solver
654      */
655     public DecompositionSolver getSolver() {
656         return new Solver(singularValues, getUT(), getV(), getRank() == m, tol);
657     }
658 
659     /** Specialized solver. */
660     private static class Solver implements DecompositionSolver {
661         /** Pseudo-inverse of the initial matrix. */
662         private final RealMatrix pseudoInverse;
663         /** Singularity indicator. */
664         private final boolean nonSingular;
665 
666         /**
667          * Build a solver from decomposed matrix.
668          *
669          * @param singularValues Singular values.
670          * @param uT U<sup>T</sup> matrix of the decomposition.
671          * @param v V matrix of the decomposition.
672          * @param nonSingular Singularity indicator.
673          * @param tol tolerance for singular values
674          */
675         private Solver(final double[] singularValues, final RealMatrix uT,
676                        final RealMatrix v, final boolean nonSingular, final double tol) {
677             final double[][] suT = uT.getData();
678             for (int i = 0; i < singularValues.length; ++i) {
679                 final double a;
680                 if (singularValues[i] > tol) {
681                     a = 1 / singularValues[i];
682                 } else {
683                     a = 0;
684                 }
685                 final double[] suTi = suT[i];
686                 for (int j = 0; j < suTi.length; ++j) {
687                     suTi[j] *= a;
688                 }
689             }
690             pseudoInverse = v.multiply(new Array2DRowRealMatrix(suT, false));
691             this.nonSingular = nonSingular;
692         }
693 
694         /**
695          * Solve the linear equation A &times; X = B in least square sense.
696          * <p>
697          * The m&times;n matrix A may not be square, the solution X is such that
698          * ||A &times; X - B|| is minimal.
699          * </p>
700          * @param b Right-hand side of the equation A &times; X = B
701          * @return a vector X that minimizes the two norm of A &times; X - B
702          * @throws org.hipparchus.exception.MathIllegalArgumentException
703          * if the matrices dimensions do not match.
704          */
705         @Override
706         public RealVector solve(final RealVector b) {
707             return pseudoInverse.operate(b);
708         }
709 
710         /**
711          * Solve the linear equation A &times; X = B in least square sense.
712          * <p>
713          * The m&times;n matrix A may not be square, the solution X is such that
714          * ||A &times; X - B|| is minimal.
715          * </p>
716          *
717          * @param b Right-hand side of the equation A &times; X = B
718          * @return a matrix X that minimizes the two norm of A &times; X - B
719          * @throws org.hipparchus.exception.MathIllegalArgumentException
720          * if the matrices dimensions do not match.
721          */
722         @Override
723         public RealMatrix solve(final RealMatrix b) {
724             return pseudoInverse.multiply(b);
725         }
726 
727         /**
728          * Check if the decomposed matrix is non-singular.
729          *
730          * @return {@code true} if the decomposed matrix is non-singular.
731          */
732         @Override
733         public boolean isNonSingular() {
734             return nonSingular;
735         }
736 
737         /**
738          * Get the pseudo-inverse of the decomposed matrix.
739          *
740          * @return the inverse matrix.
741          */
742         @Override
743         public RealMatrix getInverse() {
744             return pseudoInverse;
745         }
746 
747         /** {@inheritDoc} */
748         @Override
749         public int getRowDimension() {
750             return pseudoInverse.getColumnDimension();
751         }
752 
753         /** {@inheritDoc} */
754         @Override
755         public int getColumnDimension() {
756             return pseudoInverse.getRowDimension();
757         }
758 
759     }
760 }