001    package numal.lowprecision;
002    
003    import static java.lang.Math.*;
004    
005    /** 
006     * References: Wilkinson,J.H., Reinsch,C. : 1971, <i>Handbook of Automatic Computation, Vol. 2, Linear Algebra</i>, Springer-Verlag, Berlin.
007     */
008    public class LinearAlgebra extends Object {
009    
010    /** Calculates the singular value decomposition UDV^T of a given matrix A.
011     * The matrix is first transformed to bidiagonal form by calling {@link #hshreabid}, the two
012     * transforming matrices are calculated by calling {@link #psttfmmat} and {@link #pretfmmat},
013     * and finally the singular value decomposition is calculated by {@link #qrisngvaldecbid}.
014     * <ul>
015     * <li><b>input:</b></li> 
016     * <li>em[0] : the machine precision</li> 
017     * <li>em[2] : the relative precision in the singular values</li> 
018     * <li>em[4] : the maximal number of iterations to be performed</li> 
019     * <li>em[6] : the minimal nonneglectable singular value</li> 
020     * <li><b>output:</b></li> 
021     * <li>em[1] : the infinity norm of the matrix</li> 
022     * <li>em[3] : the maximal neglected superdiagonal element</li> 
023     * <li>em[5] : the number of iterations performed</li> 
024     * <li>em[7] : the numerical rank of the matrix; i.e. the number of singular values greater than or equal to em[6]</li> 
025     * </ul> 
026     *
027     * <h3>Dependencies</h3> {@link #hshreabid}, {@link #psttfmmat},  {@link #pretfmmat},  {@link #qrisngvaldecbid}.
028     *
029     * <h3>References</h3> Method originally from class numal.LinearAlgebra - Lau (2004) 
030     * section 3.15.2.B Singular Values - Real full matrices p.316.
031     *
032     * @param a <b>input</b>: the given mxn matrix <b>output</b>: the matrix U in the singular value decomposition UDV^T
033     * @param m the number of rows of the matrix a
034     * @param n the number of columns of a, <b>precondition:</b> n &le; m
035     * @param val <b>output</b>: the singular values 1 to n
036     * @param v <b>output</b>: V^T (transpose of matrix v) in the singular value decomposition
037     * @param em input and output parameters (see above)
038     * @return number of singular values not found, i.e., a number not equal to zero if the number of iterations exceeds em[4]
039     *
040     */
041    public static int qrisngvaldec(float a[][], int m, int n, float val[], float v[][], float em[]) {
042      float b[] = new float[n + 1];    // matrix B superdiagonal
043      hshreabid(a, m, n, val, b, em);  // get b
044      psttfmmat(a, n, v, b);
045      pretfmmat(a, m, n, val);
046      return qrisngvaldecbid(val, b, m, n, a, v, em);
047    }
048    
049    /** Reduces an m x n matrix <i>a</i> to a bidiagonal form <i>B</i>.
050     * With A=A1, u(1) is so chosen that all elements but the first column of     A1' = (I - 2 u(1) u(1)^T / u(1)^T u(1)) A1 are zero; 
051     * v(1) is so chosen that all elements but the first two of the first row in  A1''= (I - 2 v(1) v(1)^T / v(1)^T v(1)) A1' are zero;
052     * the first row and column are stripped from A1'' to produce the (m-1) x (n-1) matrix A2 and the process is repeated.
053     * <b>Note:</b> this method is a slight improvement of a part of a procedure (svd) of Wilkinson and Reinsch (1971)
054     * by skipping a transformation if the column or row is already in the desired form, (i.e., if the sum of the squares of the
055     * elements that ought to be zero is smaller than a certain constant). In svd the transformation is skipped only if the norm
056     * of the full row or column is small enough. As a result, some ill-defined transformations are skipped in {@link #hshreabid}.
057     * Moreover, if a transformation is skipped, a zero is not stored in the diagonal or superdiagonal, but the value that would
058     * have been found if the column or row were in the desired form already is stored.
059     * 
060     * <h3>Dependencies</h3> {@link Basic#tammat}, {@link Basic#mattam}, {@link Basic#elmcol}, {@link Basic#elmrow}
061     *
062     * <h3>References</h3> Method originally from class numal.LinearAlgebra - Lau (2004) 
063     * section 3.12.1.A Other transformations : To bidiagonal form - real matrices p.212.
064     *
065     * @param a <b>input:</b> m by n matrix <b>output:</b> data concerning the premultiplying and postmultiplying matrices
066     * @param m number of rows
067     * @param n number of columns
068     * @param d <b>output:</b> diagonal of the bidiagonal matrix <i>B</i>
069     * @param b <b>output:</b> super diagonal of the bidiagonal matrix <i>B</i>
070     * @param em <b>input:</b> em[0] machine precision <b>output:</b> em[1] infinity norm of original matrix
071     */
072    public static void hshreabid(float a[][], int m, int n, float d[], float b[], float em[]) {
073    
074      float norm = 0f;
075      for (int i = 1; i <= m; i++) {
076        float w = 0f;
077        for (int j = 1; j <= n; j++) w += abs(a[i][j]);
078        if (w > norm) norm = w;
079      }
080      
081      float machtol = em[0] * norm;
082      em[1] = norm;
083      for (int i = 1; i <= n; i++) {
084        int i1 = i + 1;
085        float s = Basic.tammat(i1, m, i, i, a, a);
086        if (s < machtol)
087          d[i] = a[i][i];
088        else {
089          float f = a[i][i];
090          s += f * f;
091          float g = (f < 0f) ? (float) sqrt(s) : (float) -sqrt(s);
092          d[i] = g;
093          float h = f * g - s;
094          a[i][i] = f - g;
095          for (int j = i1; j <= n; j++)
096            Basic.elmcol(i, m, j, i, a, a, Basic.tammat(i, m, i, j, a, a) / h);
097        }
098        if (i < n) {
099          s = Basic.mattam(i1 + 1, n, i, i, a, a);
100          if (s < machtol)
101            b[i] = a[i][i1];
102          else {
103            float f = a[i][i1];
104            s += f * f;
105            float g = (f < 0f) ? (float) sqrt(s) : (float) -sqrt(s);
106            b[i] = g;
107            float h = f * g - s;
108            a[i][i1] = f - g;
109            for (int j = i1; j <= m; j++)
110              Basic.elmrow(i1, n, j, i, a, a, Basic.mattam(i1, n, i, j, a, a) / h);
111          }
112        }
113      }
114    }
115    /** Calculates the postmultiplying matrix from the intermediate results generated by {@link #hshreabid},
116     * 
117     * <h3>Dependencies</h3> {@link Basic#elmcol}, {@link Basic#matmat}
118     *
119     * <h3>References</h3> Method originally from class numal.LinearAlgebra - Lau (2004) 
120     * section 3.12.1.B Other transformations : To bidiagonal form - real matrices p.214.
121     * 
122     * @param a data concerning postmultiplying matrix, as generated by {@link #hshreabid} (nxn)
123     * @param n number of columns and rows of a
124     * @param v <b>output:</b> postmultiplying matrix (nxn)
125     * @param b superdiagonal of A as generated by {@link #hshreabid} are in the [1:n-1] elements of this array
126     */
127    public static void psttfmmat(float a[][], int n, float v[][], float b[]) {
128      int i1 = n;
129      v[n][n] = 1f;
130      for (int i = n - 1; i >= 1; i--) {
131        float h = b[i] * a[i][i1];
132        if (h < 0f) {
133          for (int j = i1; j <= n; j++) v[j][i] = a[i][j] / h;
134          for (int j = i1; j <= n; j++) Basic.elmcol(i1, n, j, i, v, v, Basic.matmat(i1, n, i, j, a, v));
135        }
136        for (int j = i1; j <= n; j++) v[i][j] = v[j][i] = 0f;
137        v[i][i] = 1f;
138        i1 = i;
139      }
140    }
141    
142    /** Calculates the premultiplying matrix from the intermediate results generated by {@link #hshreabid},
143     * 
144     * <h3>Dependencies</h3> {@link Basic#elmcol}, {@link Basic#tammat}
145     *
146     * <h3>References</h3> Method originally from class numal.LinearAlgebra - Lau (2004) 
147     * section 3.12.1.C Other transformations : To bidiagonal form - real matrices p.214.
148     *
149     * @param a <b>input:</b> data concerning premultiplying matrix, as generated by {@link #hshreabid} (nxn) <b>output:</b> premultiplying matrix
150     * @param m number of rows of a
151     * @param n number of columns of a, precondition: n &le; m
152     * @param d diagonal as generated by {@link #hshreabid} (1xn)
153     */
154    public static void pretfmmat(float a[][], int m, int n, float d[]) {
155      for (int i = n; i >= 1; i--) {
156        int i1 = i + 1;
157        float g = d[i];
158        float h = g * a[i][i];
159        for (int j = i1; j <= n; j++) a[i][j] = 0f;
160        if (h < 0f) {
161          for (int j = i1; j <= n; j++)
162            Basic.elmcol(i, m, j, i, a, a, Basic.tammat(i1, m, i, j, a, a) / h);
163          for (int j = i; j <= m; j++) a[j][i] /= g;
164        } else
165          for (int j = i; j <= m; j++) a[j][i] = 0f;
166        a[i][i] += 1f;
167      }
168    }
169    /** Calculates by use of a variant of the QR algorithm the singular value decomposition of an mxn matrix A
170     * (m &ge; n), i.e., the mxn matrix U, the nxn diagonal matrix D, and the nxn matrix V for which A=UDV^T, 
171     * where U^T U = V^T V = I (nxn unit matrix). It is assumed that A has been reduced to bidiagonal form form by preliminary
172     * pre- and post-multiplication by matrices of the form I - 2 u u^T / u^T u by use of {@link #hshreabid}.
173     * <b>Note:</b> Matrix A is input as a synthesis of d, b vectors and u and v matrices.
174     * <ul>
175     * <li><b>input:</b></li> 
176     * <li>em[1] : infinity norm of the matrix</li>
177     * <li>em[2] : relative precision of the singular values</li>
178     * <li>em[4] : maximal number of iterations to be performed</li>
179     * <li>em[6] : minimal nonneglectable singular value</li>
180     * <li><b>output:</b></li> 
181     * <li>em[3] : maximal neglected superdiagonal element</li>
182     * <li>em[5] : number of iterations performed</li>
183     * <li>em[7] : numerical rank of the matrix (number of singular values &ge; em[6]</li>
184     * </ul> 
185     *
186     * <h3>Dependencies</h3> {@link Basic#rotcol}.
187     *
188     * <h3>References</h3> Method originally from class numal.LinearAlgebra - Lau (2004) 
189     * section 3.15.1.B Singular Values - Real bidiagonal matrices p.312. 
190     * Wilkinson,J.H., Reinsch,C. : 1971, <i>Handbook of Automatic Computation, Vol. 2, Linear Algebra</i>, Springer-Verlag, Berlin.
191     *
192     * @param d <b>input</b>: diagonal of the bidiagonal matrix <b>output</b>: singular values
193     * @param b superdiagonal of the bidiagonal matrix in the [1:n-1] elements of this array
194     * @param m rows of matrix U
195     * @param n columns of matrix U (also size of matrix V, vector d and b), 
196     * @param u <b>input</b>: premultiplying matrix as produced by {@link #pretfmmat} <b>output</b>: premultiplying matrix U of the singular value decomposition UDV^T
197     * @param v <b>input</b>: transpose of postmultiplying matrix as produced by {@link #psttfmmat} <b>output</b>: transpose of postmultiplying matrix V of the singular value decomposition UDV^T
198     * @param em see description above
199     * @return number of singular values not found if the number of iterations exceeds em[4] (zero if iterations &le; em[4]) 
200     */
201    public static int qrisngvaldecbid(float d[], float b[], int m, int n, float u[][], float v[][], float em[]) {
202      float z, x, y, g, h, f, c, s;
203    
204      float tol = em[2] * em[1];
205      int iteration = 0; 
206      float bmax = 0f;
207      int max = (int) em[4];
208      float min = em[6];
209      int rank = n; 
210      int n0 = n;
211      do {
212        int k = n;
213        int n1 = n - 1;
214        while (true) {
215          k--;
216          if (k <= 0) break;
217          if (abs(b[k]) >= tol) {
218            if (abs(d[k]) < tol) {
219              c = 0f;
220              s = 1f;
221              for (int i = k; i <= n1; i++) {
222                f = s * b[i];
223                b[i] *= c;
224                int i1 = i + 1;
225                if (abs(f) < tol) break;
226                g = d[i1];
227                d[i1] = h = (float) sqrt(f * f + g * g);
228                c = g / h;
229                s = -f / h;
230                Basic.rotcol(1, m, k, i1, u, c, s);
231              }
232              break;
233            }
234          } else {
235            if (abs(b[k]) > bmax) bmax = abs(b[k]);
236            break;
237          }
238        }
239        if (k == n1) {
240          if (d[n] < 0f) {
241            d[n] = -d[n];
242            for (int i = 1; i <= n0; i++) v[i][n] = -v[i][n];
243          }
244          if (d[n] <= min) rank--;
245          n = n1;
246        } else {
247          iteration++;
248          if (iteration > max) break;
249          int k1 = k + 1;
250          z = d[n];
251          x = d[k1];
252          y = d[n1];
253          g = (n1 == 1) ? 0f : b[n1 - 1];
254          h = b[n1];
255          f =((y - z) * (y + z) + (g - h) * (g + h))/(2f * h * y);
256          g = (float) sqrt(f * f + 1.0f);
257          f =((x - z) * (x + z) + h * (y / ((f < 0f) ? f - g : f + g) - h)) / x;
258          c = s = 1f; // not required
259          for (int i = k1 + 1; i <= n; i++) {
260            int i1 = i - 1;
261            g = b[i1];
262            y = d[i];
263            h = s * g;
264            g *= c;
265            z = (float) sqrt(f * f + h * h);
266            c = f / z;
267            s = h / z;
268            if (i1 != k1) b[i1-1] = z;
269            f = x * c + g * s;
270            g = g * c - x * s;
271            h = y * s;
272            y *= c;
273            Basic.rotcol(1, n0, i1, i, v, c, s);
274            d[i1] = z = (float) sqrt(f * f + h * h);
275            c = f / z;
276            s = h / z;
277            f = c * g + s * y;
278            x = c * y - s * g;
279            Basic.rotcol(1, m, i1, i, u, c, s);
280          }
281          b[n1] = f;
282          d[n] = x;
283        }
284      } while (n > 0);
285      em[3] = bmax;
286      em[5] = iteration;
287      em[7] = rank;
288      return n;
289    }
290    
291    }