ublasのbanded_matrixとlu_factorizeで、帯行列の逆行列とベクトルの積を線形時間で

帯行列の逆行列は一般には密行列なので、帯行列の逆行列の計算は\mathrm{O}(n^2)。しかし、帯行列のLU分解は\mathrm{O}(n)で出来るので、帯行列Aとベクトルvに対してA^{ー1} v\mathrm{O}(n)で計算できる。これを実装。

/*! g++ main.cpp -g -DNDEBUG
 */

#include <boost/numeric/ublas/banded.hpp>
#include <boost/numeric/ublas/io.hpp>
#include <boost/numeric/ublas/operation.hpp>
#include <boost/numeric/ublas/lu.hpp>
#include <boost/numeric/ublas/matrix_proxy.hpp>

#include <boost/timer.hpp>

#include <vector>
#include <iterator>

using namespace boost::numeric;
using namespace std;


void makeBanded(ublas::banded_matrix<double>& a,
		const int n, const int w){
  a.resize(n, n, w, w);
  a.clear();
  for(int i = 0; i < n; ++i){
    for(int j = max(0, i - w); j < i; ++j){
      a(i, j) = 1;
    }
    for(int j = i + 1; j < min(n, i + w + 1); ++j){
      a(i, j) = 2;
    }
    a(i, i) = w * 5;
  }
}

template<class V>
void makeVec(ublas::vector_expression<V>& v, size_t i){
  //copy(v().begin(), v().end(), 0); //compile error : the cause is unclear.
  for(size_t j = 0; j < v().size(); j++){
    v()(j) = 0;
  }
  v()(i) = 1;
}

template<class V>
void invertProd(ublas::banded_matrix<double>& a,
		ublas::vector_expression<V>& v){
  ublas::permutation_matrix<> pm(a.size1());
  ublas::lu_factorize(a, pm);
  ublas::lu_substitute(a, pm, v());
}




int main(int argc, char* argv[]){
  if(argc != 3){
    cout << "usage : a.out dim iter" << endl;
    return 1;
  }

  const size_t dim = atoi(argv[1]);
  const size_t iter = atoi(argv[2]);
  const size_t wid = 1;

  boost::timer timer;

  ublas::banded_matrix<double> a;
  makeBanded(a, dim, wid);

  ublas::vector<double> v(a.size1());
  makeVec(v, 0);

  ublas::banded_matrix<double> b(a);
  ublas::vector<double> ret(v);

  timer.restart();
  invertProd(b, ret);
  cout << timer.elapsed() << endl;

#ifndef NDEBUG
  ublas::matrix<double> aInv(a.size1(), a.size2());
  for(size_t i = 0; i < aInv.size1(); ++i){
    ublas::banded_matrix<double> b(a);
    ublas::matrix_column<ublas::matrix<double> > aInvCol(aInv, i);
    makeVec(aInvCol, i);
    invertProd(b, aInvCol);
  }

  cout << a << endl
       << prod(a, aInv) << endl;
#endif

  return 0;
}

#ifndef NDEBUGの中は、invertProdが正しく逆行列との積を計算していることの確認。A^{-1} (0, \ldots, 0, 1, 0, \ldots, 0)'の結果をaInvに入れるている。これで、A^{-1}の第i列を取り出して、一つの行列にまとめた上でAとの積を計算して、単位行列になることを確認。
関数の引数をvector_expression型にすると、vectorそのものでも、matrix_columnのようなものでも受け取れるということは分かったんだけど、イテレータの部分がコンパイルエラーになったりして、イマイチ使い方が良く判らん。こういうのは、どこで勉強すれば良いのか。