ibeta_series.js (4600B)
1 /** 2 * @license Apache-2.0 3 * 4 * Copyright (c) 2018 The Stdlib Authors. 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 * 18 * 19 * ## Notice 20 * 21 * The original C++ code and copyright notice are from the [Boost library]{@link http://www.boost.org/doc/libs/1_64_0/boost/math/special_functions/beta.hpp}. The implementation has been modified for JavaScript. 22 * 23 * ```text 24 * (C) Copyright John Maddock 2006. 25 * 26 * Use, modification and distribution are subject to the 27 * Boost Software License, Version 1.0. (See accompanying file 28 * LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) 29 * ``` 30 */ 31 32 'use strict'; 33 34 // MODULES // 35 36 var lanczosSumExpGScaled = require( './../../../../base/special/gamma-lanczos-sum-expg-scaled' ); 37 var sumSeries = require( './../../../../base/tools/sum-series' ); 38 var log1p = require( './../../../../base/special/log1p' ); 39 var sqrt = require( './../../../../base/special/sqrt' ); 40 var exp = require( './../../../../base/special/exp' ); 41 var pow = require( './../../../../base/special/pow' ); 42 var ln = require( './../../../../base/special/ln' ); 43 var MIN_VALUE = require( '@stdlib/constants/float64/smallest-normal' ); 44 var MAX_LN = require( '@stdlib/constants/float64/max-ln' ); 45 var MIN_LN = require( '@stdlib/constants/float64/min-ln' ); 46 var G = require( '@stdlib/constants/float64/gamma-lanczos-g' ); 47 var E = require( '@stdlib/constants/float64/e' ); 48 49 50 // VARIABLES // 51 52 var opts = { 53 'maxTerms': 100 54 }; 55 56 57 // FUNCTIONS // 58 59 /** 60 * Series approximation to the incomplete beta. 61 * 62 * @private 63 * @param {NonNegativeNumber} a - function parameter 64 * @param {NonNegativeNumber} b - function parameter 65 * @param {Probability} x - function parameter 66 * @param {number} result - initial result value 67 * @returns {Function} series function 68 */ 69 function ibetaSeriesT( a, b, x, result ) { 70 var poch = 1.0 - b; 71 var n = 1; 72 return next; 73 74 /** 75 * Calculate the next term of the series. 76 * 77 * @private 78 * @returns {number} series expansion term 79 */ 80 function next() { 81 var r = result / a; 82 a += 1.0; 83 result *= poch * x / n; 84 n += 1; 85 poch += 1.0; 86 return r; 87 } 88 } 89 90 91 // MAIN // 92 93 /** 94 * Incomplete beta series. 95 * 96 * @private 97 * @param {NonNegativeNumber} a - function parameter 98 * @param {NonNegativeNumber} b - function parameter 99 * @param {Probability} x - function parameter 100 * @param {NonNegativeInteger} s0 - initial value 101 * @param {boolean} normalized - boolean indicating whether to evaluate the power terms of the regularized or non-regularized incomplete beta function 102 * @param {(Array|TypedArray|Object)} out - output array holding the derivative as the second element 103 * @param {Probability} y - probability equal to `1-x` 104 * @returns {number} function value 105 */ 106 function ibetaSeries( a, b, x, s0, normalized, out, y ) { 107 var result; 108 var agh; 109 var bgh; 110 var cgh; 111 var l1; 112 var l2; 113 var c; 114 var s; 115 116 if ( normalized ) { 117 c = a + b; 118 119 // Incomplete beta power term, combined with the Lanczos approximation: 120 agh = a + G - 0.5; 121 bgh = b + G - 0.5; 122 cgh = c + G - 0.5; 123 result = lanczosSumExpGScaled( c ) / ( lanczosSumExpGScaled( a ) * lanczosSumExpGScaled( b ) ); // eslint-disable-line max-len 124 125 l1 = ln( cgh / bgh ) * ( b - 0.5 ); 126 l2 = ln( x * cgh / agh ) * a; 127 128 // Check for over/underflow in the power terms: 129 if ( 130 l1 > MIN_LN && 131 l1 < MAX_LN && 132 l2 > MIN_LN && 133 l2 < MAX_LN 134 ) { 135 if ( a * b < bgh * 10.0 ) { 136 result *= exp( ( b-0.5 ) * log1p( a / bgh ) ); 137 } else { 138 result *= pow( cgh / bgh, b - 0.5 ); 139 } 140 result *= pow( x * cgh / agh, a ); 141 result *= sqrt( agh / E ); 142 143 if ( out ) { 144 out[ 1 ] = result * pow( y, b ); 145 } 146 } 147 else { 148 // We need logs, and this *will* cancel: 149 result = ln( result ) + l1 + l2 + ( ( ln( agh ) - 1.0 ) / 2.0 ); 150 if ( out ) { 151 out[ 1 ] = exp( result + ( b * ln( y ) ) ); 152 } 153 result = exp( result ); 154 } 155 } 156 else { 157 // Non-normalized, just compute the power: 158 result = pow( x, a ); 159 } 160 if ( result < MIN_VALUE ) { 161 return s0; // Safeguard: series can't cope with denorms. 162 } 163 s = ibetaSeriesT( a, b, x, result ); 164 opts.initialValue = s0; 165 return sumSeries( s, opts ); 166 } 167 168 169 // EXPORTS // 170 171 module.exports = ibetaSeries;