rStats.js

精度

English 日本語

この文書は 英語版 の訳です。内容が食い違うときは英語版が正です。

公開しているすべての関数を R 4.6.2 と照合してテストしています (R にない erf と erfc は mpmath と照合)。期待値は oracle/ の R スクリプトで生成し、test/fixtures/ に保存しています (1 ケースにつき JSON 1 行)。そのため、テストの実行に R は要りません。テストの入力には必ず、典型的な値、遠い裾 (p = 1e-300)、境界 (0, 1, df → ∞)、NaN と Infinity、負の値、巨大なパラメータを含めています。

ケースが合格する条件

  1. 下記の許容誤差の範囲で R と一致する。または
  2. 同じ許容誤差の範囲で 高精度の参照値と一致する (256〜1300 ビットの Rmpfr、または gmp による厳密な有理数)。この場合は R 自身が誤っており、テストの出力では「R より正確」として数えます。または
  3. 分位点関数では、後退誤差が R 以下である。つまり、rStats.js の答えで評価した分布関数の値が、R の答えで評価した値と同じくらい p に近いことです。分布関数が平坦なところでは分位点関数の条件数が大きく、p のわずかな違いが答えの大きな違いになるため、そこで相対誤差を比べても意味がありません。離散分布では、答えは定義を満たす最小の整数でなければならず、これを分布関数で確かめます。または
  4. 検定、モデルとほかの一部の関数では、rStats.js が意図して R の結果を再現しないケースで、置き換えた期待値と一致する。結果が定義されないために R が NaN や ±Inf を返し、rStats.js がエラーを投げる入力や、R のバグによる R のエラーが当たります。テストは R の期待値をそのエラーや正しい値に置き換え (テストファイルの substitute)、理由を件数とともに出力します。

rStats.js が意図して R と異なるケースは、テストファイルで理由を書いたうえで除外し (knownDifference)、differences-from-r.md に載せています。テストの出力は、フィクスチャごとに、R と一致したケース、参照値と一致したケース、後退誤差で合格したケース、除外したケースの数を報告します。

許容誤差は上限であって、目標ではありません。R が正しい範囲では、rStats.js は R と同じビットを目指します。別の計算に切り替えるのは、ふだんの計算の結果が有限でない、範囲外、または桁落ちしているときだけです。そのため、遠い範囲の修正で通常の範囲の結果が変わることはありません。R が誤っている範囲では、その範囲に限って rStats.js は正しい値を返し、違いを differences-from-r.md に記録します。

比較の詳細は次のとおりです。

参照値

許容誤差

分野 相対許容誤差
特殊関数、密度関数、分布関数 1e-14 (非心 t: 1e-13、ptukey: 2e-14)
base R にない分布 (逆ガンマ、Kumaraswamy、Pareto、三角、Laplace、逆正弦) d/p/q は 1e-14。extraDistr (逆正弦は R の beta(1/2, 1/2) を (a, b) に移したもの) と mpmath に対して
分位点関数 1e-14、または後退誤差が R 以下。離散分布の分位点は完全一致か定義による検証
乱数 離散値、sample、r2dtable、L’Ecuyer-CMRG の runif と各ケース後の状態 (.Random.seed) は完全一致。連続値は 1e-12 以内。消費した一様乱数の個数を厳密に確かめる
記述統計 1e-14 (打ち消し合いから決まる 0 に近い歪度と尖度は絶対誤差で測る)
仮説検定 すべての数値フィールドで 1e-12 (cor: 1e-15、pAdjust: 完全一致。ansariTest の正規近似の区間など、許容誤差 1e-4 の uniroot で求める根: 1e-4)。テキストフィールドは完全一致
線形モデル (lm、confint、predict) すべての数値フィールドで 1e-12
混合モデル (lmer) 厳密な最適点の参照値に対して、logLik、AIC、BIC、deviance、REMLcrit は 1e-12。θ、固定効果とその共分散と標準誤差、σ、VarCorr、ranef、当てはめ値と残差は、それぞれの最大の絶対値の 1e-9 (観測した最大は 1.2e-12。境界のすぐ内側の平らな最適点)。参照値で 0 の θ の要素はちょうど 0。lme4 に対しては、基準の値が lme4 より大きくないことだけ。与えた θ での逸脱度は 1e-12
線形代数 1e-14 (crossprod、norm、backsolve、chol、qr、svd による 2-ノルム、行列演算、seq、lu) または 1e-13 (solve、determinant、rcond、svd、kappa、ginv、eigen)。結果のノルムに対する相対値で測る。prcomp は 1e-12。ピボット、ランク、行列式の符号は完全一致
数値計算 1e-15 (approx)、1e-14 (integrate、optimize、uniroot、barylag、hermiteInterp、ode、および絶対誤差の下限 1e-12 付きの grad、jacobian、hessian)、1e-13 (spline、splinefun、splinefunH)

許容誤差は分野ごとに実測をもとに決めました。R または参照値に対して観測された最大の誤差は、許容誤差をわずかに下回っています。

一部の許容誤差が広い理由

乱数

Math.log、Math.exp、Math.pow、Math.tan の最後の数ビットが V8 と glibc の libm で異なるため、連続値は完全には一致しません。観測された最大の差は 1.2e-13 で、shape がごく小さい (例: 0.001) ガンマ乱数で生じます。そこでは exp(log(u)/shape) が誤差を 1/shape 倍に拡大します。一様乱数の個数は、各ケースの後に runif(1) を引いて確かめます。採択・棄却の判定が 1 回でも異なれば、そこに現れます。

R の既定以外の RNG の種類 (rngRounding、rngLecuyer、rngStreams) も同じ方法で確かめ、さらに各ケースの後の状態 (getRandomSeed() と R の .Random.seed。種類のコードと、L’Ecuyer-CMRG では 7 個の整数すべて) を比べます。L’Ecuyer-CMRG の一様乱数は double による厳密な整数演算で計算するので、runif と離散値はすべて R と完全に一致します。連続値 (rnorm、rexp、rgamma) は上と同じく 1e-12 以内で比べます。

仮説検定

線形代数

既知の精度の問題

テストしている範囲では、既知の問題はありません。すべてのケースが、許容誤差の範囲で R または高精度の参照値と一致します。

その範囲の外では、次数が数千で x < ν の besselJ と besselY の相対誤差が最大で 1.5e-14 ほどになります (たとえば besselY(3000, 3949) で 1.04e-14、besselJ(1000, 1500) で 9.9e-15、besselJ(2000, 2999) で 1.5e-14。0.16.0 で、次数 1100〜9000 の 25 点を Rmpfr と比べて測りました)。漸化式と連分数の多数のステップで丸め誤差が積み重なるためです。

R の qnbinom は 13 個の入力で終了しません (例: qnbinom(-1, 0.5, 1e-10, log.p = TRUE))。oracle/nbinom.R は各ケースを 2 秒に制限し、それらの行をフィクスチャから外します。

精度を支える工夫

ほとんどのカーネルは SciPy の xsf (BSD-3) と Boost.Math (BSL-1.0) から移植しています。線形代数は LAPACK のリファレンス実装 (修正 BSD)、LINPACK、EISPACK (パブリックドメイン) に従っています。THIRD_PARTY_NOTICES.md を参照してください。R に並ぶ、または R を上回るために加えた主な変更は次のとおりです。

これにかかる速度面のコストと、double-double の効果を測る方法は performance.md で説明しています。