bpp-core3  3.0.0
LUDecomposition.h
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: The Bio++ Development Group
2 //
3 // SPDX-License-Identifier: CECILL-2.1
4 
5 #ifndef BPP_NUMERIC_MATRIX_LUDECOMPOSITION_H
6 #define BPP_NUMERIC_MATRIX_LUDECOMPOSITION_H
7 
8 
9 #include "../../Exceptions.h"
10 #include "../NumTools.h"
11 #include "Matrix.h"
12 
13 // From the STL:
14 #include <algorithm>
15 #include <vector>
16 // for min(), max() below
17 
18 namespace bpp
19 {
35 template<class Real>
37 {
38 private:
39  /* Array for internal storage of decomposition. */
43  size_t m, n;
44  int pivsign;
45  std::vector<size_t> piv;
46 
47 private:
48  static void permuteCopy(const Matrix<Real>& A, const std::vector<size_t>& piv, size_t j0, size_t j1, Matrix<Real>& X)
49  {
50  size_t piv_length = piv.size();
51 
52  X.resize(piv_length, j1 - j0 + 1);
53 
54  for (size_t i = 0; i < piv_length; i++)
55  {
56  for (size_t j = j0; j <= j1; j++)
57  {
58  X(i, j - j0) = A(piv[i], j);
59  }
60  }
61  }
62 
63  static void permuteCopy(const std::vector<Real>& A, const std::vector<size_t>& piv, std::vector<Real>& X)
64  {
65  size_t piv_length = piv.size();
66  if (piv_length != A.size())
67  X.clean();
68 
69  X.resize(piv_length);
70 
71  for (size_t i = 0; i < piv_length; i++)
72  {
73  X[i] = A[piv[i]];
74  }
75  }
76 
77 public:
85  // This is the constructor in JAMA C++ port. However, it seems to have some bug...
86  // We use the original JAMA's 'experimental' port, which gives good results instead.
87  /* LUDecomposition (const Matrix<Real> &A) : LU(A), m(A.getNumberOfRows()), n(A.getNumberOfColumns()), piv(A.getNumberOfRows())
88  {
89 
90  // Use a "left-looking", dot-product, Crout/Doolittle algorithm.
91 
92 
93  for (size_t i = 0; i < m; i++) {
94  piv[i] = i;
95  }
96  pivsign = 1;
97  //Real *LUrowi = 0;;
98  vector<Real> LUrowi;
99  vector<Real> LUcolj;
100 
101  // Outer loop.
102 
103  for (size_t j = 0; j < n; j++) {
104 
105  // Make a copy of the j-th column to localize references.
106 
107  LUcolj = LU.col(j);
108  //for (size_t i = 0; i < m; i++) {
109  // LUcolj[i] = LU(i,j);
110  //}
111 
112  // Apply previous transformations.
113 
114  for (size_t i = 0; i < m; i++) {
115  //LUrowi = LU[i];
116  LUrowi = LU.row(i);
117 
118  // Most of the time is spent in the following dot product.
119 
120  size_t kmax = NumTools::min<size_t>(i,j);
121  double s = 0.0;
122  for (size_t k = 0; k < kmax; k++) {
123  s += LUrowi[k] * LUcolj[k];
124  }
125 
126  LUrowi[j] = LUcolj[i] -= s;
127  }
128 
129  // Find pivot and exchange if necessary.
130 
131  size_t p = j;
132  for (size_t i = j+1; i < m; i++) {
133  if (NumTools::abs<Real>(LUcolj[i]) > NumTools::abs<Real>(LUcolj[p])) {
134  p = i;
135  }
136  }
137  if (p != j) {
138  size_t k=0;
139  for (k = 0; k < n; k++) {
140  double t = LU(p,k);
141  LU(p,k) = LU(j,k);
142  LU(j,k) = t;
143  }
144  k = piv[p];
145  piv[p] = piv[j];
146  piv[j] = k;
147  pivsign = -pivsign;
148  }
149 
150  // Compute multipliers.
151 
152  if ((j < m) && (LU(j,j) != 0.0)) {
153  for (size_t i = j+1; i < m; i++) {
154  LU(i,j) /= LU(j,j);
155  }
156  }
157  }
158  }
159  */
160 
162  LU(A),
163  L_(A.getNumberOfRows(), A.getNumberOfColumns()),
164  U_(A.getNumberOfColumns(), A.getNumberOfColumns()),
165  m(A.getNumberOfRows()),
166  n(A.getNumberOfColumns()),
167  pivsign(1),
168  piv(A.getNumberOfRows())
169  {
170  for (size_t i = 0; i < m; i++)
171  {
172  piv[i] = i;
173  }
174  // Main loop.
175  for (size_t k = 0; k < n; k++)
176  {
177  // Find pivot.
178  size_t p = k;
179  for (size_t i = k + 1; i < m; i++)
180  {
181  if (NumTools::abs<Real>(LU(i, k)) > NumTools::abs<Real>(LU(p, k)))
182  {
183  p = i;
184  }
185  }
186  // Exchange if necessary.
187  if (p != k)
188  {
189  for (size_t j = 0; j < n; j++)
190  {
191  double t = LU(p, j); LU(p, j) = LU(k, j); LU(k, j) = t;
192  }
193  size_t t = piv[p]; piv[p] = piv[k]; piv[k] = t;
194  pivsign = -pivsign;
195  }
196  // Compute multipliers and eliminate k-th column.
197  if (LU(k, k) != 0.0)
198  {
199  for (size_t i = k + 1; i < m; i++)
200  {
201  LU(i, k) /= LU(k, k);
202  for (size_t j = k + 1; j < n; j++)
203  {
204  LU(i, j) -= LU(i, k) * LU(k, j);
205  }
206  }
207  }
208  }
209  }
210 
217  {
218  for (size_t i = 0; i < m; i++)
219  {
220  for (size_t j = 0; j < n; j++)
221  {
222  if (i > j)
223  {
224  L_(i, j) = LU(i, j);
225  }
226  else if (i == j)
227  {
228  L_(i, j) = 1.0;
229  }
230  else
231  {
232  L_(i, j) = 0.0;
233  }
234  }
235  }
236  return L_;
237  }
238 
245  {
246  for (size_t i = 0; i < n; i++)
247  {
248  for (size_t j = 0; j < n; j++)
249  {
250  if (i <= j)
251  {
252  U_(i, j) = LU(i, j);
253  }
254  else
255  {
256  U_(i, j) = 0.0;
257  }
258  }
259  }
260  return U_;
261  }
262 
268  std::vector<size_t> getPivot () const
269  {
270  return piv;
271  }
272 
273 
279  Real det() const
280  {
281  if (m != n)
282  {
283  return Real(0);
284  }
285  Real d = Real(pivsign);
286  for (size_t j = 0; j < n; j++)
287  {
288  d *= LU(j, j);
289  }
290  return d;
291  }
292 
304  Real solve (const Matrix<Real>& B, Matrix<Real>& X) const
305  {
306  /* Dimensions: A is mxn, X is nxk, B is mxk */
307 
308  if (B.getNumberOfRows() != m)
309  {
310  throw BadIntegerException("Wrong dimension in LU::solve", static_cast<int>(B.getNumberOfRows()));
311  }
312 
313  Real minD = NumTools::abs<Real>(LU(0, 0));
314  for (size_t i = 1; i < m; i++)
315  {
316  Real currentValue = NumTools::abs<Real>(LU(i, i));
317  if (currentValue < minD)
318  minD = currentValue;
319  }
320 
321  if (minD < NumConstants::SMALL())
322  {
323  throw ZeroDivisionException("Singular matrix in LU::solve.");
324  }
325 
326  // Copy right hand side with pivoting
327  size_t nx = B.getNumberOfColumns();
328 
329  permuteCopy(B, piv, 0, nx - 1, X);
330 
331  // Solve L*Y = B(piv,:)
332  for (size_t k = 0; k < n; k++)
333  {
334  for (size_t i = k + 1; i < n; i++)
335  {
336  for (size_t j = 0; j < nx; j++)
337  {
338  X(i, j) -= X(k, j) * LU(i, k);
339  }
340  }
341  }
342  // Solve U*X = Y;
343  // !!! Do not use unsigned int with -- loop!!!
344  // for (int k = n-1; k >= 0; k--) {
345  size_t k = n;
346 
347  do
348  {
349  k--;
350  for (size_t j = 0; j < nx; j++)
351  {
352  X(k, j) /= LU(k, k);
353  }
354  for (size_t i = 0; i < k; i++)
355  {
356  for (size_t j = 0; j < nx; j++)
357  {
358  X(i, j) -= X(k, j) * LU(i, k);
359  }
360  }
361  }
362  while (k > 0);
363 
364  return minD;
365  }
366 
367 
378  Real solve (const std::vector<Real>& b, std::vector<Real>& x) const
379  {
380  /* Dimensions: A is mxn, X is nxk, B is mxk */
381 
382  if (b.dim1() != m)
383  {
384  throw BadIntegerException("Wrong dimension in LU::solve", b.dim1());
385  }
386 
387  Real minD = NumTools::abs<Real>(LU(0, 0));
388  for (size_t i = 1; i < m; i++)
389  {
390  Real currentValue = NumTools::abs<Real>(LU(i, i));
391  if (currentValue < minD)
392  minD = currentValue;
393  }
394 
395  if (minD < NumConstants::SMALL())
396  {
397  throw ZeroDivisionException("Singular matrix in LU::solve.");
398  }
399 
400  permuteCopy(b, piv, x);
401 
402  // Solve L*Y = B(piv)
403  for (size_t k = 0; k < n; k++)
404  {
405  for (size_t i = k + 1; i < n; i++)
406  {
407  x[i] -= x[k] * LU(i, k);
408  }
409  }
410 
411  // Solve U*X = Y;
412  // for (size_t k = n-1; k >= 0; k--) {
413  size_t k = n;
414  do
415  {
416  k--;
417  x[k] /= LU(k, k);
418  for (size_t i = 0; i < k; i++)
419  {
420  x[i] -= x[k] * LU(i, k);
421  }
422  }
423  while (k > 0);
424 
425  return minD;
426  }
427 }; /* class LU */
428 } // end of namespace bpp.
429 #endif // BPP_NUMERIC_MATRIX_LUDECOMPOSITION_H
The matrix template interface.
Definition: Matrix.h:22
The base class exception for zero division error.
Definition: Exceptions.h:67
const RowMatrix< Real > & getL()
Return lower triangular factor.
const RowMatrix< Real > & getU()
Return upper triangular factor.
virtual void resize(size_t nRows, size_t nCols)=0
Resize the matrix.
Number exception: integers.
Definition: Exceptions.h:77
Real solve(const std::vector< Real > &b, std::vector< Real > &x) const
Solve A*x = b, where x and b are vectors of length equal to the number of rows in A...
static void permuteCopy(const std::vector< Real > &A, const std::vector< size_t > &piv, std::vector< Real > &X)
RowMatrix< Real > L_
std::vector< size_t > piv
Real det() const
Compute determinant using LU factors.
virtual size_t getNumberOfColumns() const =0
Real solve(const Matrix< Real > &B, Matrix< Real > &X) const
Solve A*X = B.
LUDecomposition(const Matrix< Real > &A)
LU Decomposition.
static double SMALL()
Definition: NumConstants.h:45
LU Decomposition.
RowMatrix< Real > LU
static void permuteCopy(const Matrix< Real > &A, const std::vector< size_t > &piv, size_t j0, size_t j1, Matrix< Real > &X)
RowMatrix< Real > U_
virtual size_t getNumberOfRows() const =0
std::vector< size_t > getPivot() const
Return pivot permutation vector.