Gmm++ の深い内部

linalg_traits 構造体

Gmm++ の主な原則は,それぞれのベクトルと行列の型(これはインスタンス化されません)が linalg_traits という名前の対応する構造体を持っているということです.この構造体にはすべての情報が含まれています.例えば,この構造体の要素 linalg_type は,対応する型がベクトルまたは行列を表す場合には, abstract_vector または abstract_matrix に設定されます. V がベクトルのインターフェイス型で, M が行列のインターフェイス型である場合,次のようにしてこのコンポーネントにアクセスできます.

typename gmm::linalg_traits<V>::linalg_type ...  // should be abstract_vector
typename gmm::linalg_traits<M>::linalg_type ...  // should be abstract_matrix

abstract_vectorabstract_matrixgmm/gmm_def.h で定義されています .これらは汎用アルゴリズムを特殊化できるvoid型です.

ベクトル型では,次の情報を使用できます.

typename gmm::linalg_traits<V>::value_type     --> type of the components of the
                                                   vector
typename gmm::linalg_traits<V>::reference      --> type of reference on a component
typename gmm::linalg_traits<V>::is_reference   --> if the vector is a simple
                                                   reference or an instantiated vector
typename gmm::linalg_traits<V>::linalg_type    --> should be abstract_vector
typename gmm::linalg_traits<V>::index_sorted    --> linalg_true or linalg_false
typename gmm::linalg_traits<V>::const_iterator --> const iterator to iterate on the
                                                   components of the vector in
                                                   order to read them.
typename gmm::linalg_traits<V>::iterator       --> iterator to iterate on the
                                                   components of the vector in
                                                   order to read or write them.
typename gmm::linalg_traits<V>::storage_type   --> should be abstract_sparse,
                                                   abstract_skyline or
                                                   abstract_dense

typename gmm::linalg_traits<V>::origin_type    --> the type of vector itself
                                                   or the type of referenced
                                                   vector for a reference.

gmm::linalg_traits<V>::size(v)     --> a method which gives the size of the vector.
gmm::linalg_traits<V>::begin(v)    --> a method which gives an iterator on the
                                       beginning of the vector
gmm::linalg_traits<V>::end(v)      --> iterator on the end of the vector
gmm::linalg_traits<V>::origin(v)   --> gives a void pointer allowing to identify
                                       the vector
gmm::linalg_traits<V>::do_clear(v) --> make a clear on the vector

gmm::linalg_traits<V>::access(o, it, ite, i) --> return the ith component or a
                                        reference on the ith component. o is a
                                        pointer o type ``origin_type *'' or
                                        ``const origin_type *''.

gmm::linalg_traits<V>::clear(o, it, ite) --> clear the vector. o is a
                                        pointer o type ``origin_type *'' or
                                        ``const origin_type *''.

行列型の場合

typename gmm::linalg_traits<M>::value_type     --> type of the components of the
                                                   matrix
typename gmm::linalg_traits<M>::reference      --> type of reference on a component
typename gmm::linalg_traits<M>::is_reference   --> if the matrix is a simple
                                                   reference or an instantiated matrix
typename gmm::linalg_traits<M>::linalg_type    --> should be abstract_matrix
typename gmm::linalg_traits<M>::storage_type   --> should be abstract_sparse,
                                                   abstract_skyline or
                                                   abstract_dense
typename gmm::linalg_traits<M>::index_sorted    --> linalg_true or linalg_false
typename gmm::linalg_traits<M>::sub_orientation --> should be row_major, col_major
                                                    row_and_col or col_and_row.
typename gmm::linalg_traits<M>::sub_col_type      --> type of reference on a column
                                                    (if the matrix is not row_major)
typename gmm::linalg_traits<M>::const_sub_col_type --> type of const reference on a
                                                     column
typename gmm::linalg_traits<M>::col_iterator      --> iterator on the columns
typename gmm::linalg_traits<M>::const_col_iterator --> const iterator on the columns
typename gmm::linalg_traits<M>::sub_row_type      --> type of reference on a row
                                                    (if the matrix is not col_major)
typename gmm::linalg_traits<M>::const_sub_row_type --> type of const reference on a
                                                     row
typename gmm::linalg_traits<M>::const_row_iterator --> const iterator on the rows
typename gmm::linalg_traits<M>::row_iterator       --> iterator on the rows

typename gmm::linalg_traits<M>::origin_type    --> the type of vector itself
                                                   or the type of referenced
                                                   vector for a reference.

gmm::linalg_traits<M>::nrows(m)     --> methods which gives the number of rows of
                                        the matrix
gmm::linalg_traits<M>::ncols(m)     --> number of columns
gmm::linalg_traits<M>::row_begin(m) --> iterator on the first row (if not col_major)
gmm::linalg_traits<M>::row_end(m)   --> iterator on the end of the rows
gmm::linalg_traits<M>::col_begin(m) --> iterator on the first column
                                        (if not row_major)
gmm::linalg_traits<M>::col_end(m)   --> iterator on the end of the columns
gmm::linalg_traits<M>::row(it)      --> gives the reference on a row with an iterator
                                        (if not col_major)
gmm::linalg_traits<M>::col(it)      --> gives the reference on a column with an
                                        iterator  (if not row_major)
gmm::linalg_traits<M>::origin(m)    --> gives a void pointer allowing to identify
                                        the matrix
gmm::linalg_traits<M>::access(it,i) --> return the ith component or a reference
                                        on the ith component of the row or
                                        column pointed by it.
gmm::linalg_traits<M>::do_clear(m)  --> make a clear on the matrix

これは,新しいベクトルまたは行列型をインタフェースするために入力する必要がある構造です.例は gmm/gmm_interface.h で見ることができます.ほとんどの汎用的なアルゴリズムは gmm/gmm_blas.h にあります.

ベクトルのコンポーネントをイテレートする方法

ベクトルの成分を累積する例を次に示します. V はベクトル型であり, v はインスタンス化されたベクトルであると仮定します.

typename gmm::linalg_traits<V>::value_type r(0); // scalar in which we accumulate
typename gmm::linalg_traits<V>::const_iterator it = vect_const_begin(v); // beginning of v
typename gmm::linalg_traits<V>::const_iterator ite = vect_const_end(v); // end of v

for (; it != ite; ++it)  // loop on the components
  r += *it;              // accumulate the components

このコードは,あらゆる種類のインターフェイスされたベクトルで動作します.

疎なベクトルやスカイラインなベクトルでは,イテレータが指すコンポーネントのインデックスを it.index() で取得することができます. V1V2 が2つのベクトル型であり, v1v2 が対応する2つのインスタンス化されたベクトルであると仮定した場合の,2つの疎ベクトルまたはスカイラインベクトルのスカラー積の例を次に示します.

typename gmm::linalg_traits<V1>::const_iterator it1 = vect_const_begin(v1),
typename gmm::linalg_traits<V1>::const_iterator ite1 = vect_const_end(v1);
typename gmm::linalg_traits<V2>::const_iterator it2 = vect_const_begin(v2),
typename gmm::linalg_traits<V2>::const_iterator ite2 = vect_const_end(v2);
typename gmm::linalg_traits<V1>::value_type r(0); // it is assumed that V2 have a
                                             // compatible value_type

while (it1 != ite1 && it2 != ite2) {  // loops on the components
  if (it1.index() == it2.index()) {
    res += (*it1) * (*it2));          // if the indices are equals accumulate
    ++it1;
    ++it2;
  }
  else if (it1.index() < it2.index())
    ++it1;
  else
    ++it2;
}

このアルゴリズムでは,インデックスが疎ベクトルで増えているという事実を使用します.密なベクトルイテレータには it.index() というメソッドがないので,このコードは密なベクトルに対しては動作しません.

行列をイテレートする方法

行列が列行列でない場合は行を反復し,行行列でない場合は行列の列を反復します( gmm::dense_matrix<T> の型は col_and_roxのような副方向型なので,行と列の両方で反復することができます).

最適化する必要がない場合は,次のような基本的なループを使用できます.

for (size_t i = 0; i < gmm::mat_nrows(m); ++i) {
  typename gmm::linalg_traits<M>::const_sub_row_type row = mat_const_row(M, i);

  ...

  std::cout << "norm of row " << i << " : " << vect_norm2(row) << std::endl;
}

しかし,次のようにイテレータを使用することもできます.

typename gmm::linalg_traits<M>::const_row_iterator it = mat_row_const_begin(m);
typename gmm::linalg_traits<M>::const_row_iterator ite = mat_row_const_end(m);

for (; it != ite; ++it) {
  typename gmm::linalg_traits<M>::const_sub_row_type
    row = gmm::linalg_traits<M>::row(it);

  ...

  std::cout << "norm of row " << i << " : " << vect_norm2(row) << std::endl;
}

アルゴリズムをすべての型の行列で動作させる方法

このためには,一般的に特化する必要があります.例えば, gmm::nnz のコードを見てみましょう.これは保存されているコンポーネント(実際には,実数の gmm::nnz アルゴリズムはほとんどの場合特化されており,コンポーネントを1つずつ数えたりはしません)の数を数えます.

template <class L> inline size_type nnz(const L& l) {
  return nnz(l, typename linalg_traits<L>::linalg_type());
}

template <class L> inline size_type nnz(const L& l, abstract_vector) {
  typename linalg_traits<L>::const_iterator it = vect_const_begin(l);
  typename linalg_traits<L>::const_iterator ite = vect_const_end(l);
  size_type res(0);
  for (; it != ite; ++it) ++res;
  return res;
}

template <class L> inline size_type nnz(const L& l, abstract_matrix) {
  return nnz(l,  typename principal_orientation_type<typename
                 linalg_traits<L>::sub_orientation>::potype());
}

template <class L> inline size_type nnz(const L& l, row_major) {
  size_type res(0);
  for (size_type i = 0; i < mat_nrows(l); ++i)
    res += nnz(mat_const_row(l, i));
  return res;
}

template <class L> inline size_type nnz(const L& l, col_major) {
  size_type res(0);
  for (size_type i = 0; i < mat_ncols(l); ++i)
    res += nnz(mat_const_col(l, i));
  return res;
}

パラメータがベクトルまたは行列の場合,最初の関数は2番目または3番目の関数にそれぞれディスパッチされます.行列がrow_majorまたはcolumn majorの場合,3番目の関数はそれぞれ4番目と5番目の関数に再ディスパッチされます.もちろん,関数は inline で宣言されているので,少なくとも2つのディスパッチャ関数は実装されません.つまり,この構築はコストがかからないということです.