Virtual Testbed
Ship dynamics simulator for extreme conditions
linear_algebra.cc
1 #include <iostream>
2 
3 #include <vtestbed/config/real_type.hh>
4 #include <vtestbed/linalg/linear_algebra.hh>
5 
6 //#define VTB_DEBUG_LINEAR_ALGEBRA
7 //#define VTB_DEBUG_EIGEN_VALUES
8 
9 template <class T>
10 bool
12  using blitz::all;
13  return all(*this == this->transpose());
14 }
15 
16 template <class T>
17 bool
19  using blitz::all;
20  using blitz::abs;
21  return all(abs(*this - this->transpose()) < eps);
22 }
23 
24 template <class T>
25 bool
27  using blitz::Range;
28  using blitz::sum;
29  using blitz::pow2;
30  using std::sqrt;
31  #if defined(VTB_DEBUG_LINEAR_ALGEBRA)
32  if (!this->is_square()) {
33  throw std::runtime_error{"bad shape"};
34  }
35  #endif
36  const auto& rhs = *this;
37  Matrix<T> L(rhs.shape());
38  L = 0;
39  const int n = rhs.rows();
40  for (int i=0; i<n; ++i) {
41  for (int j=0; j<i; ++j) {
42  Range r{0,j};
43  L(i,j) = (rhs(i,j) - sum(L(i,r)*L(j,r))) / L(j,j);
44  }
45  T s = sum(pow2(L(i, Range(0,i))));
46  if (rhs(i,i) < s) { return false; }
47  L(i,i) = sqrt(rhs(i,i) - s);
48  }
49  return true;
50 }
51 
52 template <class T>
53 bool
55  const auto& rhs = *this;
56  const int nrows = rhs.rows();
57  const int ncols = rhs.cols();
58  for (int i=1; i<nrows; ++i) {
59  for (int j=1; j<ncols; ++j) {
60  if (rhs(i,j) != rhs(i-1, j-1)) { return false; }
61  }
62  }
63  return true;
64 }
65 
66 template <class T>
67 bool
69  using std::abs;
70  const auto& rhs = *this;
71  const int nrows = rhs.rows();
72  const int ncols = rhs.cols();
73  for (int i=1; i<nrows; ++i) {
74  for (int j=1; j<ncols; ++j) {
75  if (abs(rhs(i,j) - rhs(i-1,j-1)) < eps) {
76  return false;
77  }
78  }
79  }
80  return true;
81 }
82 
83 template <class T>
84 auto
86  #if defined(VTB_DEBUG_LINEAR_ALGEBRA)
87  if (!this->is_square()) {
88  throw std::runtime_error{"bad shape"};
89  }
90  #endif
91  using blitz::sum;
92  auto& L = *this;
93  const int n = L.rows();
94  Vector<T> y(b.shape());
95  y(0) = b(0) / L(0,0);
96  for (int i=1; i<n; ++i) {
97  blitz::Range r{0,i-1};
98  y(i) = (b(i) - sum(L(i,r)*y(r))) / L(i,i);
99  }
100  return y;
101 }
102 
103 template <class T>
104 auto
106  #if defined(VTB_DEBUG_LINEAR_ALGEBRA)
107  if (!this->is_square()) {
108  throw std::runtime_error{"bad shape"};
109  }
110  #endif
111  auto& L = *this;
112  const int n = L.rows();
113  Vector<T> x(y.shape());
114  x(n-1) = y(n-1) / L(n-1,n-1);
115  for (int i=n-2; i>=0; --i) {
116  blitz::Range r{i+1,n-1};
117  T sum{0};
118  for (int j=i+1; j<n; ++j) {
119  sum += conj(L(j,i)) * x(j);
120  }
121  x(i) = (y(i) - sum) / L(i,i);
122  }
123  return x;
124 }
125 
126 template <class T>
127 auto
129  #if defined(VTB_DEBUG_LINEAR_ALGEBRA)
130  if (!this->is_square() ||
131  !this->is_symmetric()) {
132  throw std::runtime_error{"bad matrix"};
133  }
134  #endif
135  using namespace blitz;
136  Lower_triangular_matrix_ldlt<T> L(this->shape());
137  L = 0;
138  auto& A = *this;
139  const int n = A.rows();
140  Vector<T> d(n);
141  int j = 0;
142  d(j) = A(j,j);
143  for (int i=0; i<n; ++i) {
144  L(i,j) = A(i,j) / d(j);
145  }
146  for (j=1; j<n; ++j) {
147  Range r{0,j-1};
148  d(j) = A(j,j) - sum(pow2(L(j,r))*d(r));
149  for (int i=0; i<n; ++i) {
150  L(i,j) = (A(i,j) - sum(L(i,r)*d(r)*L(j,r))) / d(j);
151  }
152  }
153  L._diagonal.reference(d);
154  return L;
155 }
156 
157 template <class T>
158 auto
160  using std::abs;
161  using std::sqrt;
162  Symmetric_matrix<T> a(this->copy());
163  const int n = a.rows();
164  Vector<T> result(n);
165  result = 0;
166  // The number of elements under the main diagonal.
167  int nelements = n*(n - 1) / 2;
168  int niterations = nelements*nsweeps;
169  for (int it=0; it<niterations; ++it) {
172  int pivot_i = 0;
173  int pivot_j = 1;
174  T pivot = abs(a(0,1));
175  for (int i=0; i<n; ++i) {
176  for (int j=0; j<n; ++j) {
177  if (i == j) {
178  continue;
179  }
180  T new_pivot = abs(a(i,j));
181  if (new_pivot > pivot) {
182  pivot = new_pivot;
183  pivot_i = i;
184  pivot_j = j;
185  }
186  }
187  }
188  if (abs(pivot) < eps) {
189  break;
190  }
191  int i = pivot_i;
192  int j = pivot_j;
195  T w = (a(j,j) - a(i,i)) / (2*a(i,j));
196  T sqrt_w = sqrt(1 + w*w);
197  T t1 = -w + sqrt_w, t2 = -w - sqrt_w;
198  T t = t1;
199  if (t1 == 0 || (t2 != 0 && abs(t2) < abs(t1))) {
200  t = t2;
201  }
202  T r = sqrt(1 + t*t);
203  T cos = 1 / r, sin = t / r;
204  T tau = sin / (1 + cos);
206  T a_ij = a(i,j);
207  a(i,j) = (cos*cos - sin*sin)*a(i,j) + cos*sin*(a(i,i) - a(j,j));
208  a(j,i) = a(i,j);
209  a(i,i) -= t*a_ij;
210  a(j,j) += t*a_ij;
211  for (int k=0; k<n; ++k) {
212  if (k == i || k == j) {
213  continue;
214  }
215  T sa_ik = sin*a(i,k);
216  T sa_jk = sin*a(j,k);
217  a(i,k) -= sa_jk + tau*sa_ik;
218  a(j,k) += sa_ik - tau*sa_jk;
219  a(k,i) = a(i,k);
220  a(k,j) = a(j,k);
221  }
222  #if defined(VTB_DEBUG_EIGEN_VALUES)
223  std::clog
224  << std::setw(20) << __func__
225  << std::setw(20) << pivot
226  << std::setw(20) << w
227  << std::setw(20) << t
228  << std::setw(20) << cos
229  << std::setw(20) << sin
230  << std::endl;
231  #endif
232  }
233  for (int i=0; i<n; ++i) {
234  result(i) = a(i,i);
235  }
236  return result;
237 }
238 
239 template <class T>
240 auto
242  #if defined(VTB_DEBUG_LINEAR_ALGEBRA)
243  if (!this->is_square() || !this->is_symmetric()) {
244  throw std::runtime_error{"bad shape"};
245  }
246  #endif
247  using blitz::Range;
248  using blitz::sum;
249  using blitz::pow2;
250  using std::sqrt;
251  Lower_triangular_matrix<T> L(this->shape());
252  L = 0;
253  auto& rhs = *this;
254  const int n = rhs.rows();
255  for (int i=0; i<n; ++i) {
256  for (int j=0; j<i; ++j) {
257  Range r{0,j};
258  L(i,j) = (rhs(i,j) - sum(L(i,r)*L(j,r))) / L(j,j);
259  }
260  T s = rhs(i,i) - sum(pow2(L(i, Range(0,i))));
261  if (s <= 0) {
262  std::clog << "i=" << i << std::endl;
263  std::clog << "s=" << s << std::endl;
264  throw Invalid_matrix("not positive definite");
265  }
266  L(i,i) = sqrt(s);
267  }
268  return L;
269 }
270 
271 template <class T>
272 auto
274  #if defined(VTB_DEBUG_LINEAR_ALGEBRA)
275  if (!this->is_square()) {
276  throw std::runtime_error{"bad shape"};
277  }
278  #endif
279  Square_matrix<T> result(this->shape());
280  auto& rhs = *this;
281  const int n = rhs.rows();
282  for (int j=0; j<n; ++j) {
283  for (int i=0; i<n; ++i) {
284  result(i,j) = (_permutations(i) == j) ? 1 : 0;
285  for (int k=0; k<i; ++k) {
286  result(i,j) -= rhs(i,k) * result(k,j);
287  }
288  }
289  for (int i=n-1; i>=0; --i) {
290  for (int k=i+1; k<n; ++k) {
291  result(i,j) -= rhs(i,k) * result(k,j);
292  }
293  result(i,j) /= rhs(i,i);
294  if (!std::isfinite(result(i,j))) {
295  throw std::invalid_argument{"inverse: bad matrix"};
296  }
297  }
298  }
299  return result;
300 }
301 
302 template <class T>
303 auto
304 vtb::linalg::Lower_upper_triangular_matrix<T>::solve(const Vector<T>& b) const -> Vector<T> {
305  const auto& A = *this;
306  const auto& p = this->_permutations;
307  const int n = this->rows();
308  Vector<T> x(b.shape());
309  for (int i=0; i<n; ++i) {
310  x(i) = b(p(i));
311  }
312  for (int i=0; i<n; ++i) {
313  for (int k=0; k<i; ++k) {
314  x(i) -= A(i,k)*x(k);
315  }
316  }
317  for (int i=n-1; i>=0; --i) {
318  for (int k=i+1; k<n; ++k) {
319  x(i) -= A(i,k)*x(k);
320  }
321  x(i) /= A(i,i);
322  if (!std::isfinite(x(i))) {
323  throw std::invalid_argument{"solve: bad matrix"};
324  }
325  }
326  return x;
327 }
328 
329 template <class T>
330 auto
332  #if defined(VTB_DEBUG_LINEAR_ALGEBRA)
333  if (!this->is_square()) {
334  throw std::runtime_error{"bad shape"};
335  }
336  #endif
337  using std::abs;
338  using std::isfinite;
339  using std::swap;
340  using blitz::abs;
341  using blitz::max;
342  using blitz::Range;
343  const auto& _ = Range::all();
344  auto& rhs = *this;
345  const int n = rhs.rows();
346  auto& p = this->_permutations;
347  p.resize(n);
348  for (int i=0; i<n; ++i) {
349  p(i) = i;
350  }
351  int npermutations = 0;
352  Vector<T> tmp(n);
353  for (int i=0; i<n; ++i) {
354  int pivot = 0;
355  T elem{0};
356  // find max element
357  for (int j=i; j<n; ++j) {
358  const T new_elem = abs(rhs(j,i));
359  if (elem < new_elem) {
360  elem = new_elem;
361  pivot = j;
362  }
363  }
364  // swap rows
365  if (pivot != i) {
366  swap(p(i), p(pivot));
367  tmp = rhs(pivot,_);
368  rhs(pivot,_) = rhs(i,_);
369  rhs(i,_) = tmp;
370  ++npermutations;
371  }
372  for (int j=i+1; j<n; ++j) {
373  const T x = rhs(i,i);
374  rhs(j,i) /= x;
375  if (!isfinite(rhs(j,i))) {
376  throw std::invalid_argument{
377  "lower_upper_triangular_self: bad matrix"
378  };
379  }
380  for (int k=i+1; k<n; ++k) {
381  rhs(j,k) -= rhs(j,i)*rhs(i,k);
382  }
383  }
384  }
385  this->_npermutations = npermutations;
386  return *this;
387 }
388 
389 template <class T>
390 T
392  #if defined(VTB_DEBUG_LINEAR_ALGEBRA)
393  if (!this->is_square()) {
394  throw std::runtime_error{"bad shape"};
395  }
396  #endif
397  using namespace blitz;
398  firstIndex i;
399  auto& rhs = *this;
400  T result = product(rhs(i,i));
401  if (this->_npermutations%2 == 1) {
402  result = -result;
403  }
404  return result;
405 }
406 
407 template <class T>
408 auto
409 vtb::linalg::product(const Matrix<T>& lhs, const Vector<T>& rhs) -> Vector<T>
410  {
411  #if defined(VTB_DEBUG_LINEAR_ALGEBRA)
412  if (lhs.extent(1) != rhs.extent(0)) {
413  std::cerr << lhs.shape() << std::endl;
414  std::cerr << rhs.shape() << std::endl;
415  throw std::runtime_error{"product: bad shape"};
416  }
417  #endif
418  const blitz::Range& _ = blitz::Range::all();
419  const int nrows = lhs.rows();
420  Vector<T> result(nrows);
421  for (int i=0; i<nrows; ++i) {
422  result(i) = blitz::dot(lhs(i,_), rhs);
423  }
424  return result;
425 }
426 
427 template <class T>
428 auto
429 vtb::linalg::product(const Matrix<T>& lhs, const Matrix<T>& rhs) -> Matrix<T>
430  {
431  const int nrows = lhs.rows();
432  const int ncols = rhs.cols();
433  #if defined(VTB_DEBUG_LINEAR_ALGEBRA)
434  if (lhs.cols() != rhs.rows()) {
435  throw std::runtime_error{"bad shape"};
436  }
437  #endif
438  const blitz::Range& _ = blitz::Range::all();
439  Matrix<T> result(nrows, ncols);
440  for (int i=0; i<nrows; ++i) {
441  for (int j=0; j<ncols; ++j) {
442  result(i,j) = blitz::dot(lhs(i,_), rhs(_,j));
443  }
444  }
445  return result;
446 }
447 
453 
454 template auto
455 vtb::linalg::product(
456  const Matrix<VTB_REAL_TYPE>& lhs,
457  const Matrix<VTB_REAL_TYPE>& rhs
458 ) -> Matrix<VTB_REAL_TYPE>;
459 
460 template auto
461 vtb::linalg::product(
462  const Matrix<VTB_REAL_TYPE>& lhs,
463  const Vector<VTB_REAL_TYPE>& rhs
464 ) -> Vector<VTB_REAL_TYPE>;
465 
466 #if defined(VTB_REAL_TYPE_FLOAT)
471 template auto
472 vtb::linalg::product(
473  const Matrix<double>& lhs,
474  const Matrix<double>& rhs
475 ) -> Matrix<double>;
476 template auto
477 vtb::linalg::product(
478  const Matrix<double>& lhs,
479  const Vector<double>& rhs
480 ) -> Vector<double>;
481 #endif
T swap(T... args)
Vector< T > backward_substitution(const Vector< T > &y) const
Backward substitution.
bool is_positive_definite() const
New and missing Blitz++ functions.
Definition: blitz.hh:16
Vector< T > eigen_values(T eps, int nsweeps) const
Compute eigen values.
T isfinite(T... args)
Lower_triangular_matrix< T > cholesky() const
Cholesky decomposition.
Vector< T > forward_substitution(const Vector< T > &b) const
Forward substitution.