Files
tensor/stats/negbinom_far_pin_test.go
T
petrbalvin af4ee19703
Release / gates (push) Successful in 4m38s
Test / test (push) Successful in 5m16s
Release / release (push) Successful in 35s
feat: initial release
Assisted-by: GLM 5.3 Flash
2026-09-03 10:00:00 +02:00

61 lines
2.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
// SPDX-License-Identifier: MIT
// Pin for the negative binomial's far regime: when the summation's seed
// p^r underflows to an exact zero the recurrence can no longer start,
// and the CDF answers through the regularised beta identity instead.
// The law with r = 100 and p = 1e-4 has its mean at 999 900, so the CDF
// at the mean is near one half; the seed 1e-400 underflows outright, and
// the summation that ignored that answered an exact zero at every k.
package stats
import (
"math"
"testing"
)
func TestNegativeBinomialFarTail(t *testing.T) {
atMean, err := NegativeBinomialCDF(999900, 100, 1e-4)
if err != nil {
t.Fatalf("NegativeBinomialCDF(999900, 100, 1e-4): %v", err)
}
// The mean is r(1−p)/p = 999 900 and the spread is near 100 000, so
// the true value at the mean sits within a few 1e-3 of one half
// (exactly 0.5133 by the exact referent). The identity's accuracy in
// this regime is bounded by the lgamma noise of its front factor, so
// the pin holds a loose band and a strict ordering.
if math.Abs(atMean-0.5133007914) > 1e-6 {
t.Fatalf("NegativeBinomialCDF at the mean = %.17g, want 0.5133007914 to within 1e-6", atMean)
}
below, err := NegativeBinomialCDF(950000, 100, 1e-4)
if err != nil {
t.Fatalf("NegativeBinomialCDF(950000, 100, 1e-4): %v", err)
}
above, err := NegativeBinomialCDF(1050000, 100, 1e-4)
if err != nil {
t.Fatalf("NegativeBinomialCDF(1050000, 100, 1e-4): %v", err)
}
if !(below < atMean && atMean < above) {
t.Fatalf("the CDF is not increasing through the mean: %.17g, %.17g, %.17g", below, atMean, above)
}
far, err := NegativeBinomialCDF(2000000, 100, 1e-4)
if err != nil {
t.Fatalf("NegativeBinomialCDF(2000000, 100, 1e-4): %v", err)
}
if far < 0.9999 {
t.Fatalf("NegativeBinomialCDF two hundred standard deviations out = %.17g, want ~1", far)
}
// The quantile route runs on the same CDF: the median must sit near
// the mean the law's own moments give, a fraction of the spread
// below it on the skewed side. The exact referent puts the median at
// 996 569, and the pin holds it within a thousandth of the spread.
median, err := NegativeBinomialQuantile(0.5, 1e-4, 100)
if err != nil {
t.Fatalf("NegativeBinomialQuantile: %v", err)
}
if math.Abs(median-996569) > 100 {
t.Fatalf("NegativeBinomialQuantile(0.5) = %v, want within 100 of the exact median 996569", median)
}
}