1 // This code has been mechanically translated from the original FORTRAN
2 // code at http://netlib.org/minpack.
3 
4 /** Authors:    Lars Tandle Kyllingstad
5     Copyright:  Copyright (c) 2009, Lars T. Kyllingstad. All rights reserved.
6     License:    Boost Licence 1.0
7 */
8 module scid.ports.minpack.qrfac;
9 
10 
11 private import std.algorithm: min, max;
12 private import std.math: sqrt;
13 
14 private import scid.ports.minpack.enorm;
15 
16 
17 
18 
19 /** This subroutine uses Householder transformations with column
20     pivoting (optional) to compute a QR factorization of the
21     m by n matrix A. That is, qrfac determines an orthogonal
22     matrix Q, a permutation matrix P, and an upper trapezoidal
23     matrix R with diagonal elements of nonincreasing magnitude,
24     such that AP = QR. The Householder transformation for
25     column k, k = 1,2,...,min(m,n), is of the form
26     ---
27                           t
28           i - (1/u(k)) u u
29     ---
30     where u has zeros in the first k-1 positions. The form of
31     this transformation and the method of pivoting first
32     appeared in the corresponding LINPACK subroutine.
33 
34     Params:
35 
36       m = a positive integer input variable set to the number
37         of rows of a.
38 
39       n = a positive integer input variable set to the number
40         of columns of a.
41 
42       a = an m by n array. on input a contains the matrix for
43         which the qr factorization is to be computed. on output
44         the strict upper trapezoidal part of a contains the strict
45         upper trapezoidal part of r, and the lower trapezoidal
46         part of a contains a factored form of q (the non-trivial
47         elements of the u vectors described above).
48 
49       lda = a positive integer input variable not less than m
50         which specifies the leading dimension of the array a.
51 
52       pivot = a logical input variable. if pivot is set true,
53         then column pivoting is enforced. if pivot is set false,
54         then no column pivoting is done.
55 
56       ipvt = an integer output array of length lipvt. ipvt
57         defines the permutation matrix p such that a*p = q*r.
58         column j of p is column ipvt(j) of the identity matrix.
59         if pivot is false, ipvt is not referenced.
60 
61       lipvt = a positive integer input variable. if pivot is false,
62         then lipvt may be as small as 1. if pivot is true, then
63         lipvt must be at least n.
64 
65       rdiag = an output array of length n which contains the
66         diagonal elements of r.
67 
68       acnorm = an output array of length n which contains the
69         norms of the corresponding columns of the input matrix a.
70         if this information is not needed, then acnorm can coincide
71         with rdiag.
72 
73       wa = a work array of length n. if pivot is false, then wa
74         can coincide with rdiag.
75 */
76 void qrfac(Real)(size_t m, size_t n, Real* a, size_t lda,
77     bool pivot, size_t* ipvt, size_t lipvt, Real* rdiag, Real* acnorm,
78     Real* wa)
79 {
80     size_t i, j, ij, ikmax, jp1, k, kmax, minmn;
81     Real ajnorm, sum, temp;
82     
83     enum : Real
84     {
85         one = 1.0,
86         p05 = 0.05,
87         zero = 0.0,
88 
89         // epsmch is the machine precision
90         epsmch = Real.epsilon
91     }
92 
93 
94     // Compute the initial column norms and initialize several arrays.
95     for (j=0; j<n; j++)
96     {
97         acnorm[j] = enorm(m, a+j*m);
98         rdiag[j] = acnorm[j];
99         wa[j] = rdiag[j];
100         if (pivot) ipvt[j] = j;
101     }
102 
103 
104     // Reduce A to R with Householder transformations.
105     minmn = min(m, n);
106     for (j=0; j<minmn; j++)
107     {
108         if (pivot)
109         {
110             // Bring the column of largest norm into the pivot position.
111             kmax = j;
112             for (k=j; k<n; k++)
113                 if (rdiag[k] > rdiag[kmax]) kmax = k;
114 
115             if (kmax != j)
116             {
117                 for (i=0; i<m; i++)
118                 {
119                     ij = i + j*lda;
120                     ikmax = i + kmax*lda;
121                     temp = a[ij];
122                     a[ij] = a[ikmax];
123                     a[ikmax] = temp;
124                 }
125 
126                 rdiag[kmax] = rdiag[j];
127                 wa[kmax] = wa[j];
128                 k = ipvt[j];
129                 ipvt[j] = ipvt[kmax];
130                 ipvt[kmax] = k;
131             }
132         }
133 
134 
135         // Compute the Householder transformation to reduce the
136         // j-th column of A to a multiple of the j-th unit vector.
137         ajnorm = enorm(m-j, a+j+j*lda);
138         if (ajnorm != zero)
139         {
140             if (a[j+j*lda] < zero) ajnorm = -ajnorm;
141             for (i=j; i<m; i++)
142                 a[i+j*lda] /= ajnorm;
143             a[j+j*lda] += one;
144 
145 
146             // Apply the transformation to the remaining columns
147             // and update the norms.
148             jp1 = j + 1;
149             if (n >= jp1)
150             {
151                 for (k=jp1; k<n; k++)
152                 {
153                     sum = zero;
154                     for (i=j; i<m; i++)
155                         sum += a[i+j*lda]*a[i+k*lda];
156                     temp = sum/a[j+j*lda];
157                     for (i=j; i<m; i++)
158                         a[i+k*lda] -= temp*a[i+j*lda];
159                     
160                     if (pivot  &&  rdiag[k] != zero)
161                     {
162                         temp = a[j+k*lda]/rdiag[k];
163                         rdiag[k] *= sqrt(max(zero, one-temp*temp));
164                         if (p05*((rdiag[k]/wa[k])^^2) <= epsmch)
165                         {
166                             rdiag[k] = enorm(m-j-1, a+jp1+k*lda);
167                             wa[k] = rdiag[k];
168                         }
169                     }
170                 }
171             }
172         }
173         rdiag[j] = -ajnorm;
174     }
175 }
176 
177 
178 unittest { alias qrfac!(real) rqrfac; }