changeset 30016:ad6a57b215e8

betainc.m: Improve accuracy for certain special integer inputs (bug #60682). Use exact computation for trivial values of parameters a and b. Otherwise, fall back on continued fraction expansion (default algorithm). * NEWS: Announce better accuracy for special cases. * betainc.m: Check input for 3 special cases: 1) a = 1, b = 1; 2) a = 1, b = anything; 3) a = anything, b = 1. Compute these exactly. Use indexing to select remaining non-trivial values from input and calculate these with continued fraction expansion.
author Michele Ginesi <michele.ginesi@gmail.com>
date Fri, 28 May 2021 18:26:05 +0200
parents af135277dc50
children 26bb2cbf6da2
files NEWS scripts/specfun/betainc.m
diffstat 2 files changed, 36 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Thu Aug 19 16:36:35 2021 -0400
+++ b/NEWS	Fri May 28 18:26:05 2021 +0200
@@ -81,6 +81,9 @@
 Matlab-compatible and yields results which more nearly minimize `norm
 (A*x - b)`.  Previously, Octave computed a minimum-norm solution.
 
+- The `betainc` function now calculates an exact output for the
+important special cases where a or b are 1.
+
 - The `whos` function now displays an additional attribute 's' when
 the variable is a sparse type.
 
--- a/scripts/specfun/betainc.m	Thu Aug 19 16:36:35 2021 -0400
+++ b/scripts/specfun/betainc.m	Fri May 28 18:26:05 2021 +0200
@@ -127,13 +127,38 @@
   ## Initialize output array
   y = zeros (size (x), class (x));
 
+  ## Trivial cases (long code here trades memory for speed)
+  a_one = (a == 1);
+  b_one = (b == 1);
+  a_b_one = a_one & b_one;
+  a_not_one = ! a_one;
+  b_not_one = ! b_one;
+  non_trivial = a_not_one & b_not_one;
+  a_one &= b_not_one;
+  b_one &= a_not_one;
+  
+  if (strcmpi (tail, "lower"))
+    y(a_b_one) = x(a_b_one);
+    y(a_one) = 1 - (1 - x(a_one)) .^ b(a_one);
+    y(b_one) = x(b_one) .^ a(b_one);
+  elseif (strcmpi (tail, "upper"))
+    y(a_b_one) = 1 - x(a_b_one);
+    y(a_one) = (1 - x(a_one)) .^ b(a_one);
+    y(b_one) = 1 - x(b_one) .^ a(b_one);
+  endif
+  
+  ## Non-Trivial cases
   ## In the following, we use the fact that the continued fraction Octave uses
   ## is more efficient when x <= a / (a + b).  Moreover, to compute the upper
   ## version, which is defined as I_x(a,b,"upper") = 1 - I_x(a,b) we use the
   ## property I_x(a,b) + I_(1-x) (b,a) = 1.
+  
+  x = x(non_trivial);
+  a = a(non_trivial);
+  b = b(non_trivial);
 
   if (strcmpi (tail, "lower"))
-    fflag = (x > a./(a+b));
+    fflag = (x > a./(a + b));
     x(fflag) = 1 - x(fflag);
     [a(fflag), b(fflag)] = deal (b(fflag), a(fflag));
   elseif (strcmpi (tail, "upper"))
@@ -153,10 +178,12 @@
   f = __betainc__ (x, a, b);
 
   ## Divide continued fraction by B(a,b) / (x^a * (1-x)^b) to obtain I_x(a,b).
-  y = a .* log (x) + b .* log1p (-x) ...
-      + (gammaln (a + b) - gammaln (a) - gammaln (b)) + log (f);
-  y = real (exp (y));
-  y(fflag) = 1 - y(fflag);
+  y_nt = a .* log (x) + b .* log1p (-x) ...
+         + (gammaln (a + b) - gammaln (a) - gammaln (b)) + log (f);
+  y_nt = real (exp (y_nt));
+  y_nt(fflag) = 1 - y_nt(fflag);
+  
+  y(non_trivial) = y_nt;
 
   ## Restore original shape
   y = reshape (y, orig_sz);
@@ -220,7 +247,7 @@
 
 %!test <*34405>
 %! assert (betainc (NaN, 1, 2), NaN);
-%! assert (betainc (0.5, 1, Inf), NaN);
+%! assert (betainc (0.5, 1, Inf), 1);
 
 ## Test input validation
 %!error <Invalid call> betainc ()