inverse_students_t.js (5918B)
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_62_0/boost/math/special_functions/detail/t_distribution_inv.hpp}. The implementation has been modified for JavaScript. 22 * 23 * ```text 24 * Copyright John Maddock 2006. 25 * Copyright Paul A. Bristow 2007. 26 * 27 * Use, modification and distribution are subject to the 28 * Boost Software License, Version 1.0. (See accompanying file 29 * LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) 30 * ``` 31 */ 32 33 'use strict'; 34 35 // MODULES // 36 37 var erfcinv = require( './../../../../base/special/erfcinv' ); 38 var floor = require( './../../../../base/special/floor' ); 39 var ldexp = require( './../../../../base/special/ldexp' ); 40 var round = require( './../../../../base/special/round' ); 41 var acos = require( './../../../../base/special/acos' ); 42 var sqrt = require( './../../../../base/special/sqrt' ); 43 var abs = require( './../../../../base/special/abs' ); 44 var cos = require( './../../../../base/special/cos' ); 45 var pow = require( './../../../../base/special/pow' ); 46 var sin = require( './../../../../base/special/sin' ); 47 var SQRT2 = require( '@stdlib/constants/float64/sqrt-two' ); 48 var PI = require( '@stdlib/constants/float64/pi' ); 49 var inverseStudentsTBodySeries = require( './inverse_students_t_body_series.js' ); 50 var inverseStudentsTTailSeries = require( './inverse_students_t_tail_series.js' ); 51 var inverseStudentsTHill = require( './inverse_students_t_hill.js' ); 52 53 54 // VARIABLES // 55 56 var DF_THRESHOLD = 0x10000000; // 2^28 57 var ONE_THIRD = 1.0 / 3.0; 58 var EXP = ( 2.0 * 53.0 ) / 3.0; 59 var C = 0.85498797333834849467655443627193; 60 61 62 // MAIN // 63 64 /** 65 * Evaluates Student's t quantiles. 66 * 67 * @private 68 * @param {PositiveNumber} df - degrees of freedom 69 * @param {Probability} u - input probability 70 * @param {Probability} v - probability equal to `1-u` 71 * @returns {number} function value 72 */ 73 function inverseStudentsT( df, u, v ) { 74 var crossover; 75 var tolerance; 76 var rootAlpha; 77 var invert; 78 var result; 79 var alpha; 80 var tmp; 81 var p0; 82 var p2; 83 var p4; 84 var p5; 85 var p; 86 var r; 87 var x; 88 var a; 89 var b; 90 91 result = 0; 92 if ( u > v ) { 93 // Function is symmetric, so invert it: 94 tmp = v; 95 v = u; 96 u = tmp; 97 invert = true; 98 } else { 99 invert = false; 100 } 101 if ( floor(df) === df && df < 20 ) { 102 // We have integer degrees of freedom, try for the special cases first: 103 tolerance = ldexp( 1.0, EXP ); 104 105 switch ( floor( df ) ) { 106 case 1: 107 // `df = 1` is the same as the Cauchy distribution, see Shaw Eq 35: 108 if ( u === 0.5 ) { 109 result = 0.0; 110 } else { 111 result = -cos( PI * u ) / sin( PI * u ); 112 } 113 break; 114 case 2: 115 // `df = 2` has an exact result, see Shaw Eq 36: 116 result = ( (2.0*u) - 1.0 ) / sqrt( 2.0 * u * v ); 117 break; 118 case 4: 119 // `df = 4` has an exact result, see Shaw Eq 38 & 39: 120 alpha = 4.0 * u * v; 121 rootAlpha = sqrt( alpha ); 122 r = 4 * cos( acos( rootAlpha ) / 3.0 ) / rootAlpha; 123 x = sqrt( r - 4.0 ); 124 result = ( u - 0.5 < 0.0 ) ? -x : x; 125 break; 126 case 6: 127 // We get numeric overflow in this area: 128 if ( u < 1.0e-150 ) { 129 return ( ( invert ) ? -1 : 1 ) * inverseStudentsTHill( df, u ); 130 } 131 // Newton-Raphson iteration of a polynomial case, choice of seed value is taken from Shaw's online supplement: 132 a = 4.0 * ( u - (u*u) );// 1 - 4 * (u - 0.5f) * (u - 0.5f); 133 b = pow( a, ONE_THIRD ); 134 p = 6.0 * ( 1.0 + ( C * ( (1.0/b) - 1.0 ) ) ); 135 do { 136 p2 = p * p; 137 p4 = p2 * p2; 138 p5 = p * p4; 139 p0 = p; 140 141 // Next term is given by Eq 41: 142 p = 2.0 * ( (8.0*a*p5) - (270.0*p2) + 2187 ) / 143 ( 5.0 * ( (4.0*a*p4) - (216.0*p) - 243.0 ) ); 144 } while ( abs( (p - p0) / p ) > tolerance ); 145 146 // Use Eq 45 to extract the result: 147 p = sqrt( p - df ); 148 result = ( u - 0.5 < 0.0 ) ? -p : p; 149 break; 150 default: 151 if ( df > DF_THRESHOLD ) { // 2^28 152 result = erfcinv( 2.0 * u ) * SQRT2; 153 } else if ( df < 3 ) { 154 // Use a roughly linear scheme to choose between Shaw's tail series and body series: 155 crossover = 0.2742 - ( df * 0.0242143 ); 156 if ( u > crossover ) { 157 result = inverseStudentsTBodySeries( df, u ); 158 } else { 159 result = inverseStudentsTTailSeries( df, u ); 160 } 161 } else { 162 // Use Hill's method except in the extreme tails where we use Shaw's tail series. The crossover point is roughly exponential in -df: 163 crossover = ldexp( 1.0, round( df / -0.654 ) ); 164 if ( u > crossover ) { 165 result = inverseStudentsTHill( df, u ); 166 } else { 167 result = inverseStudentsTTailSeries( df, u ); 168 } 169 } 170 } 171 } else if ( df > DF_THRESHOLD ) { 172 result = -erfcinv( 2.0 * u ) * SQRT2; 173 } else if ( df < 3 ) { 174 // Use a roughly linear scheme to choose between Shaw's tail series and body series: 175 crossover = 0.2742 - ( df * 0.0242143 ); 176 if ( u > crossover ) { 177 result = inverseStudentsTBodySeries( df, u ); 178 } else { 179 result = inverseStudentsTTailSeries( df, u ); 180 } 181 } else { 182 // Use Hill's method except in the extreme tails where we use Shaw's tail series. The crossover point is roughly exponential in -df: 183 crossover = ldexp( 1.0, round( df / -0.654 ) ); 184 if ( u > crossover ) { 185 result = inverseStudentsTHill( df, u ); 186 } else { 187 result = inverseStudentsTTailSeries( df, u ); 188 } 189 } 190 return ( invert ) ? -result : result; 191 } 192 193 194 // EXPORTS // 195 196 module.exports = inverseStudentsT;