herta
Version:
Advanced mathematics framework for scientific, engineering, and financial applications
470 lines (400 loc) • 11.6 kB
JavaScript
/**
* Probability distributions module for herta.js
* Provides methods for working with various probability distributions
*/
const utils = require('../utils/utils');
// Probability distributions module
const probability = {};
/**
* Normal (Gaussian) distribution functions
*/
probability.normal = {
/**
* Calculate the probability density function (PDF) of a normal distribution
* @param {number} x - The value to calculate the PDF at
* @param {number} [mean=0] - The mean of the distribution
* @param {number} [stdDev=1] - The standard deviation of the distribution
* @returns {number} - The PDF value
*/
pdf(x, mean = 0, stdDev = 1) {
return utils.normalPDF(x, mean, stdDev);
},
/**
* Calculate the cumulative distribution function (CDF) of a normal distribution
* @param {number} x - The value to calculate the CDF at
* @param {number} [mean=0] - The mean of the distribution
* @param {number} [stdDev=1] - The standard deviation of the distribution
* @returns {number} - The CDF value (between 0 and 1)
*/
cdf(x, mean = 0, stdDev = 1) {
return utils.normalCDF(x, mean, stdDev);
},
/**
* Generate a random sample from a normal distribution
* @param {number} [mean=0] - The mean of the distribution
* @param {number} [stdDev=1] - The standard deviation of the distribution
* @returns {number} - A random sample
*/
sample(mean = 0, stdDev = 1) {
// Box-Muller transform
const u1 = Math.random();
const u2 = Math.random();
const z0 = Math.sqrt(-2.0 * Math.log(u1)) * Math.cos(2.0 * Math.PI * u2);
return mean + stdDev * z0;
},
/**
* Calculate the inverse CDF (quantile function) of a normal distribution
* @param {number} p - Probability value (between 0 and 1)
* @param {number} [mean=0] - The mean of the distribution
* @param {number} [stdDev=1] - The standard deviation of the distribution
* @returns {number} - The value x such that CDF(x) = p
*/
inverse(p, mean = 0, stdDev = 1) {
if (p <= 0 || p >= 1) {
throw new Error('Probability must be between 0 and 1 (exclusive)');
}
// Approximation of the inverse error function
function inverseErf(x) {
const a = 0.147;
const y = Math.log(1 - x * x);
const z = 2 / (Math.PI * a) + y / 2;
return Math.sign(x) * Math.sqrt(Math.sqrt(z * z - y / a) - z);
}
// Convert to standard normal and then scale
const x = Math.sqrt(2) * inverseErf(2 * p - 1);
return mean + stdDev * x;
}
};
/**
* Binomial distribution functions
*/
probability.binomial = {
/**
* Calculate the probability mass function (PMF) of a binomial distribution
* @param {number} k - Number of successes
* @param {number} n - Number of trials
* @param {number} p - Probability of success in a single trial
* @returns {number} - The PMF value
*/
pmf(k, n, p) {
if (k < 0 || k > n) {
return 0;
}
if (p < 0 || p > 1) {
throw new Error('Probability must be between 0 and 1');
}
const binomialCoeff = utils.binomial(n, k);
return binomialCoeff * p ** k * (1 - p) ** (n - k);
},
/**
* Calculate the cumulative distribution function (CDF) of a binomial distribution
* @param {number} k - Number of successes
* @param {number} n - Number of trials
* @param {number} p - Probability of success in a single trial
* @returns {number} - The CDF value
*/
cdf(k, n, p) {
if (k < 0) {
return 0;
}
if (k >= n) {
return 1;
}
if (p < 0 || p > 1) {
throw new Error('Probability must be between 0 and 1');
}
let sum = 0;
for (let i = 0; i <= k; i++) {
sum += this.pmf(i, n, p);
}
return sum;
},
/**
* Calculate the mean of a binomial distribution
* @param {number} n - Number of trials
* @param {number} p - Probability of success in a single trial
* @returns {number} - The mean
*/
mean(n, p) {
return n * p;
},
/**
* Calculate the variance of a binomial distribution
* @param {number} n - Number of trials
* @param {number} p - Probability of success in a single trial
* @returns {number} - The variance
*/
variance(n, p) {
return n * p * (1 - p);
},
/**
* Generate a random sample from a binomial distribution
* @param {number} n - Number of trials
* @param {number} p - Probability of success in a single trial
* @returns {number} - A random sample
*/
sample(n, p) {
let successes = 0;
for (let i = 0; i < n; i++) {
if (Math.random() < p) {
successes++;
}
}
return successes;
}
};
/**
* Poisson distribution functions
*/
probability.poisson = {
/**
* Calculate the probability mass function (PMF) of a Poisson distribution
* @param {number} k - Number of events
* @param {number} lambda - Average number of events in the interval
* @returns {number} - The PMF value
*/
pmf(k, lambda) {
if (k < 0 || !Number.isInteger(k)) {
return 0;
}
if (lambda <= 0) {
throw new Error('Lambda must be positive');
}
const numerator = lambda ** k * Math.exp(-lambda);
const denominator = utils.factorial(k);
return numerator / denominator;
},
/**
* Calculate the cumulative distribution function (CDF) of a Poisson distribution
* @param {number} k - Number of events
* @param {number} lambda - Average number of events in the interval
* @returns {number} - The CDF value
*/
cdf(k, lambda) {
if (k < 0) {
return 0;
}
if (lambda <= 0) {
throw new Error('Lambda must be positive');
}
let sum = 0;
const kFloor = Math.floor(k);
for (let i = 0; i <= kFloor; i++) {
sum += this.pmf(i, lambda);
}
return sum;
},
/**
* Calculate the mean of a Poisson distribution
* @param {number} lambda - Average number of events in the interval
* @returns {number} - The mean
*/
mean(lambda) {
return lambda;
},
/**
* Calculate the variance of a Poisson distribution
* @param {number} lambda - Average number of events in the interval
* @returns {number} - The variance
*/
variance(lambda) {
return lambda;
},
/**
* Generate a random sample from a Poisson distribution
* @param {number} lambda - Average number of events in the interval
* @returns {number} - A random sample
*/
sample(lambda) {
if (lambda <= 0) {
throw new Error('Lambda must be positive');
}
// Knuth's algorithm
const L = Math.exp(-lambda);
let k = 0;
let p = 1;
do {
k++;
p *= Math.random();
} while (p > L);
return k - 1;
}
};
/**
* Exponential distribution functions
*/
probability.exponential = {
/**
* Calculate the probability density function (PDF) of an exponential distribution
* @param {number} x - The value to calculate the PDF at
* @param {number} lambda - Rate parameter
* @returns {number} - The PDF value
*/
pdf(x, lambda) {
if (x < 0) {
return 0;
}
if (lambda <= 0) {
throw new Error('Lambda must be positive');
}
return lambda * Math.exp(-lambda * x);
},
/**
* Calculate the cumulative distribution function (CDF) of an exponential distribution
* @param {number} x - The value to calculate the CDF at
* @param {number} lambda - Rate parameter
* @returns {number} - The CDF value
*/
cdf(x, lambda) {
if (x < 0) {
return 0;
}
if (lambda <= 0) {
throw new Error('Lambda must be positive');
}
return 1 - Math.exp(-lambda * x);
},
/**
* Calculate the mean of an exponential distribution
* @param {number} lambda - Rate parameter
* @returns {number} - The mean
*/
mean(lambda) {
if (lambda <= 0) {
throw new Error('Lambda must be positive');
}
return 1 / lambda;
},
/**
* Calculate the variance of an exponential distribution
* @param {number} lambda - Rate parameter
* @returns {number} - The variance
*/
variance(lambda) {
if (lambda <= 0) {
throw new Error('Lambda must be positive');
}
return 1 / (lambda * lambda);
},
/**
* Generate a random sample from an exponential distribution
* @param {number} lambda - Rate parameter
* @returns {number} - A random sample
*/
sample(lambda) {
if (lambda <= 0) {
throw new Error('Lambda must be positive');
}
// Inverse transform sampling
const u = Math.random();
return -Math.log(1 - u) / lambda;
},
/**
* Calculate the inverse CDF (quantile function) of an exponential distribution
* @param {number} p - Probability value (between 0 and 1)
* @param {number} lambda - Rate parameter
* @returns {number} - The value x such that CDF(x) = p
*/
inverse(p, lambda) {
if (p < 0 || p >= 1) {
throw new Error('Probability must be between 0 and 1');
}
if (lambda <= 0) {
throw new Error('Lambda must be positive');
}
return -Math.log(1 - p) / lambda;
}
};
/**
* Uniform distribution functions
*/
probability.uniform = {
/**
* Calculate the probability density function (PDF) of a uniform distribution
* @param {number} x - The value to calculate the PDF at
* @param {number} a - Lower bound
* @param {number} b - Upper bound
* @returns {number} - The PDF value
*/
pdf(x, a, b) {
if (a >= b) {
throw new Error('Upper bound must be greater than lower bound');
}
if (x < a || x > b) {
return 0;
}
return 1 / (b - a);
},
/**
* Calculate the cumulative distribution function (CDF) of a uniform distribution
* @param {number} x - The value to calculate the CDF at
* @param {number} a - Lower bound
* @param {number} b - Upper bound
* @returns {number} - The CDF value
*/
cdf(x, a, b) {
if (a >= b) {
throw new Error('Upper bound must be greater than lower bound');
}
if (x < a) {
return 0;
}
if (x > b) {
return 1;
}
return (x - a) / (b - a);
},
/**
* Calculate the mean of a uniform distribution
* @param {number} a - Lower bound
* @param {number} b - Upper bound
* @returns {number} - The mean
*/
mean(a, b) {
if (a >= b) {
throw new Error('Upper bound must be greater than lower bound');
}
return (a + b) / 2;
},
/**
* Calculate the variance of a uniform distribution
* @param {number} a - Lower bound
* @param {number} b - Upper bound
* @returns {number} - The variance
*/
variance(a, b) {
if (a >= b) {
throw new Error('Upper bound must be greater than lower bound');
}
return (b - a) ** 2 / 12;
},
/**
* Generate a random sample from a uniform distribution
* @param {number} a - Lower bound
* @param {number} b - Upper bound
* @returns {number} - A random sample
*/
sample(a, b) {
if (a >= b) {
throw new Error('Upper bound must be greater than lower bound');
}
return a + Math.random() * (b - a);
},
/**
* Calculate the inverse CDF (quantile function) of a uniform distribution
* @param {number} p - Probability value (between 0 and 1)
* @param {number} a - Lower bound
* @param {number} b - Upper bound
* @returns {number} - The value x such that CDF(x) = p
*/
inverse(p, a, b) {
if (p < 0 || p > 1) {
throw new Error('Probability must be between 0 and 1');
}
if (a >= b) {
throw new Error('Upper bound must be greater than lower bound');
}
return a + p * (b - a);
}
};
module.exports = probability;