その過程でどうしてもベクトル・行列演算が必要になったので、時期標準の呼び声高いBoost内のベクトル・行列ライブラリ、uBLASを用いてみました。
今回の記事は、特に僕が必要であった演算を実現すべく、参考にしたサイト及びそれらをまとめたテンプレート関数群をご紹介します。
参考にした主なサイトは以下のとおりです。
2で概要を掴み、1で詳細を詰め、3で必要なものを実装... がオススメです。
特に3の矢野さんの日記には大変助けられました。ほとんど全ての実装がblogを元にしているといっても過言ではありません(この場を借りて御礼申し上げます)。
先人達の知恵を集結し、まとめた関数群がこちら。
/*
matrix.hpp is template functions using uBLAS
References:
boost Basic Linear Algebra http://www.boost.org/doc/libs/1_39_0/libs/numeric/ublas/doc/index.htm
yanoの日記 http://d.hatena.ne.jp/blono/
cholesky.hpp http://www.guwi17.de/ublas/examples/cholesky.hpp
ver.0.1
13/10/09
Takaaki
*/
#ifndef _MATRIX_
#define _MATRIX_
#include <boost/numeric/ublas/lu.hpp>
#include <boost/numeric/ublas/vector.hpp>
#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/matrix_proxy.hpp>
#include <boost/numeric/ublas/io.hpp>
#include <boost/numeric/ublas/triangular.hpp>
#include <algorithm>
using namespace boost::numeric::ublas;
namespace math{
namespace ublas = boost::numeric::ublas;
/*mean*/
template <class V> double mean(const V& v);
/*variance*/
template <class V> double var(const V& v);
/*standard deviation*/
template <class V> double sd(const V& v);
/*Invert*/
template <class M, class MI> void invert(const M& m, MI& mi);
/*Trace*/
template <class M> double trace(const M& m);
/*Determinant*/
template <class M> double determinant(const M& m);
template <class M> double det(const M& m);
/*Cholesky decomposition*/
template <class M> size_t cholesky(const M& A, M& L);
}
template <class V>
double math::mean(const V& v)
{
return ublas::sum(v) / v.size();
}
template <class V>
double math::var(const V& v)
{
/*Create mean vector*/
ublas::vector<double> mv(v.size());
std::fill(mv.begin(), mv.end(), math::mean(v));
return sum(element_prod(v - mv, v-mv)) / (v.size() - 1.0);
}
template <class V>
double math::sd(const V& v)
{
return std::sqrt(var(v));
}
template <class M, class MI>
void math::invert(const M& m, MI& mi)
{
BOOST_UBLAS_CHECK(m.size1() == m.size2(), ublas::external_logic());
ublas::matrix<double> lhs(m);
ublas::matrix<double> rhs(ublas::identity_matrix<double>(m.size1()));
ublas::permutation_matrix<> pm(m.size1());
ublas::lu_factorize(lhs, pm);
ublas::lu_substitute(lhs, pm, rhs);
mi.resize(rhs.size1(), rhs.size2(), false);
mi.assign(rhs);
}
template <class M>
double math::trace(const M& m)
{
BOOST_UBLAS_CHECK(m.size1() == m.size2(), ublas::external_logic());
M cm(m);
ublas::matrix_vector_range<M> diag(cm, ublas::range(0,m.size1()), ublas::range(0,m.size2()));
return ublas::sum(diag);
}
template <class M>
double math::determinant(const M& m)
{
BOOST_UBLAS_CHECK(m.size1() == m.size2(), ublas::external_logic());
ublas::matrix<double> lu(m);
ublas::permutation_matrix<> pm(m.size1());
ublas::lu_factorize(lu,pm);
double det(1);
typedef ublas::permutation_matrix<>::size_type size_type;
for (size_type i = 0; i < pm.size(); ++i) {
det *= (i == pm(i)) ? +lu(i, i) : -lu(i, i);
}
return det;
}
template <class M>
double math::det(const M& m)
{
return math::determinant(m);
}
template <class M>
size_t math::cholesky(const M& A, M& L)
{
typedef typename M::value_type T;
assert( A.size1() == A.size2() );
assert( A.size1() == L.size1() );
assert( A.size2() == L.size2() );
const size_t n = A.size1();
for (size_t k=0 ; k < n; k++) {
double qL_kk = A(k,k) - inner_prod( project( row(L, k), range(0, k) ),project( row(L, k), range(0, k) ) );
if (qL_kk <= 0) {
return 1 + k;
} else {
double L_kk = sqrt( qL_kk );
L(k,k) = L_kk;
matrix_column<M> cLk(L, k);
project( cLk, range(k+1, n) )
= ( project( column(A, k), range(k+1, n) )
- prod( project(L, range(k+1, n), range(0, k)),
project(row(L, k), range(0, k) ) ) ) / L_kk;
}
}
return 0;
}
#endif
これをmath.hppとして保存し、以下のmain.cppで動作を確認してみます。
#include <cstdlib>
#include "math.hpp"
/*function of random variables*/
double runif();
double rnorm(double, double);
const static double PI = 3.14159265358979323846;
/*main*/
int main(void)
{
srand((unsigned int)time(NULL));
int dim = 3;
double mean = 0.0;
double sd = 1.0;
/*create vector & matrix*/
vector<double> v(dim);
matrix<double> m(dim,dim); /*synmetric matrix*/
matrix<double> im(dim,dim); /*inverse of m*/
matrix<double> L(dim,dim); /*triangular matrix*/
/*fill the vector & matrix as diagnal matrix*/
for(int i=0;i<dim;++i){
v(i) = static_cast<double>(i + 3);
for(int j=0;j<dim;++j){
if(j >= i){
i == j ? m(i,j) = 1.0 : m(i,j) = rnorm(mean,sd);
}else{
m(i,j) = m(j,i);
}
}
}
/*print*/
std::cout<<"v : "<<v<<"\n";
std::cout<<"m : "<<m<<"\n";
/*mean, variance, standard deviation*/
std::cout<<"mean(v) : "<<math::mean(v)<<"\n";
std::cout<<"var(v) : "<<math::var(v)<<"\n";
std::cout<<"sd(v) : "<<math::sd(v)<<"\n";
/*inverse, trace, determinant, cholesky */
math::invert(m,im);
std::cout<<"m * m^-1 : "<<prod(m,im)<<"\n"; /*check the inverse*/
std::cout<<"trace(m) : "<<math::trace(m)<<"\n";
std::cout<<"determinant(m) : "<<math::determinant(m)<<"\n";
math::cholesky(m,L);
std::cout<<"cholesky decompsition of m\n";
std::cout<<"L : "<<L<<"\n";
std::cout<<"L' : "<<trans(L)<<"\n";
std::cout<<"L * L' : "<<prod(L,trans(L))<<"\n";
return 0;
}
double runif()
{
return static_cast<double>(rand()) / RAND_MAX;
}
double rnorm(double mean, double sd)
{
double U=0.0;
double V=0.0;
while(true){
U = runif();
V = runif();
if(U>0.000001 && V > 0.000001){
break;
}
}
double R = sqrt(-2.0*log(U));
double T = 2.0*PI*V;
if(U>0.5){
return sd * R * cos(T) + mean;
}else{
return sd * R * sin(T) + mean;
}
}
確認環境はg++(gcc) 4.1.2、boost 1_39_0です。
$g++ main.cpp $./a.out
の出力結果がこちら。
v : [3](3,4,5) m : [3,3]((1,0.30186,0.0902471),(0.30186,1,0.0612414),(0.0902471,0.0612414,1)) mean(v) : 4 var(v) : 1 sd(v) : 1 m * m^-1 : [3,3]((1,-8.23994e-18,0),(-3.72966e-17,1,-1.38778e-17),(0,6.93889e-18,1)) trace(m) : 3 determinant(m) : 0.900322 cholesky decompsition of m L : [3,3]((1,0,0),(0.30186,0.953352,0),(0.0902471,0.035663,0.995281)) L' : [3,3]((1,0.30186,0.0902471),(0,0.953352,0.035663),(0,0,0.995281)) L * L' : [3,3]((1,0.30186,0.0902471),(0.30186,1,0.0612414),(0.0902471,0.0612414,1))
Awesome!



0 件のコメント:
コメントを投稿