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