OPAL (Object Oriented Parallel Accelerator Library)  2.2.0
OPAL
QRSolver.cpp
Go to the documentation of this file.
1 // ------------------------------------------------------------------------
2 // $RCSfile: QRSolver.cpp,v $
3 // ------------------------------------------------------------------------
4 // $Revision: 1.1.1.1 $
5 // ------------------------------------------------------------------------
6 // Copyright: see Copyright.readme
7 // ------------------------------------------------------------------------
8 //
9 // Class: QRSolver
10 // This class implements routines for the least-square solution of
11 // systems of linear equations.
12 // Given an m by n matrix A, an n by n diagonal matrix D,
13 // and an m-vector B, two problem can be solved:
14 //
15 // 1. Solve the the system
16 // A*X = B,
17 // in the least squares sense. The first step to solve this problem is
18 // QRSolver solver(A, pivot);
19 // The second step is then
20 // solver.solveR(X);
21 //
22 // 2. Solve the the two systems
23 // A*X = B, D*X = 0,
24 // in the least squares sense. The first step to solve this problem is
25 // QRSolver solver(A, pivot);
26 // The second step is
27 // solver.solveS(D, X);
28 // it can be repeated as many times as required for different diagonal
29 // matrices D.
30 //
31 // In both cases, the method
32 // solver.getColNorm(C);
33 // can be called to return the original column norms of A.
34 //
35 // ------------------------------------------------------------------------
36 //
37 // $Date: 2000/03/27 09:33:35 $
38 // $Author: Andreas Adelmann $
39 //
40 // ------------------------------------------------------------------------
41 
42 #include "Algebra/QRSolver.h"
43 #include "Algebra/Matrix.h"
44 #include "Algebra/Vector.h"
45 #include <cassert>
46 #include <cmath>
47 
48 
49 // This method uses Householder transformations with optional column
50 // pivoting to compute a QR-factorization of the m-by-n matrix A.
51 // QRSolver determines an orthogonal matrix Q, a permutation matrix P,
52 // and an upper trapezoidal matrix R with diagonal elements of
53 // nonincreasing magnitude, such that A*P = Q*R. The Householder
54 // transformation for column k, k = 1, 2, ..., min(m,n), is of the form
55 //
56 // T
57 // I - (1/U(k))*U*U
58 //
59 // where U has zeros in the first k-1 positions. The form of this
60 // transformation and the method of pivoting first appeared in the
61 // corresponding LINPACK subroutine.
62 //
64 (const Matrix<double> &A, const Vector<double> &B, bool pivot):
65  n(A.ncols()), m(A.nrows()), Q(A), R(n, n, 0.0), QtB(B), S(n, n, 0.0),
66  column_norm(n, 0.0), piv_col(n, 0) {
67  assert(B.size() >= m);
69 
70  // Compute the initial and internal column norms.
71  for(int j = 0; j < n; ++j) {
72  double temp = 0.0;
73  for(int i = 0; i < m; ++i) temp += Q(i, j) * Q(i, j);
74  dot(j) = column_norm(j) = sqrt(temp);
75  piv_col(j) = j;
76  }
77 
78  // Reduce A to R using Householder transformations.
79  // Up to min(m, n) columns are used.
80  for(int j = 0; j < std::min(m, n); ++j) {
81  if(pivot) {
82  // Bring column of largest norm into pivot position.
83  int kmax = j;
84 
85  for(int k = j + 1; k < n; ++k) {
86  if(dot(k) > dot(kmax)) kmax = k;
87  }
88 
89  if(kmax != j) {
90  std::swap_ranges(Q.col_begin(j), Q.col_end(j), Q.col_begin(kmax));
91  dot(kmax) = dot(j);
92  std::swap(piv_col(j), piv_col(kmax));
93  }
94  }
95 
96  // Compute the Householder transformation which reduces the j-th
97  // column of A to a multiple of the j-th unit vector.
98  double unorm = 0.0;
99  for(int k = j; k < m; ++k) unorm += Q(k, j) * Q(k, j);
100  unorm = (Q(j, j) > 0.0) ? sqrt(unorm) : - sqrt(unorm);
101 
102  // Build column j of R.
103  for(int i = 0; i < j; ++i) {
104  R(i, j) = Q(i, j);
105  Q(i, j) = 0.0;
106  }
107  R(j, j) = - unorm;
108 
109  if(unorm != 0.0) {
110  // Modify column j of Q.
111  for(int i = j; i < m; ++i) {
112  Q(i, j) /= unorm;
113  }
114  Q(j, j) += 1.0;
115 
116  // Transform remaining columns of Q.
117  for(int k = j + 1; k < n; ++k) {
118  // Apply transformation to column k.
119  double sum = 0.0;
120  for(int i = j; i < m; ++i) sum += Q(i, j) * Q(i, k);
121  sum /= Q(j, j);
122  for(int i = j; i < m; ++i) Q(i, k) -= sum * Q(i, j);
123 
124  if(pivot) {
125  // Recompute column norm.
126  double sum = 0.0;
127  for(int i = j + 1; i < m; ++i) sum += Q(i, k) * Q(i, k);
128  dot(k) = sqrt(sum);
129  }
130  }
131 
132  // Transform r.h.s.
133  if(Q(j, j) != 0.0) {
134  double sum = 0.0;
135  for(int i = j; i < m; ++i) sum += Q(i, j) * QtB(i);
136  sum /= Q(j, j);
137  for(int i = j; i < m; ++i) QtB(i) -= Q(i, j) * sum;
138  }
139  }
140  }
141 }
142 
143 
145 {}
146 
147 
149  solve(R, QtB, X);
150 }
151 
152 
153 void QRSolver::solveS(const Array1D<double> &D, double p, Vector<double> &X) {
154  Vector<double> Z(m);
155  Array1D<double> M(D);
156  double root = sqrt(p);
157  for(int i = 0; i < n; ++i) M(i) *= root;
158 
159  R_to_S(R, M, S, Z);
160  solve(S, Z, X);
161 }
162 
163 
165  solveT(R, V);
166 }
167 
168 
170  solveT(S, V);
171 }
172 
173 
175  norms = column_norm;
176 }
177 
178 
181  // Eliminate the diagonal matrix D using Givens rotations.
182  assert(D.size() >= n);
183  S = R;
184  Z = QtB;
185 
186  for(int j = 0; j < n; ++j) {
187  // Prepare the row of D to be eliminated, locating the diagonal
188  // element using P from the QR-factorization.
189  int l = piv_col(j);
190 
191  if(D(l) != 0.0) {
192  Vector<double> row_D(n, 0.0);
193  row_D(j) = D(l);
194 
195  // The transformations to eliminate the row of D; modify only
196  // a single element of transpose(Q)*B beyond the first n,
197  // which is initially 0.
198  double QtBpj = 0.0;
199 
200  for(int k = j; k < n; ++k) {
201  // Determine a Givens rotation which eliminates the appropriate
202  // element in the current row of D.
203  if(row_D(k) != 0.0) {
204  double t = sqrt(S(k, k) * S(k, k) + row_D(k) * row_D(k));
205  double s = row_D(k) / t;
206  double c = S(k, k) / t;
207 
208  // Compute the modified diagonal element of S and the modified
209  // element of ((Q transpose)*B, 0).
210  S(k, k) = t;
211  t = c * Z(k) + s * QtBpj;
212  QtBpj = -s * Z(k) + c * QtBpj;
213  Z(k) = t;
214 
215  // Accumulate the transformation in the row of S.
216  for(int i = k + 1; i < n; ++i) {
217  t = c * S(k, i) + s * row_D(i);
218  row_D(i) = -s * S(k, i) + c * row_D(i);
219  S(k, i) = t;
220  }
221  }
222  }
223  }
224  }
225 }
226 
227 
229  Vector<double> &X) const {
230  // Copy QtB to the solution.
231  Vector<double> Z(QtB);
232 
233  // Solve the triangular system for Z.
234  // If R is singular, then obtain a least squares solution.
235  int rank = n;
236  for(int j = 0; j < n; ++j) {
237  if(R(j, j) == 0.0 && rank == n) rank = j;
238  if(rank < n) Z(j) = 0.0;
239  }
240 
241  for(int j = rank; j-- > 0;) {
242  double sum = Z(j);
243  for(int i = j + 1; i < rank; ++i) {
244  sum -= R(j, i) * Z(i);
245  }
246  Z(j) = sum / R(j, j);
247  }
248 
249  // Permute the components of Z back to components of X.
250  X = Vector<double>(n, 0.0);
251  for(int j = 0; j < n; ++j) {
252  X(piv_col(j)) = Z(j);
253  }
254 }
255 
256 
258  assert(V.size() == n);
259  Vector<double> X(n);
260  for(int i = 0; i < n; ++i) X(i) = V(piv_col(i));
261 
262  for(int i = 0; i < n; ++i) {
263  if(R(i, i) == 0.0) break;
264  for(int j = 0; j < i; ++j) X(j) -= R(i, j) * V(i);
265  X(i) /= R(i, i);
266  }
267 
268  V = X;
269 }
int n
Definition: QRSolver.h:115
void R_to_S(const Matrix< double > &R, const Array1D< double > &D, Matrix< double > &S, Vector< double > &Z)
Definition: QRSolver.cpp:179
int m
Definition: QRSolver.h:116
Array1D< double > column_norm
Definition: QRSolver.h:132
void solveRT(Vector< double > &V) const
Pre-multiply the vector $V$ by $R.transpose()^{-1}$.
Definition: QRSolver.cpp:164
Definition: rbendmap.h:8
Array1D< int > piv_col
Definition: QRSolver.h:136
int ncols() const
Get number of columns.
Definition: Array2D.h:307
void getColNorm(Array1D< double > &) const
Return the original column norms of the matrix $A$.
Definition: QRSolver.cpp:174
T::PETE_Expr_t::PETE_Return_t sum(const PETE_Expr< T > &expr)
Definition: PETE.h:1213
double dot(const Vector3D &lhs, const Vector3D &rhs)
Vector dot product.
Definition: Vector3D.cpp:118
void solveST(Vector< double > &V) const
Pre-multiply the vector $V$ by $S.transpose()^{-1}$.
Definition: QRSolver.cpp:169
int size() const
Get array size.
Definition: Array1D.h:228
Matrix< double > R
Definition: QRSolver.h:123
void solveR(Vector< double > &X) const
Solution of $A*X = B$ in the least-squares sense.
Definition: QRSolver.cpp:148
constexpr double c
The velocity of light in m/s.
Definition: Physics.h:52
QRSolver(const Matrix< double > &A, const Vector< double > &B, bool pivot)
Constructor.
Definition: QRSolver.cpp:64
Tps< T > sqrt(const Tps< T > &x)
Square root.
Definition: TpsMath.h:91
void solve(const Matrix< double > &R, const Vector< double > &QtB, Vector< double > &X) const
Definition: QRSolver.cpp:228
void solveT(const Matrix< double > &R, Vector< double > &V) const
Definition: QRSolver.cpp:257
Vector< double > QtB
Definition: QRSolver.h:126
Matrix< double > S
Definition: QRSolver.h:129
void solveS(const Array1D< double > &D, double p, Vector< double > &X)
Solution of $A*X = B, D*X = 0$ in the least-squares sense.
Definition: QRSolver.cpp:153
int nrows() const
Get number of rows.
Definition: Array2D.h:301
T::PETE_Expr_t::PETE_Return_t min(const PETE_Expr< T > &expr, NDIndex< D > &loc)
Definition: ReductionLoc.h:95