EOL BSpline Library  v2.2
All Classes Files Functions Variables Enumerations Enumerator Pages
BandedMatrix.h
1 /* -*- mode: c++; c-basic-offset: 4; -*- */
2 /************************************************************************
3  * Copyright 2022 University Corporation for Atmospheric Research.
4  * All rights reserved.
5  *
6  * Use of this code is subject to the standard BSD license:
7  *
8  * http://www.opensource.org/licenses/bsd-license.html
9  *
10  * See the COPYRIGHT file in the source distribution for the license text,
11  * or see this web page:
12  *
13  * http://www.eol.ucar.edu/homes/granger/bspline/doc/
14  *
15  *************************************************************************/
16 
20 #ifndef _BANDEDMATRIX_ID
21 #define _BANDEDMATRIX_ID "$Id$"
22 
23 #include <vector>
24 #include <algorithm>
25 #include <iostream>
26 
27 template <class T> class BandedMatrixRow;
28 
29 template <class T> class BandedMatrix
30 {
31 public:
32  typedef unsigned int size_type;
33  typedef T element_type;
34 
35  // Create a banded matrix with the same number of bands above and below
36  // the diagonal.
37  BandedMatrix(int N_ = 1, int nbands_off_diagonal = 0)
38  {
39  if (!setup(N_, nbands_off_diagonal))
40  setup();
41  }
42 
43  // Create a banded matrix by naming the first and last non-zero bands,
44  // where the diagonal is at zero, and bands below the diagonal are
45  // negative, bands above the diagonal are positive.
46  BandedMatrix(int N_, int first, int last)
47  {
48  if (!setup(N_, first, last))
49  setup();
50  }
51 
52  inline bool
53  setup(int N_ = 1, int noff = 0)
54  {
55  return setup(N_, -noff, noff);
56  }
57 
58  bool
59  setup(int N_, int first, int last)
60  {
61  // Check our limits first and make sure they make sense.
62  // Don't change anything until we know it will work.
63  if (first > last || N_ <= 0)
64  return false;
65 
66  // Need at least as many N_ as columns and as rows in the bands.
67  if (N_ < abs(first) || N_ < abs(last))
68  return false;
69 
70  top = last;
71  bot = first;
72  N = N_;
73  out_of_bounds = T();
74 
75  // Finally setup the diagonal vectors
76  nbands = last - first + 1;
77  bands.resize(nbands);
78  int i;
79  for (i = 0; i < nbands; ++i)
80  {
81  // The length of each array varies with its distance from the
82  // diagonal
83  int len = N - (abs(bot + i));
84  bands[i].clear();
85  bands[i].resize(len);
86  }
87  return true;
88  }
89 
91  operator=(const T& e)
92  {
93  int i;
94  for (i = 0; i < nbands; ++i)
95  {
96  std::fill_n(bands[i].begin(), bands[i].size(), e);
97  }
98  out_of_bounds = e;
99  return (*this);
100  }
101 
102  BandedMatrix<T>(BandedMatrix<T>&) = default;
103  BandedMatrix<T>& operator=(BandedMatrix<T>&) = default;
104 
105 private:
106  // Return false if coordinates are out of bounds
107  inline bool
108  check_bounds(int i, int j, int& v, int& e) const
109  {
110  v = (j - i) - bot;
111  e = (i >= j) ? j : i;
112  return !(v < 0 || v >= nbands || e < 0 ||
113  (unsigned int)e >= bands[v].size());
114  }
115 
116 public:
117  T&
118  element(int i, int j)
119  {
120  int v, e;
121  if (check_bounds(i, j, v, e))
122  return (bands[v][e]);
123  else
124  return out_of_bounds;
125  }
126 
127  const T&
128  element(int i, int j) const
129  {
130  int v, e;
131  if (check_bounds(i, j, v, e))
132  return (bands[v][e]);
133  else
134  return out_of_bounds;
135  }
136 
137  inline T&
138  operator()(int i, int j)
139  {
140  return element(i - 1, j - 1);
141  }
142 
143  inline const T&
144  operator()(int i, int j) const
145  {
146  return element(i - 1, j - 1);
147  }
148 
149  size_type
150  num_rows() const
151  {
152  return N;
153  }
154 
155  size_type
156  num_cols() const
157  {
158  return N;
159  }
160 
161  const BandedMatrixRow<T>
162  operator[](int row) const
163  {
164  return BandedMatrixRow<T>(*this, row);
165  }
166 
168  operator[](int row)
169  {
170  return BandedMatrixRow<T>(*this, row);
171  }
172 
173 private:
174  // Each diagonal band is a vector of T.
175  typedef std::vector<T> diagonal_t;
176 
177  int top;
178  int bot;
179  int nbands;
180  std::vector<diagonal_t> bands;
181  int N;
182  T out_of_bounds;
183 };
184 
185 template <class T>
186 std::ostream&
187 operator<<(std::ostream& out, const BandedMatrix<T>& m)
188 {
189  unsigned int i, j;
190  for (i = 0; i < m.num_rows(); ++i)
191  {
192  for (j = 0; j < m.num_cols(); ++j)
193  {
194  out << m.element(i, j) << " ";
195  }
196  out << std::endl;
197  }
198  return out;
199 }
200 
201 /*
202  * Helper class for the intermediate in the [][] operation.
203  */
204 template <class T> class BandedMatrixRow
205 {
206 public:
207  BandedMatrixRow(const BandedMatrix<T>& _m, int _row) : bm(_m), i(_row) {}
208 
209  BandedMatrixRow(BandedMatrix<T>& _m, int _row) : bm(_m), i(_row) {}
210 
211  typename BandedMatrix<T>::element_type&
212  operator[](int j)
213  {
214  return const_cast<BandedMatrix<T>&>(bm).element(i, j);
215  }
216 
217  const typename BandedMatrix<T>::element_type&
218  operator[](int j) const
219  {
220  return bm.element(i, j);
221  }
222 
223 private:
224  const BandedMatrix<T>& bm;
225  int i;
226 };
227 
228 /*
229  * Vector multiplication
230  */
231 
232 template <class Vector, class Matrix>
233 Vector
234 operator*(const Matrix& m, const Vector& v)
235 {
236  typename Matrix::size_type M = m.num_rows();
237  typename Matrix::size_type N = m.num_cols();
238 
239  assert(N <= v.size());
240  // if (M > v.size())
241  // return Vector();
242 
243  Vector r(N);
244  for (unsigned int i = 0; i < M; ++i)
245  {
246  typename Matrix::element_type sum = 0;
247  for (unsigned int j = 0; j < N; ++j)
248  {
249  sum += m[i][j] * v[j];
250  }
251  r[i] = sum;
252  }
253  return r;
254 }
255 
256 /*
257  * LU factor a diagonally banded matrix using Crout's algorithm, but
258  * limiting the trailing sub-matrix multiplication to the non-zero
259  * elements in the diagonal bands. Return nonzero if a problem occurs.
260  */
261 template <class MT>
262 int
263 LU_factor_banded(MT& A, unsigned int bands)
264 {
265  typename MT::size_type M = A.num_rows();
266  typename MT::size_type N = A.num_cols();
267  if (M != N)
268  return 1;
269 
270  typename MT::size_type i, j, k;
271  typename MT::element_type sum;
272 
273  for (j = 1; j <= N; ++j)
274  {
275  // Check for zero pivot
276  if (A(j, j) == 0)
277  return 1;
278 
279  // Calculate rows above and on diagonal. A(1,j) remains as A(1,j).
280  for (i = (j > bands) ? j - bands : 1; i <= j; ++i)
281  {
282  sum = 0;
283  for (k = (j > bands) ? j - bands : 1; k < i; ++k)
284  {
285  sum += A(i, k) * A(k, j);
286  }
287  A(i, j) -= sum;
288  }
289 
290  // Calculate rows below the diagonal.
291  for (i = j + 1; (i <= M) && (i <= j + bands); ++i)
292  {
293  sum = 0;
294  for (k = (i > bands) ? i - bands : 1; k < j; ++k)
295  {
296  sum += A(i, k) * A(k, j);
297  }
298  A(i, j) = (A(i, j) - sum) / A(j, j);
299  }
300  }
301 
302  return 0;
303 }
304 
305 /*
306  * Solving (LU)x = B. First forward substitute to solve for y, Ly = B.
307  * Then backwards substitute to find x, Ux = y. Return nonzero if a
308  * problem occurs. Limit the substitution sums to the elements on the
309  * bands above and below the diagonal.
310  */
311 template <class MT, class Vector>
312 int
313 LU_solve_banded(const MT& A, Vector& b, unsigned int bands)
314 {
315  typename MT::size_type i, j;
316  typename MT::size_type M = A.num_rows();
317  typename MT::size_type N = A.num_cols();
318  typename MT::element_type sum;
319 
320  if (M != N || M == 0)
321  return 1;
322 
323  // Forward substitution to find y. The diagonals of the lower
324  // triangular matrix are taken to be 1.
325  for (i = 2; i <= M; ++i)
326  {
327  sum = b[i - 1];
328  for (j = (i > bands) ? i - bands : 1; j < i; ++j)
329  {
330  sum -= A(i, j) * b[j - 1];
331  }
332  b[i - 1] = sum;
333  }
334 
335  // Now for the backward substitution
336  b[M - 1] /= A(M, M);
337  for (i = M - 1; i >= 1; --i)
338  {
339  if (A(i, i) == 0) // oops!
340  return 1;
341  sum = b[i - 1];
342  for (j = i + 1; (j <= N) && (j <= i + bands); ++j)
343  {
344  sum -= A(i, j) * b[j - 1];
345  }
346  b[i - 1] = sum / A(i, i);
347  }
348 
349  return 0;
350 }
351 
352 #endif /* _BANDEDMATRIX_ID */
Definition: BandedMatrix.h:205
Definition: BandedMatrix.h:30
Definition: BSplineBase.cpp:57