distriprob
Version:
A library for calculating the PDF, CDFs, and quantile function values of common probability distributions
317 lines (316 loc) • 9.74 kB
JavaScript
;
const async_1 = require("./async");
const random_1 = require("./random");
const cfs = require("./continuedFractionSolver");
const rf = require("./rootFind");
// This import and then renaming of imports is necessary to allow the async module to
// correctly generate web worker scripts.
const continuedFractionSolver = cfs.continuedFractionSolver;
const rootFind = rf.rootFind;
function lnGamma(val) {
const EULER_MASCHERONI = 0.57721566490153286060651209008240243104215933593992;
let result;
var valFloor = Math.floor(val);
var x = val - valFloor;
if (x === 0) {
result = 0;
for (var i = 2; i < valFloor; i++) {
result = result + Math.log(i);
}
}
else {
var sum = ((1 / 2) - x) * (EULER_MASCHERONI + Math.log(2));
sum = sum + ((1 - x) * Math.log(Math.PI));
sum = sum - ((1 / 2) * Math.log(Math.sin(Math.PI * x)));
var infiniteSum = 0;
for (var n = 1; n <= 100; n++) {
var addition = (Math.sin(2 * Math.PI * n * x) * Math.log(n)) / n;
infiniteSum = infiniteSum + addition;
}
sum = sum + ((1 / Math.PI) * infiniteSum);
result = sum;
for (var j = 0; j < valFloor; j++) {
result = result + Math.log(x);
x++;
}
}
return result;
}
exports.lnGamma = lnGamma;
/**
* This function gives the log of the extended(on all non-negative n, possibly
* non-integer n) factorial function.
* @param n
*/
function lnFactorial(n) {
if (typeof n !== "number") {
throw new Error(`The log factorial function is only defined for numeric arguments.`);
}
if (n < 0) {
throw new Error(`The log factorial of negative numbers in not defined.`);
}
if (n === 0) {
return Number.NEGATIVE_INFINITY;
}
return lnGamma(n + 1);
}
exports.lnFactorial = lnFactorial;
function gammaContinuedFraction(x, a) {
function num(j) {
if (j === 1) {
return 1;
}
else if (j % 2 === 0) {
return j / 2 - a;
}
else if (j % 2 === 1) {
return Math.floor(j / 2);
}
else {
throw new Error(`argument must be non-negative integer`);
}
}
function denom(j) {
if (j === 0) {
return 0;
}
else if (j % 2 === 0) {
return 1;
}
else if (j % 2 === 1) {
return x;
}
else {
throw new Error(`argument must be non-negative integer`);
}
}
return continuedFractionSolver(num, denom);
}
exports.gammaContinuedFraction = gammaContinuedFraction;
function lnLowerIncompleteGammaA(x, a) {
const numerator = (a * Math.log(x)) - x;
const denominator = lnGamma(a + 1);
let summands = [1];
let infiniteSum = 0;
let summandDenominator;
let i = 1;
do {
summandDenominator = 0;
for (var j = 1; j <= i; j++) {
summandDenominator = summandDenominator + Math.log(a + j);
}
summands.push(Math.exp((i * Math.log(x)) - summandDenominator));
i++;
} while (summands[i - 1] > 0);
summands = summands.sort();
for (var k = 0; k < summands.length; k++) {
infiniteSum += summands[k];
}
return numerator - denominator + Math.log(infiniteSum);
}
exports.lnLowerIncompleteGammaA = lnLowerIncompleteGammaA;
function lnUpperIncompleteGammaB(x, a) {
const numerator = (a * Math.log(x)) - x;
const denominator = lnGamma(a);
const lnContinuedFraction = Math.log(gammaContinuedFraction(x, a));
return numerator - denominator + lnContinuedFraction;
}
exports.lnUpperIncompleteGammaB = lnUpperIncompleteGammaB;
function lnLowerIncompleteGamma(x, a) {
if (x <= a) {
return lnLowerIncompleteGammaA(x, a);
}
else if (x > a && x <= a + 1) {
const weight = x - a;
const incompleteGammaA = lnLowerIncompleteGammaA(x, a);
const incompleteGammaB = Math.log(1 - Math.exp(lnUpperIncompleteGammaB(x, a)));
return ((1 - weight) * incompleteGammaA) + (weight * incompleteGammaB);
}
else {
return Math.log(1 - Math.exp(lnUpperIncompleteGammaB(x, a)));
}
}
exports.lnLowerIncompleteGamma = lnLowerIncompleteGamma;
function lnUpperIncompleteGamma(x, a) {
if (x <= a) {
return Math.log(1 - Math.exp(lnLowerIncompleteGammaA(x, a)));
}
else if (x > a && x <= a + 1) {
const weight = x - a;
const incompleteGammaA = Math.log(1 - Math.exp(lnLowerIncompleteGammaA(x, a)));
const incompleteGammaB = lnUpperIncompleteGammaB(x, a);
return ((1 - weight) * incompleteGammaA) + (weight * incompleteGammaB);
}
else {
return lnUpperIncompleteGammaB(x, a);
}
}
exports.lnUpperIncompleteGamma = lnUpperIncompleteGamma;
function lowerIncompleteGamma(x, a) {
if (x <= a) {
return Math.exp(lnLowerIncompleteGammaA(x, a));
}
else if (x > a && x <= a + 1) {
var weight = x - a;
var incompleteGammaA = Math.exp(lnLowerIncompleteGammaA(x, a));
var incompleteGammaB = 1 - Math.exp(lnUpperIncompleteGammaB(x, a));
return (1 - weight) * incompleteGammaA + weight * incompleteGammaB;
}
else {
return 1 - Math.exp(lnUpperIncompleteGammaB(x, a));
}
}
exports.lowerIncompleteGamma = lowerIncompleteGamma;
function upperIncompleteGamma(x, a) {
if (x <= a) {
return 1 - Math.exp(lnLowerIncompleteGammaA(x, a));
}
else if (x > a && x <= a + 1) {
var weight = x - a;
var incompleteGammaA = 1 - Math.exp(lnLowerIncompleteGammaA(x, a));
var incompleteGammaB = Math.exp(lnUpperIncompleteGammaB(x, a));
return (1 - weight) * incompleteGammaA + weight * incompleteGammaB;
}
else {
return Math.exp(lnUpperIncompleteGammaB(x, a));
}
}
exports.upperIncompleteGamma = upperIncompleteGamma;
function inverseLowerIncompleteGamma(p, a, initialEstimate) {
if (!initialEstimate) {
initialEstimate = a;
}
const lnLowIncompGamma = function (x) {
return lnLowerIncompleteGamma(x, a);
};
const derivativeLnLowIncompGammma = function (x) {
return Math.exp(((a - 1) * Math.log(x)) - x - lnGamma(a) + lnLowerIncompleteGamma(x, a));
};
return rootFind(lnLowIncompGamma, derivativeLnLowIncompGammma, Math.log(p), initialEstimate, null, 0);
}
exports.inverseLowerIncompleteGamma = inverseLowerIncompleteGamma;
function pdfSync(x, shape, scale) {
if (x <= 0) {
return 0;
}
else {
const lnNum = ((shape - 1) * Math.log(x)) - (x / scale);
const lnDenom = (lnGamma(shape) + (shape * Math.log(scale)));
return Math.exp(lnNum - lnDenom);
}
}
exports.pdfSync = pdfSync;
function pdf(x, shape, scale) {
return async_1.asyncGen([lnGamma], pdfSync, [x, shape, scale]);
}
exports.pdf = pdf;
function cdfSync(x, shape, scale, lowerTail = true) {
return gammaCDF(x, shape, scale, lowerTail);
}
exports.cdfSync = cdfSync;
function gammaCDF(x, shape, scale, lowerTail = true) {
if (x <= 0) {
if (lowerTail) {
return 0;
}
else {
return 1;
}
}
else {
let a = x / scale;
if (lowerTail) {
return lowerIncompleteGamma(a, shape);
}
else {
return upperIncompleteGamma(a, shape);
}
}
}
exports.gammaCDF = gammaCDF;
function cdf(x, shape, scale, lowerTail = true) {
return async_1.asyncGen([
lnGamma,
gammaContinuedFraction,
continuedFractionSolver,
lnLowerIncompleteGammaA,
lnUpperIncompleteGammaB,
lowerIncompleteGamma,
upperIncompleteGamma
], cdfSync, [x, shape, scale, lowerTail]);
}
exports.cdf = cdf;
function quantileSync(p, shape, scale, lowerTail = true) {
function f(val) {
return cdfSync(val, shape, scale, lowerTail);
}
function fPrime(val) {
if (lowerTail) {
return pdfSync(val, shape, scale);
}
else {
return -pdfSync(val, shape, scale);
}
}
if (p === 0) {
if (lowerTail) {
return 0;
}
else {
return Number.POSITIVE_INFINITY;
}
}
else if (p === 1) {
if (lowerTail) {
return Number.POSITIVE_INFINITY;
}
else {
return 0;
}
}
else {
const distMean = shape * scale;
return rootFind(f, fPrime, p, distMean, null, 0);
}
}
exports.quantileSync = quantileSync;
function quantile(p, shape, scale, lowerTail = true) {
return async_1.asyncGen([
lnGamma,
gammaContinuedFraction,
continuedFractionSolver,
lnLowerIncompleteGammaA,
lnUpperIncompleteGammaB,
lowerIncompleteGamma,
upperIncompleteGamma,
rootFind,
rf.newton,
rf.bisection,
pdfSync,
gammaCDF,
cdfSync
], quantileSync, [p, shape, scale, lowerTail]);
}
exports.quantile = quantile;
function randomSync(n, shape, scale, seed, randoms) {
return random_1.randSync(n, quantileSync, [shape, scale], seed, randoms);
}
exports.randomSync = randomSync;
function random(n, shape, scale, seed) {
return random_1.rand(n, quantileSync, [shape, scale], seed, [
lnGamma,
gammaContinuedFraction,
continuedFractionSolver,
lnLowerIncompleteGammaA,
lnUpperIncompleteGammaB,
lowerIncompleteGamma,
upperIncompleteGamma,
rootFind,
rf.newton,
rf.bisection,
pdfSync,
gammaCDF,
cdfSync
]);
}
exports.random = random;