Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

BUG: `special.betaincinv` / `stats.beta.ppf` wrong when `min(a, b) == 1000` exactly (Boost `ibeta_inv` falls between two strict comparisons)

クローズ 初心者向け
#1,511 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

関連するプルリクエストがすでにマージされています。

  • #1512 @NAThompson による — マージ済み

評価

難易度
2/5
見積もり時間
1〜3時間
初心者へのやさしさ
79/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
cpp
領域
backend

調査の方向性

初期推定値が選択される boost/math/special_functions/detail/ibeta_inverse.hpp の ibeta_inv_imp 付近、735~745 行を読んでください。まず ibeta_inv(1000, 1e9, 0.025) の回帰テストを実行するか追加してください。逆関数が期待値を返し、その点で正則化ベータ関数を評価すると 0.025 が得られ、鏡像の場合も含まれることをもって完了とします。

索引モデルが issue の本文から書いたものです。

説明

Summary

betaincinv(a, b, y) returns a wrong value when the smaller shape parameter is exactly 1000 and the other one is large (from about 2e7). a = 999.999 and a = 1000.001 are both fine. The error is not small: for betaincinv(1000, 1e9, 0.025) the result is 2x too large and betainc at it is 1.0, not 0.025. stats.beta.ppf goes through the same code. It reproduces on 1.17.1 and 1.18.1.

Reproducer

from scipy.special import betainc, betaincinv

for a in (999.999, 1000.0, 1000.001):
    x = betaincinv(a, 1e9, 0.025)
    print(f"a={a}: x={x:.6e}  betainc(a, b, x)={betainc(a, 1e9, x):.6f}")
a=999.999: x=9.389711e-07  betainc(a, b, x)=0.025000
a=1000.0: x=1.903149e-06  betainc(a, b, x)=1.000000
a=1000.001: x=9.389731e-07  betainc(a, b, x)=0.025000

The correct value is 9.389730e-07 (mpmath, 50 digits). stats.beta.ppf(0.025, 1000, 1e9) gives the same wrong 1.903149e-06. The mirrored case is wrong too: betainc(1e9, 1000, betaincinv(1e9, 1000, 0.975)) is 2.4e-115, not 0.975.

Where it fails

Scanning a = 2…20000 (integers) and b from 1e5 to 1e15, at y = 0.025 and 0.975, the only failing a is 1000. For a = 1000 the failures start at about b = 2e7. Every y we tried fails, not only the tails (betainc at the returned value: y = 1e-6 → 1, 0.1 → 1, 0.9 → 0.864).

Cause

ibeta_inv_imp in boost/math/special_functions/detail/ibeta_inverse.hpp (develop, around lines 735–745). When choosing the initial guess for large a + b:

if((y > 1e-5) && BOOST_MATH_GPU_SAFE_MIN(a, b) < 1000)
{
   x = temme_method_3_ibeta_inverse(a, b, p, q, pol);
   y = 1 - x;
}
else if ((y > 1e-5) && BOOST_MATH_GPU_SAFE_MIN(a, b) > 1000)
{
   // All options have failed, use the saddle point as a starting location:
   x = BOOST_MATH_GPU_SAFE_MAX(a, b) / (a + b);
   y = BOOST_MATH_GPU_SAFE_MIN(a, b) / (a + b);
}

Both comparisons are strict, so min(a, b) == 1000 takes neither branch: no initial guess is set for it, and the iteration converges to the wrong point. Making one of them inclusive (>= 1000 in the second) would cover the gap.

Suggested fix

  • Boost: make the saddle-point branch min(a, b) >= 1000 (or the Temme branch <= 1000), and add ibeta_inv(1000, 1e9, 0.025) as a regression case.
  • SciPy: pick up the fixed Boost, and add the reproducer above to scipy/special tests (betainc(a, b, betaincinv(a, b, y)) ≈ y at a = 1000, b = 1e9).

Workaround

Solve betainc(a, b, x) = y directly, which is accurate at these arguments:

from scipy.optimize import brentq
from scipy.special import betainc

x = brentq(lambda x: betainc(1000, 1e9, x) - 0.025, 0, 1, xtol=1e-300, rtol=1e-12)  # 9.389730e-07

Versions

SciPy 1.17.1 and 1.18.1, NumPy 2.5.1, Python 3.13.5, macOS 26.6 arm64. Not found in existing SciPy or Boost.Math issues; the nearest, boostorg/math#1487, is a different ibeta_inv failure (a = b = 5, p = 0.5).

主要言語
C++
スター
377
フォーク
271
平均マージ
11時間 23分
マージ済み PR(30日)
78

環境構築

このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

boostorg/math のほかの issue

boostorg/math の issue をすべて見る

似ている issue

C++ の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。