はじめに
BigMathR
BigMathRは、昔作っていたオブジェクト指向言語用数学ライブラリ“GlueMath”の亜種的なものです。いずれも数学アルゴリズムコレクションですが、こちらはAPIベースです。
命名について
元々は膨大なアルゴリズムのテストバリュを生成するために、Rubyの多倍長クラスであるBigDecimalクラスを利用して、初等関数の厳密値を参照する、という設計でした。しかし作っていくと、どちらかというとBigMathの多様化(オブジェクト指向デザインで言うならポリモーフィズム)が目立っていました。それでBigMathモジュールの拡張を作ろう、という流れで、Refineされた数学モジュールということで名前は"BigMathR"となったわけです。
実際はどうかというと、たかだか初等関数ソルバーを用意するというだけで、専門数学のアルゴリズム集みたいな作りになりました。(詳細はリンク先のGITHUBページを参考のほど)
BigMathRモジュールのトップレベルでアクセスできるモジュール関数の他、サブモジュールには様々な数学関数がインクルードされているというデザインで落ち着きました。トップレベルでは、現在、指数・対数・三角・双曲線・逆三角・逆双曲線の関数を網羅しています。
どうして公開なのか?
個人メモ程度のことなら qiitaでページ開設するほどのことでもありません。こうした世に残しておくもの(個人資産)の利用される範囲はワールドワイドですから、今度は引き継ぎ手のことも考えなくてはいけません。
その意味では、シェアされるものとしては、このライブラリに限ったことではないでしょう。
固定小数点を扱うため、アルゴリズムもそれを考えての実装となっています。面白いことに、sinとcos、sinhとcoshを同時に計算する級数展開アルゴリズムも収録しています。詳しくはGITHUBページを参照してください。
目次
- 使い方
- 仕様
- ポリモーフィズムな数学関数
- ブランチ構造
- バグ修正
- 既知のバグ
- 免責
- 今後の展開
使い方
Gemのインストールを示します:
$ gem install bigdecimal-math_r
以下でrequireします。
require 'bigdecimal/math_r'
仕様
現在バージョン
v0.2.8
ポリモーフィズムな数学関数
モジュールのトップレベルにある関数はポリモーフィズムな作りです。引数に実数を当てれば実数解、複素数を当てれば複素数解として値が返ってきます。定義域外であればエラーにはならず複素数解として解釈し値が返ってきます。例えばBigMathR.sqrt(-1, PREC)の解は(0.0+1.0i)です。
hypotも複素数に対応しておりますが、この関数は常に正の実数を返す関数です。例えばBigMathR.hypot(3i, 4i, PREC)の解は5.0です。
※v0.2.1から誤差関数に対応しました。実数解ですがBigMathR#erf、BigMathR#erfcが使えます。
ブランチ構造
数学関数は各々***_branch()という関数で取り扱っていて、主値を計算できるならどんなアルゴリズムにも対応するという方法を取っています(/という設計に落ち着きました)。引数が無理数・非数であった場合は専門ルーチンで解を創出し、定義域だけを計算します。このため、三角関数を扱う関数では引数の範囲を $-2\pi \leq x \leq 2\pi$ に収めるという面白い機構を実装しています。
| 実数解 | 計算域 | 複素数解 | 計算域 |
|---|---|---|---|
| $e^x$ | $0\lt x \lt 1$ | $e^z=e^{x+\imath y}$ | $x\in\mathbb{R},-2\pi\leq y \leq 2\pi$ |
| $2^x$ | $0\lt x \lt 1$ | $2^z=2^{x+\imath y}$ | $x\in\mathbb{R},-(2\pi/\log 2)\leq y \leq (2\pi/\log 2)$ |
| $\log x$ | $1 \leq x \lt 2$ | $\log z = \log x+\imath y$ | $x \in \mathbb{R}, y \in \mathbb{R}$ |
| $\log_2 x$ | $1 \leq x \lt 2$ | $\log_2 z = \log_2 x+\imath y$ | $x \in \mathbb{R}, y \in \mathbb{R}$ |
| $\log_{10} x$ | $1 \leq x \lt 10$ | $\log_{10} z = \log_{10} x+\imath y$ | $x \in \mathbb{R}, y \in \mathbb{R}$ |
| $\sin x$ | $-2\pi\leq x \leq 2\pi$ | $\sin z = \sin x+\imath y$ | $-2\pi\leq x \leq 2\pi, y \in \mathbb{R} $ |
| $\cos x$ | $-2\pi\leq x \leq 2\pi$ | $\cos z = \cos x+\imath y$ | $-2\pi\leq x \leq 2\pi, y \in \mathbb{R} $ |
| $\tan x$ | $-2\pi\leq x \leq 2\pi$ | $\tan z = \tan x+\imath y$ | $-2\pi\leq x \leq 2\pi, y \in \mathbb{R} $ |
| $\csc x$ | $-2\pi\leq x \leq 2\pi$ | $\csc z = \csc x+\imath y$ | $-2\pi\leq x \leq 2\pi, y \in \mathbb{R} $ |
| $\sec x$ | $-2\pi\leq x \leq 2\pi$ | $\sec z = \sec x+\imath y$ | $-2\pi\leq x \leq 2\pi, y \in \mathbb{R} $ |
| $\cot x$ | $-2\pi\leq x \leq 2\pi$ | $\cot z = \cot x+\imath y$ | $-2\pi\leq x \leq 2\pi, y \in \mathbb{R} $ |
| $\sinh x$ | $x \in \mathbb{R} \wedge | x | \lt 3$ | $\sinh z = \sinh x+\imath y$ | $x \in \mathbb{R}, -2\pi\leq y \leq 2\pi$ |
| $\cosh x$ | $x \in \mathbb{R} \wedge | x | \lt 3$ | $\cosh z = \cosh x+\imath y$ | $x \in \mathbb{R}, -2\pi\leq y \leq 2\pi$ |
| $\tanh x$ | $x \in \mathbb{R} \wedge | x | \lt 3$ | $\tanh z = \tanh x+\imath y$ | $x \in \mathbb{R}, -2\pi\leq y \leq 2\pi$ |
| $\textrm{csch}\ x$ | $x \in \mathbb{R} \wedge | x | \lt 3$ | $\textrm{csch}\ z = \textrm{csch}\ x+\imath y$ | $x \in \mathbb{R}, -2\pi\leq y \leq 2\pi$ |
| $\textrm{sech}\ x$ | $x \in \mathbb{R} \wedge | x | \lt 3$ | $\textrm{sech}\ z = \textrm{sech}\ x+\imath y$ | $x \in \mathbb{R}, -2\pi\leq y \leq 2\pi$ |
| $\coth x$ | $x \in \mathbb{R} \wedge | x | \lt 3$ | $\coth z = \coth x+\imath y$ | $x \in \mathbb{R}, -2\pi\leq y \leq 2\pi$ |
| $\sin^{-1}(x)$ | $\frac{1}{1000} \lt |x| \lt 1$ | $\sin^{-1}(z)=\sin^{-1}(x+\imath y)$ | $x \in \mathbb{R}, y \in \mathbb{R}$ |
| $\cos^{-1}(x)$ | $\frac{1}{1000} \lt |x| \lt 1$ | $\cos^{-1}(z)=\cos^{-1}(x+\imath y)$ | $x \in \mathbb{R}, y \in \mathbb{R}$ |
| $\tan^{-1}(x)$ | $x \in \mathbb{R}$ | $\tan^{-1}(z)=\tan^{-1}(x+\imath y)$ | $x \in \mathbb{R}, y \in \mathbb{R}$ |
| $\csc^{-1}(x)$ | $-1 \lt x \lt 1$ | $\csc^{-1}(z)=\csc^{-1}(x+\imath y)$ | $x \in \mathbb{R}, y \in \mathbb{R}$ |
| $\sec^{-1}(x)$ | $-1 \lt x \lt 1$ | $\sec^{-1}(z)=\sec^{-1}(x+\imath y)$ | $x \in \mathbb{R}, y \in \mathbb{R}$ |
| $\cot^{-1}(x)$ | $x \in \mathbb{R}$ | $\cot^{-1}(z)=\cot^{-1}(x+\imath y)$ | $x \in \mathbb{R}, y \in \mathbb{R}$ |
| $\sinh^{-1}(x)$ | $\frac{1}{1000} \lt |x| \lt 1$ | $\sinh^{-1}(z)=\sinh^{-1}(x+\imath y)$ | $x \in \mathbb{R}, y \in \mathbb{R}$ |
| $\cosh^{-1}(x)$ | $x \in \mathbb{R}$ | $\cosh^{-1}(z)=\cosh^{-1}(x+\imath y)$ | $x \in \mathbb{R}, y \in \mathbb{R}$ |
| $\tanh^{-1}(x)$ | $\frac{999}{1000} \lt |x| \lt 1$ | $\tanh^{-1}(z)=\tanh^{-1}(x+\imath y)$ | $x \in \mathbb{R}, y \in \mathbb{R}$ |
| $\textrm{csch}^{-1}(x)$ | $x \in \mathbb{R}$ | $\textrm{csch}^{-1}(z)=\textrm{csch}^{-1}(x+\imath y)$ | $x \in \mathbb{R}, y \in \mathbb{R}$ |
| $\textrm{sech}^{-1}(x)$ | $x \in \mathbb{R}$ | $\textrm{sech}^{-1}(z)=\textrm{sech}^{-1}(x+\imath y)$ | $x \in \mathbb{R}, y \in \mathbb{R}$ |
| $\coth^{-1}(x)$ | $x \in \mathbb{R}$ | $\coth^{-1}(z)=\coth^{-1}(x+\imath y)$ | $x \in \mathbb{R}, y \in \mathbb{R}$ |
バグ修正
v0.2.7:
- rcm2_edf()が指数部の増加・減少を正しく取らなかった
- オイラー・マスケローニ定数が正しい値を出さなかった
v0.2.6:
- いくつかの数学関数の分岐切断線(branch-cut)においてrb_range_new()を使用する際、rangeがopen-endedになっていた
-
BigMathR#asechが不正な値を導出していた - solver_triginv()とsolver_hyperbinv()が虚部0のComplexクラスを引数とした際エラーが出ていた
- rcm2_edf()が不要な型変換をしていた
既知のバグ
免責
今後の展開
実は、多倍長演算による数学関数実装は、筆者にとってこれが初めてです。持てる知識をすべて投入した格好ですが、誤差関数やガンマ関数の実装がまだという課題があります。特殊関数だと漸近展開や連分数展開なども実装しなくてはいけませんし、各々収束条件が異なるので調査が必要になります。概ね処理系定義であることがボトルネックです。