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 |
内部で使っている区間演算及び丸めの変更に boost.interval、 係数ベクトルの保持にboost.ublasを利用している。 従って、boostがきちんと動く環境が必要である。
policyの指定はやや煩雑なので、intervalでは省略することが出来るように なっている。例えば
affine演算は区間演算と同時に使うことが多いだろうし、その区間演算と 同じpolicyを指定することが多いだろうから、次のようにすることをお勧めする。 区間型のpolicyは、「区間型::traits_type」で取り出すことが出来るので、
typedef boost::numeric::interval<double> itvd; typedef affine<double, itvd::traits_type> afd;のように定義してしまい、区間型をitvd、affine型をafdで プログラムを書けば楽であろう。
#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で区間に戻している。区間演算と 比較して狭い区間が得られていることが分かる。
int i; i = afd::maxnum();とすれば読み出せるし、
afd::maxnum() = 5;のようにすれば書き換えることも出来る。
あるまとまった計算を行い、 次にそれらとはまったく独立な (前の計算で作成されたaffine変数を一切使わない)別の計算を 行う場合、いったんダミー変数の最大数を0にリセットすると 計算の高速化が期待できる。 また、区間化するなどでダミー変数のいくつかが不要になった場合など、 ダミー変数の最大数を減らしたいことがある。 いずれにしても、ダミー変数の最大数の変更はaffine arithmeticの原理を よく理解した上で慎重に行う必要がある。
exp, logもあるが、こちらはcrlibmが必要 (test-affine2.cc)
resize, append, splitはode solverで使っている特殊な関数
epsilon_reduceはaffine変数のvectorを受け取って、ダミー変数の 削減を行う。
詳細はソースを見て欲しい。