NiHu  2.0
aca.hpp
Go to the documentation of this file.
1 
6 #ifndef ACA_HPP_INCLUDED
7 #define ACA_HPP_INCLUDED
8 
9 #include <Eigen/Dense>
10 #include <type_traits> // std::decay
11 #include <utility> // std::forward
12 #include <vector>
13 
17 template <class Scalar>
18 class LowRank
19 {
20 public:
22  LowRank(void)
23  {
24  }
25 
33  template <class UType, class VType>
34  LowRank(int row0, int col0, Eigen::MatrixBase<UType> const &U, Eigen::DenseBase<VType> const &V)
35  : row0(row0), col0(col0), U(U), V(V)
36  {
37  }
38 
40  Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> const &getU(void) const
41  {
42  return U;
43  }
44 
46  Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> const &getV(void) const
47  {
48  return V;
49  }
50 
52  int getRow0(void) const
53  {
54  return row0;
55  }
56 
58  int getCol0(void) const
59  {
60  return col0;
61  }
62 
63 private:
64  int row0;
65  int col0;
66  Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> U;
67  Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> V;
68 };
69 
71 class ACA
72 {
73 private:
76  template <class Matrix>
77  class Block
78  {
79  public:
81  typedef typename std::decay<Matrix>::type::Scalar Scalar;
82 
87  Block(Matrix M, int row0, int col0)
88  : M(std::forward<Matrix>(M)), row0(row0), col0(col0)
89  {
90  }
91 
96  Scalar operator()(int i, int j) const
97  {
98  return M(row0+i, col0+j);
99  }
100 
101  private:
102  Matrix M;
103  int row0, col0;
104  };
105 
106 
113  template <class Matrix>
114  static Block<Matrix>
115  createBlock(Matrix &&M, int row0, int col0)
116  {
117  return Block<Matrix>(std::forward<Matrix>(M), row0, col0);
118  }
119 
120 
121 public:
134  template <class Matrix, class Result>
135  static int low_rank_approx(Matrix const &M, int nRows, int nCols, double eps, int R,
136  Eigen::DenseBase<Result> &U, Eigen::DenseBase<Result> &V)
137  {
138  // /// \brief the scalar type of the matrix
139  // typedef typename std::decay<Matrix>::type::Scalar Scalar;
140 
141  // result counter
142  int r = 0;
143 
144  // estimate typical matrix entry magnitude
145  auto magest = std::abs(M(nRows/2,nCols/2));
146 
147  // start row
148  int i = 0;
149 
150  // cumulative norm estimate
151  double S2 = 0.;
152 
153  // iteration counter, R iterations should always be enough
154  for (int k = 0; k < R; ++k)
155  {
156  // compute i-th matrix row
157  for (int s = 0; s < nCols; ++s)
158  V(s,r) = M(i,s);
159  // subtract already approximated parts
160  if (r > 0)
161  V.col(r) -= V.leftCols(r) * U.block(i,0,1,r).transpose();
162 
163  // search row coefficient with maximal magnitude
164  int j = 0;
165  for (int s = 1; s < nCols; ++s)
166  if (std::abs(V(s,r)) > std::abs(V(j,r)))
167  j = s;
168 
169  // if full zero row has been found we are ready
170  if (std::abs(V(j,r)) / magest < 1e-10)
171  break;
172 
173  // compute j-th column
174  for (int s = 0; s < nRows; ++s)
175  U(s,r) = M(s,j);
176  // subtract already approximated parts
177  if (r > 0)
178  U.col(r) -= U.leftCols(r) * V.block(j,0,1,r).transpose();
179  // normalise
180  U.col(r) /= U(i,r);
181 
182  // indicate that r rows and columns are ready
183  ++r;
184 
185  // update estimate of cumulated norm
186  S2 = S2 + U.col(r-1).squaredNorm() * V.col(r-1).squaredNorm();
187  for (int s = 0; s < r-1; ++s)
188  S2 += 2.0 * std::real(U.col(s).dot(U.col(r-1)) * V.col(s).dot(V.col(r-1)));
189 
190  // check error
191  double err = U.col(r-1).norm() * V.col(r-1).norm() / std::sqrt(S2);
192  if (err < eps)
193  break;
194 
195  // go to next row
196  ++i;
197  }
198 
199  return r;
200  }
201 
216  template <class Matrix, class RowArray, class ColumnArray, class BlockArray, class Input, class Output, class Ranks>
217  void multiply(Matrix const &M,
218  Eigen::DenseBase<RowArray> const &rowClusters, Eigen::DenseBase<ColumnArray> const &colClusters,
219  Eigen::DenseBase<BlockArray> const &blocks,
220  Input const &input, Output &output,
221  double eps, int maxRank,
222  Ranks &outRanks)
223  {
224  typedef typename Matrix::Scalar Scalar;
225 
226  // determine maximal row and column block size for preallocation
227  int nMaxRows = rowClusters.col(1).maxCoeff(), nMaxCols = colClusters.col(1).maxCoeff();
228 
229  // allocate memory for U, V and internal result vector of LRA
230  Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> U(nMaxRows, maxRank), V(nMaxCols, maxRank);
231  Eigen::Matrix<Scalar, Eigen::Dynamic, 1> Z(maxRank,1);
232 
233  // track number of matrix element evaluations
234  m_nEval = m_matrixSize = 0;
235 
236  // traverse blocks
237  for (int iBlock = 0; iBlock < blocks.rows(); ++iBlock)
238  {
239  auto rowCluster(rowClusters.row(blocks(iBlock,0))); // reference object
240  auto colCluster(colClusters.row(blocks(iBlock,1))); // reference object
241 
242  // get cluster size
243  int row0 = rowCluster(0), nRows = rowCluster(1);
244  int col0 = colCluster(0), nCols = colCluster(1);
245 
246  auto u(U.topRows(nRows)); // reference objects
247  auto v(V.topRows(nCols));
248 
249  // compute low rank decomposition
250  int r = low_rank_approx(createBlock(M, row0, col0), nRows, nCols, eps, maxRank, u, v);
251 
252  m_nEval += (nRows+nCols)*r;
253  m_matrixSize += nRows*nCols;
254 
255  // perform multiplication
256  auto vv(V.leftCols(r)); // reference objects
257  auto uu(U.leftCols(r));
258  auto z(Z.topRows(r));
259 
260  // z = V' * y
261  z.setZero();
262 
263 
264  for (int j = 0; j < nCols; ++j)
265  z += vv.row(j).transpose() * input( col0+j , 0);
266  // x += U * z
267  for (int i = 0; i < nRows; ++i)
268  output( row0+i, 0 ) += (uu.row(i)*z)(0,0);
269 
270  outRanks(iBlock,0) = r;
271  }
272  }
273 
284  template <class Matrix, class RowArray, class ColumnArray, class BlockArray>
285  static std::vector<LowRank<typename Matrix::Scalar> > decompose(Matrix const &M,
286  Eigen::DenseBase<RowArray> const &rowClusters, Eigen::DenseBase<ColumnArray> const &colClusters,
287  Eigen::DenseBase<BlockArray> const &blocks,
288  double eps, int maxRank)
289  {
290  typedef typename Matrix::Scalar Scalar;
291  typedef std::vector<LowRank<Scalar> > ReturnType;
292 
293  ReturnType ret;
294  ret.reserve(blocks.rows());
295 
296  // determine maximal row and column block size for preallocation
297  int nMaxRows = rowClusters.col(1).maxCoeff(), nMaxCols = colClusters.col(1).maxCoeff();
298 
299  // allocate memory for U, V and internal result vector of LRA
300  Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> U(nMaxRows, maxRank), V(nMaxCols, maxRank);
301  Eigen::Matrix<Scalar, Eigen::Dynamic, 1> Z(maxRank,1);
302 
303  // traverse blocks
304  for (int iBlock = 0; iBlock < blocks.rows(); ++iBlock)
305  {
306  auto rowCluster(rowClusters.row(blocks(iBlock,0))); // reference object
307  auto colCluster(colClusters.row(blocks(iBlock,1))); // reference object
308 
309  // get cluster size
310  int row0 = rowCluster(0), nRows = rowCluster(1);
311  int col0 = colCluster(0), nCols = colCluster(1);
312 
313  auto u(U.topRows(nRows)); // reference objects
314  auto v(V.topRows(nCols));
315 
316  // compute low rank decomposition
317  int r = low_rank_approx(createBlock(M, row0, col0), nRows, nCols, eps, maxRank, u, v);
318 
319  ret.push_back(LowRank<Scalar>(row0, col0, U.leftCols(r), V.leftCols(r)));
320  }
321 
322  return ret;
323  }
324 
325 public:
327  int get_nEval(void) const
328  {
329  return m_nEval;
330  }
332  int get_matrixSize(void) const
333  {
334  return m_matrixSize;
335  }
337  double get_sizeCompression(void) const
338  {
339  return double(get_nEval()) / get_matrixSize();
340  }
341 
342 private:
343  int m_nEval;
344  int m_matrixSize;
345 };
346 
347 #endif // ACA_HPP_INCLUDED
348 
ACA
class performing Adaptive Cross Approximation
Definition: aca.hpp:71
LowRank::getRow0
int getRow0(void) const
return row offset
Definition: aca.hpp:52
LowRank::getCol0
int getCol0(void) const
return column offset
Definition: aca.hpp:58
LowRank::getV
const Eigen::Matrix< Scalar, Eigen::Dynamic, Eigen::Dynamic > & getV(void) const
return the rhs (V) member of the LRA
Definition: aca.hpp:46
LowRank::LowRank
LowRank(void)
default constructor to be able to use with std::vector container
Definition: aca.hpp:22
LowRank::LowRank
LowRank(int row0, int col0, Eigen::MatrixBase< UType > const &U, Eigen::DenseBase< VType > const &V)
constructor setting members
Definition: aca.hpp:34
ACA::multiply
void multiply(Matrix const &M, Eigen::DenseBase< RowArray > const &rowClusters, Eigen::DenseBase< ColumnArray > const &colClusters, Eigen::DenseBase< BlockArray > const &blocks, Input const &input, Output &output, double eps, int maxRank, Ranks &outRanks)
Compute matrix-vector product with multilevel low rank approximation.
Definition: aca.hpp:217
ACA::get_sizeCompression
double get_sizeCompression(void) const
return the size compression ratio
Definition: aca.hpp:337
LowRank::getU
const Eigen::Matrix< Scalar, Eigen::Dynamic, Eigen::Dynamic > & getU(void) const
return the lhs (U) member of the LRA
Definition: aca.hpp:40
LowRank
Class capable of storing a Low Rank Approximation of a matrix block.
Definition: aca.hpp:18
ACA::decompose
static std::vector< LowRank< typename Matrix::Scalar > > decompose(Matrix const &M, Eigen::DenseBase< RowArray > const &rowClusters, Eigen::DenseBase< ColumnArray > const &colClusters, Eigen::DenseBase< BlockArray > const &blocks, double eps, int maxRank)
decompose a matrix into ACA low rank decomposition
Definition: aca.hpp:285
ACA::get_matrixSize
int get_matrixSize(void) const
number of matrix elements in processed blocks
Definition: aca.hpp:332
ACA::get_nEval
int get_nEval(void) const
return number of matrix evaluations
Definition: aca.hpp:327
ACA::low_rank_approx
static int low_rank_approx(Matrix const &M, int nRows, int nCols, double eps, int R, Eigen::DenseBase< Result > &U, Eigen::DenseBase< Result > &V)
compute low rank approximation of a matrix with a prescribed accuracy and maximum rank
Definition: aca.hpp:135