2014/06/02(月)gamma関数とdigamma関数
gamma関数を以前作ったけど、微分が0になる位置を正確に計算できなかったので、
特に負の領域で満足の出来ないものだった。(大きい区間幅の入力に弱い)
某R先生もgamma関数を作ってるけど、こちらは微分が0になる位置を数表で持っていて、
それは数式処理ソフトで作っているようで、少しずるい感じ。
その数式処理ソフトは精度保証されているのか? 単なる高精度計算である可能性は?
精度保証を行うソフトウェアは、その計算結果が精度保証出来ていると思えるための理由を
そのソフトウェア自身の中に持っていて欲しい気がする。
そのへんを完全にするために、digamma関数を何となく作り始めて、何となく動いた気がした。
-digamma(1) = γ (0.5772...=オイラー定数)らしいので、オイラー定数の精度保証もついでに出来たことになる?
2014/06/01(日)kv-0.4.4
定積分のライブラリに初期化し忘れがあり、例えばODEを解かせたあとにそのまま
定積分を行うと結果がおかしくなることがありました。
精度保証ライブラリとしては、計算結果の信頼性に影響を及ぼす可能性のあるバグはみっともないので、
こういう類のバグがあった場合は直ちにアップデートするつもりでいます。
また、Airy関数を試作して遊んでみました。Airy関数の満たす常微分方程式を初期値問題として
解くだけなので、絶対値の小さい範囲しか計算できないと思われます。
Ai(-100) = [0.17675339323949076,0.17675339323961479]
2014/05/27(火)HaswellとFMA
ついでにvmwareを最新にし、vmwareの中で飼ってるubuntuを14.04に。
CPUがSandyBridgeからHaswellになったので、ちょっとFMAで遊んでみました。
Haswellの新機能の一つに、FMA命令の追加があります。
FMA = Fused Multiply Add です。乗算と加算のフュージョンですね。
a * b + cをいっぺんに計算してくれる機能で、内積計算なんかが高速になりそうですが、
精度保証的には「a*bをいったん無誤差で計算してからcを加算し、最後に一回だけ丸める」という
動作が重要です。これを使うとtwoproductが非常に簡単に書けたりします。
で、ちょっと動くかどうか試してみました。
#include <stdio.h> #include <immintrin.h> double fma(double a, double b, double c) { double d; __m128d va = _mm_set_sd(a); __m128d vb = _mm_set_sd(b); __m128d vc = _mm_set_sd(c); __m128d vd = _mm_fmadd_sd(va, vb, vc); _mm_store_sd(&d, vd); return d; } int main() { printf("%f\n", fma(1., 2., 3.)); }こんな感じで動きました。gccだと-mfmaとか-march=nativeとか必要。VCは特に要らない?
AVXなので4並列で実行する能力がありますが、このコードは1つだけ計算してます。
1つだけ計算するのに128bitレジスタを使わなきゃいけないのはちょっと違和感?
twoproductに用いたときの性能評価とかはこれからぼちぼちやっていきましょう。
2014/04/16(水)kv-0.4.3
CardanoとFerrariを書いたときに気付いた複素数やdd周りの問題点を修正しています。
また、-DKV_NOHWROUNDをつけたときにハード的な丸めモードの変更を使わずに方向付き丸めのエミュレートで区間演算を行う機能が、ddの場合でも有効になりました。あまりスピードは出ないけど、IEEE754Std.に従う全てのCPUで動かせるようになりました。
2014/03/30(日)Cardanoの公式とFerrariの公式
数学の歴史で大変有名だけど、中身をよく吟味したことは無かった。
ちょっとしたきっかけで、これらをプログラムする機会があったのだが、
案外ややこしくて苦労した。Cardanoでは、複素数の3乗根を取る部分で3種類の解のうち
適切な1つを選ばないといけない部分があるのだが、こういうところがきちんと
書いてない文献が多かった。Ferrariでも平方根で同じような箇所がある。
最初はmathematica先生にSolve[a4 x^4 + a3 x^3 + a2 x^2 + a1 x + a0 = 0, x]とか投げて
出てきた式を適当に簡約化してこんなんでいいか、なんてやってたが、結果的にそれじゃ
使い物にならなかった。
CardanoやFerrariでは反復によらず直接解を計算するので、全計算過程を区間演算化すれば
容易に精度保証付きの解が得られる。と思って書いてみたが、ギリギリの計算が要求されて
ライブラリの問題点がいろいろ洗い出される結果に。
- atan2([0, 1e-10], [-1, 1])のような場合に結果の区間幅が大きくなってしまう。
- expに無限大に近いが無限大ではない数を入れたときの結果がおかしい (引数をfloorで丸めたものがintの範囲を超える場合)
- 複素数の絶対値や平方根で(特に原点を含むような引数で)エラーになることがある。
- ddの平方根に0を入れるとnanになる(newton法の失敗)
- exp(interval<dd>(-1e308)*2)のような計算で無限ループに陥る
複素数とddは実戦投入の経験が少なかったので、ちょうどいい訓練になった。
複素数の0に対してexp(log(0))がちゃんと0(あるいは0を含む微小区間)を返さなければ
うまく動かないようなシチュエーションも出てきて、intlabすらここはきちんと動かなかった。
最終的にはなんとか動くようになったのだが、CardanoはまだマシだがFerrariは素晴らしく
精度が出ない。doubleで平均的には7,8桁くらいか。
計算時間もかかるし、これらの方法が実際の数値計算法としてあまり使われない理由がよく分かった。
何でもやってみるものだなあ。
おかげでライブラリも結構アップデートしたので、ファイルの整理が出来たらversion upしよう。