Mercurial > octave
changeset 24919:ed6f6bbed604 stable
betainc: vectorized the Lentz's algorithm
--
changed libinterp/corefcn/__betainc_lentz__.cc
changed scripts/specfun/betainc.m
author | Michele Ginesi <michele.ginesi@gmail.com> |
---|---|
date | Wed, 21 Feb 2018 23:13:26 +0100 |
parents | ea3edda05b66 |
children | 9773b7ae807c |
files | libinterp/corefcn/__betainc_lentz__.cc scripts/specfun/betainc.m |
diffstat | 2 files changed, 155 insertions(+), 73 deletions(-) [+] |
line wrap: on
line diff
--- a/libinterp/corefcn/__betainc_lentz__.cc Tue Feb 20 20:37:25 2018 +0100 +++ b/libinterp/corefcn/__betainc_lentz__.cc Wed Feb 21 23:13:26 2018 +0100 @@ -1,4 +1,6 @@ -// Copyright (C) 2017 Michele Ginesi +// Copyright (C) 2017 Nir Krakauer +// Copyright (C) 2018 Stefan Schlögl +// Copyright (C) 2018 Michele Ginesi // // This file is part of Octave. // @@ -19,88 +21,164 @@ #if defined (HAVE_CONFIG_H) # include "config.h" #endif - #include "defun.h" +#include "fNDArray.h" DEFUN (__betainc_lentz__, args, , "Continued fraction for incomplete beta function") { int nargin = args.length (); - - if (nargin != 3) + octave_value_list outargs; + if (nargin != 4) print_usage (); else { - octave_value x_arg = args(0); - octave_value a_arg = args(1); - octave_value b_arg = args(2); - if (x_arg.is_single_type () || a_arg.is_single_type () || b_arg.is_single_type ()) + FloatNDArray x_arg_s = args(0).array_value (); + FloatNDArray a_arg_s = args(1).array_value (); + FloatNDArray b_arg_s = args(2).array_value (); + bool is_single = args(3).bool_value (); + + // total number of scenarios: get maximum of length of all vectors + int len_x = x_arg_s.rows (); + int len_a = a_arg_s.rows (); + int len_b = b_arg_s.rows (); + int len = std::max(len_x, len_a); + len = std::max(len, len_b); + + // input checks + if ((len_x != len) || (len_a != len) || (len_b != len)) + error("__betainc_lentz__: expecting arguments of same dimension"); + + // initialize scenario dependent output: + dim_vector dim_scen (len, 1); + ColumnVector f (dim_scen); + + if (! is_single) { - const float x = args(0).float_value (); - const float a = args(1).float_value (); - const float b = args(2).float_value (); - static const float tiny = pow (2, -50); - static const float eps = std::numeric_limits<float>::epsilon(); - float f = tiny; - float C = f; - float D = 0; - float alpha_m = 1; - float beta_m = a - (a * (a+b)) / (a + 1) * x; - float x2 = x * x; - float Delta = 0; - int m = 1; - int maxit = 100; - while((std::abs(Delta - 1) > eps) & (m < maxit)) + NDArray x_arg = args(0).array_value (); + NDArray a_arg = args(1).array_value (); + NDArray b_arg = args(2).array_value (); + NDArray x (dim_scen); + NDArray a (dim_scen); + NDArray b (dim_scen); + + // initialize scenario dependent input values (idx either 0 or ii) + if (len_x == 1) + x.fill (x_arg(0)); + else + x = x_arg; + // + if (len_a == 1) + a.fill (a_arg(0)); + else + a = a_arg; + if (len_b == 1) + b.fill (b_arg(0)); + else + b = b_arg; + static const double tiny = pow (2, -100); + static const double eps = std::numeric_limits<double>::epsilon(); + double xj, x2, y, Cj, Dj, aj, bj, Deltaj, alpha_j, beta_j; + int j, maxit; + maxit = 200; + // loop via all scenarios + for (octave_idx_type ii = 0; ii < len; ++ii) { - D = beta_m + alpha_m * D; - if (D == 0) - D = tiny; - C = beta_m + alpha_m / C; - if (C == 0) - C = tiny; - D = 1 / D; - Delta = C * D; - f *= Delta; - alpha_m = ((a + m - 1) * (a + b + m - 1) * (b - m) * m) / ((a + 2 * m - 1) * (a + 2 * m - 1)) * x2; - beta_m = a + 2 * m + ((m * (b - m)) / (a + 2 * m - 1) - ((a + m) * (a + b + m)) / (a + 2 * m + 1)) * x; - m++; - } - if (! error_state) - return octave_value (f); + // catch ctrl + c + OCTAVE_QUIT; + xj = x(ii); + y = tiny; + Cj = y; + Dj = 0; + aj = a(ii); + bj = b(ii); + Deltaj = 0; + alpha_j = 1; + beta_j = aj - (aj * (aj + bj)) / (aj + 1) * xj; + x2 = xj * xj; + j = 1; + + while((std::abs ((Deltaj - 1)) > eps) & (j < maxit)) + { + Dj = beta_j + alpha_j * Dj; + if (Dj == 0) + Dj = tiny; + Cj = beta_j + alpha_j / Cj; + if (Cj == 0) + Cj = tiny; + Dj = 1 / Dj; + Deltaj = Cj * Dj; + y *= Deltaj; + alpha_j = ((aj + j - 1) * (aj + bj + j - 1) * (bj - j) * j) / ((aj + 2 * j - 1) * (aj + 2 * j - 1)) * x2; + beta_j = aj + 2 * j + ((j * (bj - j)) / (aj + 2 * j - 1) - ((aj + j) * (aj + bj + j)) / (aj + 2 * j + 1)) * xj; + j++; + } + if (! error_state) + f(ii) = y; + } + outargs(0) = f; } else { - const double x = args(0).double_value (); - const double a = args(1).double_value (); - const double b = args(2).double_value (); - static const double tiny = pow (2, -100); - static const double eps = std::numeric_limits<double>::epsilon(); - double f = tiny; - double C = f; - double D = 0; - double alpha_m = 1; - double beta_m = a - (a * (a+b)) / (a + 1) * x; - double x2 = x * x; - double Delta = 0; - int m = 1; - int maxit = 200; - while((std::abs(Delta - 1) > eps) & (m < maxit)) + FloatNDArray x_s (dim_scen); + FloatNDArray a_s (dim_scen); + FloatNDArray b_s (dim_scen); + + // initialize scenario dependent input values (idx either 0 or ii) + if (len_x == 1) + x_s.fill (x_arg_s(0)); + else + x_s = x_arg_s; + // + if (len_a == 1) + a_s.fill (a_arg_s(0)); + else + a_s = a_arg_s; + if (len_b == 1) + b_s.fill (b_arg_s(0)); + else + b_s = b_arg_s; + static const float tiny = pow (2, -50); + static const float eps = std::numeric_limits<float>::epsilon(); + float xj, x2, y, Cj, Dj, aj, bj, Deltaj, alpha_j, beta_j; + int j, maxit; + maxit = 200; + // loop via all scenarios + for (octave_idx_type ii = 0; ii < len; ++ii) { - D = beta_m + alpha_m * D; - if (D == 0) - D = tiny; - C = beta_m + alpha_m / C; - if (C == 0) - C = tiny; - D = 1 / D; - Delta = C * D; - f *= Delta; - alpha_m = ((a + m - 1) * (a + b + m - 1) * (b - m) * m) / ((a + 2 * m - 1) * (a + 2 * m - 1)) * x2; - beta_m = a + 2 * m + ((m * (b - m)) / (a + 2 * m - 1) - ((a + m) * (a + b + m)) / (a + 2 * m + 1)) * x; - m++; - } - if (! error_state) - return octave_value (f); + // catch ctrl + c + OCTAVE_QUIT; + xj = x_s(ii); + y = tiny; + Cj = y; + Dj = 0; + aj = a_s(ii); + bj = b_s(ii); + Deltaj = 0; + alpha_j = 1; + beta_j = aj - (aj * (aj + bj)) / (aj + 1) * xj; + x2 = xj * xj; + j = 1; + + while((std::abs ((Deltaj - 1)) > eps) & (j < maxit)) + { + Dj = beta_j + alpha_j * Dj; + if (Dj == 0) + Dj = tiny; + Cj = beta_j + alpha_j / Cj; + if (Cj == 0) + Cj = tiny; + Dj = 1 / Dj; + Deltaj = Cj * Dj; + y *= Deltaj; + alpha_j = ((aj + j - 1) * (aj + bj + j -1) * (bj - j) * j) / ((aj + 2 * j - 1) * (aj + 2 * j - 1)) * x2; + beta_j = aj + 2 * j + ((j * (bj - j)) / (aj + 2 * j - 1) - ((aj + j) * (aj + bj + j)) / (aj + 2 * j + 1)) * xj; + j++; + } + if (! error_state) + f(ii) = y; + } + outargs(0) = f; } - } - return octave_value_list (); + } + return octave_value (outargs); }
--- a/scripts/specfun/betainc.m Tue Feb 20 20:37:25 2018 +0100 +++ b/scripts/specfun/betainc.m Wed Feb 21 23:13:26 2018 +0100 @@ -108,6 +108,10 @@ b = double (b); endif + sz = size (x); + x = x(:); + a = a(:); + b = b(:); y = zeros (size (x)); # If any of the argument is single, also the output should be @@ -143,15 +147,15 @@ ## implemented in a separate .cc file. This particular continued fraction ## gives (B(a,b) * I_x(a,b)) / (x^a * (1-x)^b). - for i = 1 : numel (x) - f(i) = __betainc_lentz__ (x(i), a(i), b(i)); - endfor + f = __betainc_lentz__ (x, a, b, strcmpi (class (x), "single")); + # We divide the 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(flag) = 1 - y(flag); + y = reshape (y, sz); endfunction