time-to-botec

Benchmark sampling in different programming languages
Log | Files | Refs | README

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;