Affine Arithmetic


本ページでは、Affine Arithmeticを行うライブラリの使い方を説明する。

Affine Arithmeticとは

概要は、 Affine Arithmeticについて を見て下さい。

ライブラリ構成ファイル

naffine.hpp 普通のaffine arithmeticを行う。 上の解説の方法1に対応
saffine.hpp 丸め誤差専用のダミー変数を用いて簡略化したaffine arithmeticを行う。 上の解説の方法2に対応
ssaffine.hpp 更に簡略化したaffine arithmeticを行う。 上の解説の方法3に対応
iaffine.hpp 普通のaffine arithmeticを行うが、0係数をデータとして持たない工夫あり。 上の解説の方法1に対応
affine.hpp 上の4つの方式を切り替えるためのファイル -

どの実装が有効かいろいろ試せるように、複数の実装を行った。 naffine.hpp,saffine.hpp,ssaffine.hpp, iaffine.hppの4つは全て同じaffineという名前の クラスを定義しており、互いに排他的である(同時にincludeすることは出来ない)。

affine.hppの中身は、
#ifndef AFFINE_INCLUDED

#if defined(USE_NAFFINE)
  #include "naffine.hpp"
  #define AFFINE_INCLUDED 1
#elif defined(USE_IAFFINE)
  #include "iaffine.hpp"
  #define AFFINE_INCLUDED 1
#elif defined(USE_SAFFINE)
  #include "saffine.hpp"
  #define AFFINE_INCLUDED 1
#elif defined(USE_SSAFFINE)
  #include "ssaffine.hpp"
  #define AFFINE_INCLUDED 1
#endif

#ifndef AFFINE_INCLUDED
#include "naffine.hpp"
#define AFFINE_INCLUDED 1
#endif

#endif
となっており、affine arithmeticを行うプログラムは単に affine.hppをincludeして書いておき、 -DUSE_NAFFINE, -DUSE_SAFFINE, -DUSE_SSAFINE, -DUSE_IAFFINEとコンパイル時にマクロを定義することによってそれぞれ naffine.hpp,saffine.hpp,ssaffine.hpp, iaffine.hppをincludeする。 デフォルトではnaffine.hppとした。

内部で使っている区間演算及び丸めの変更に boost.interval、 係数ベクトルの保持にboost.ublasを利用している。 従って、boostがきちんと動く環境が必要である。

使い方

boost.intervalのpolicy

boost.intervalは、テンプレート引数として、 内部の型 T (double等) だけでなく、 丸めの方法やエラーの取扱い方法を policyとして指定し動作を カスタマイズすることが出来るように設計されている。

policyの指定はやや煩雑なので、intervalでは省略することが出来るように なっている。例えば

と同じである。

affine型の使い方

affine型では、intervalと同様に、内部の型とpolicyの2つをテンプレート引数 として取る。今のところpolicyの省略は出来ない。 boost.intervalのpolicyに従って丸めの変更を行い、内部で区間演算を 行う場合はそのpolicyを使った区間演算を行う実装になっている。

affine演算は区間演算と同時に使うことが多いだろうし、その区間演算と 同じpolicyを指定することが多いだろうから、次のようにすることをお勧めする。 区間型のpolicyは、「区間型::traits_type」で取り出すことが出来るので、

typedef boost::numeric::interval<double> itvd;
typedef affine<double, itvd::traits_type> afd;
のように定義してしまい、区間型をitvd、affine型をafdで プログラムを書けば楽であろう。

簡単なプログラム

test-affine.cc

#include <iostream> 
#include "affine.hpp"

namespace bn = boost::numeric;

typedef bn::interval<double> itvd;
typedef affine<double, itvd::traits_type> afd;

int main()
{
    afd a, b;
    itvd x, y;

    // affine arithmetic

    a = itvd(-0.1, 0.1);
    b = square(a + 1.) - 2. * a;

    std::cout << b << "\n";
    std::cout << to_interval(b) << "\n";

    // interval arithmetic

    x = itvd(-0.1, 0.1);
    y = square(x + 1.) - 2. * x;

    std::cout << y << "\n";
}

実行結果

$ c++ test-affine.cc
$ ./a.out 
[(1.005)+(-0)e1+(-0)e2+(0)e3+(0.005)e4+(0)e5]
[1,1.01]
[0.61,1.41]
$ c++ test-affine.cc -DUSE_SAFFINE
$ ./a.out 
[(1.005)+(-0)e1+(0.005)e2+(0)er]
[1,1.01]
[0.61,1.41]
$ c++ test-affine.cc -DUSE_SSAFFINE
$ ./a.out 
[(1.005)+(-0)e1+(0.005)er]
[1,1.01]
[0.61,1.41]
$ c++ test-affine.cc -DUSE_IAFFINE
$ ./a.out 
[(1.005)+(-0)e1+(-0)e2+(0)e3+(0.005)e4+(0)e5]
[1,1.01]
[0.61,1.41]

区間型で初期化し、to_intervalで区間に戻している。区間演算と 比較して狭い区間が得られていることが分かる。

ダミー変数の最大数について

ダミー変数の最大数は、 に格納されている。プログラム開始時は0で、affine変数を区間で 初期化、affine変数に区間を代入、非線形計算(naffineのときは線形計算でも)を 行う度に+1される。
int i;
i = afd::maxnum();
とすれば読み出せるし、
afd::maxnum() = 5;
のようにすれば書き換えることも出来る。

あるまとまった計算を行い、 次にそれらとはまったく独立な (前の計算で作成されたaffine変数を一切使わない)別の計算を 行う場合、いったんダミー変数の最大数を0にリセットすると 計算の高速化が期待できる。 また、区間化するなどでダミー変数のいくつかが不要になった場合など、 ダミー変数の最大数を減らしたいことがある。 いずれにしても、ダミー変数の最大数の変更はaffine arithmeticの原理を よく理解した上で慎重に行う必要がある。

機能詳細

加減乗除、square、sqrt、rad、to_interval など。

exp, logもあるが、こちらはcrlibmが必要 (test-affine2.cc)

resize, append, splitはode solverで使っている特殊な関数

epsilon_reduceはaffine変数のvectorを受け取って、ダミー変数の 削減を行う。

詳細はソースを見て欲しい。