Boost.uBLAS で正値対称係数行列の線形方程式系(System of linear equations)の解の計算

今回は AX = B の解行列 X あるいは Ax = b の解ベクトル x を計算する関数を実装します.ただし,A は正値対称行列であるとします.
lapack には dposv という関数があり,これが内部で係数行列をコレスキー分解して解を計算してくれます.つまり,前回と同様 bindings.lapack.posv に渡す column_major な行列を用意するだけでほとんどの作業は終わりです.以下に私の実装例を示します.

math.hpp

#ifndef MATH_HPP_20081107
#define MATH_HPP_20081107

#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif


#include <boost/numeric/ublas/fwd.hpp>


namespace math {
    void solve(const boost::numeric::ublas::symmetric_matrix<double>& A, const boost::numeric::ublas::vector<double>& b, boost::numeric::ublas::vector<double>& x);
    void solve(const boost::numeric::ublas::symmetric_matrix<double>& A, const boost::numeric::ublas::matrix<double>& B, boost::numeric::ublas::matrix<double>& X);
}


#endif

math.cpp

#include "math.hpp"
#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/matrix_proxy.hpp>
#include <boost/numeric/ublas/symmetric.hpp>
#include <boost/numeric/ublas/vector.hpp>
#define BOOST_NUMERIC_BINDINGS_USE_CLAPACK
#include <boost/numeric/bindings/lapack/posv.hpp>
#include <boost/numeric/bindings/traits/ublas_matrix.hpp>
#undef  BOOST_NUMERIC_BINDINGS_USE_CLAPACK


void math::solve(const boost::numeric::ublas::symmetric_matrix<double>& A, const boost::numeric::ublas::vector<double>& b, boost::numeric::ublas::vector<double>& x)
{
    BOOST_UBLAS_CHECK(A.size1() == b.size(), boost::numeric::ublas::external_logic());

    boost::numeric::ublas::matrix<double> B(b.size(), 1), X;

    boost::numeric::ublas::column(B, 0).assign(b);
    solve(A, B, X);

    x = boost::numeric::ublas::column(X, 0);
}


void math::solve(const boost::numeric::ublas::symmetric_matrix<double>& A, const boost::numeric::ublas::matrix<double>& B, boost::numeric::ublas::matrix<double>& X)
{
    namespace ublas = boost::numeric::ublas;

    BOOST_UBLAS_CHECK(A.size1() == B.size1(), ublas::external_logic());

    ublas::matrix<double, ublas::column_major> CA(A.size1(), A.size2()), CX(B);
    int                                        info;

    for (std::size_t i = 0; i < A.size1(); ++i) {
        for (std::size_t j = 0; j <= i; ++j) {
            CA(i, j) = A(i, j);
        }
    }

    info = boost::numeric::bindings::lapack::posv('L', CA, CX);
    BOOST_UBLAS_CHECK(info == 0, ublas::internal_logic());

#if BOOST_UBLAS_TYPE_CHECK
    BOOST_UBLAS_CHECK(ublas::detail::expression_type_check(ublas::prod(A, CX), B), ublas::internal_logic());
#endif

    X = CX;
}

math.cpp に実正値対称係数行列の線形方程式系の解を計算する関数 solve を実装しました.デバッグ時には実際に計算した X を用いて,AX = B になることを確認するコードを追加しています.math.cpp をプロジェクトに加え(他のソースコードと一緒にコンパイル),math.hpp を include することにより solve が使用可能になります.
solve は AX = B の解行列 X あるいは Ax = b の解ベクトル x を計算します.ただし,A は N 次正値対称行列,X と B は N x NRHS 行列(NRHS は 1 以上の任意の数),x と b は N 次元ベクトルを表します.
NRHS が 1 のとき,X と B は N x 1 の行列になりますが,これは N 次元ベクトルに他なりません(NRHS が 1 のときは,AX = B という式はベクトルを用いて Ax = b になるのです).uBLAS ではベクトルを表す型もちゃんと用意されているため,引数にベクトルを取る関数(Ax = b を解く関数)も実装しました.コードを見ると分かりますが,この関数は AX = B を解く関数を利用しているだけです.


以下のテストコードで solve の動作を確認できます.

test.cpp

#include "math.hpp"
#include <boost/numeric/ublas/io.hpp>
#include <boost/numeric/ublas/symmetric.hpp>
#include <boost/numeric/ublas/vector.hpp>


#ifdef _MSC_VER
# pragma comment(lib, "libf2c.lib")
# pragma comment(lib, "BLAS.lib")
# pragma comment(lib, "clapack.lib")
#endif


int main()
{
    namespace ublas = boost::numeric::ublas;

    ublas::symmetric_matrix<double> A(4, 4);
    ublas::vector<double>           b(4), x;

    A(0, 0) = 4.16;
    A(1, 0) = -3.12; A(1, 1) = 5.03;
    A(2, 0) = 0.56;  A(2, 1) = -0.83; A(2, 2) = 0.76;
    A(3, 0) = -0.1;  A(3, 1) = 1.18;  A(3, 2) = 0.34; A(3, 3) = 1.18;

    b(0) = 8.7; b(1) = -13.35; b(2) = 1.89; b(3) = -4.14;

    math::solve(A, b, x);

    // 以下結果の表示
    // 計算結果を http://www.nag.co.uk/lapack-ex/node11.html と比較してみましょう
    std::cout << x << std::endl;

    return 0;
}
  • VC9
  • g++ (GCC) 3.4.4 (cygming special)

でのコンパイルを確認しています.
実行してみると,正しく解を計算できていることが分かります.
エラーが出てコンパイルできない場合は,前回の記事を見て正しくインストールできているか確認してみてください.


次回は最小二乗法を実装する予定です.
それでは.