function normalPDF(z, mu, sigma) {
return Math.exp(-0.5 * ((z - mu) / sigma) ** 2) / (sigma * Math.sqrt(2 * Math.PI));
}
function simpson(f, lo, hi, n) {
const h = (hi - lo) / n;
let s = f(lo) + f(hi);
for (let i = 1; i < n; i++) {
s += (i % 2 === 0 ? 2 : 4) * f(lo + i * h);
}
return (s * h) / 3;
}
function logit(x) {
return Math.log(x / (1 - x));
}
function normalQuantile(p) {
const a = [-3.969683028665376e+01, 2.209460984245205e+02, -2.759285104469687e+02, 1.383577518672690e+02, -3.066479806614716e+01, 2.506628277459239e+00];
const b = [-5.447609879822406e+01, 1.615858368580409e+02, -1.556989798598866e+02, 6.680131188771972e+01, -1.328068155288572e+01];
const c = [-7.784894002430293e-03, -3.223964580411365e-01, -2.400758277161838e+00, -2.549732539343734e+00, 4.374664141464968e+00, 2.938163982698783e+00];
const d = [7.784695709041462e-03, 3.224671290700398e-01, 2.445134137142996e+00, 3.754408661907416e+00];
const plow = 0.02425;
const phigh = 1 - plow;
if (p < plow) {
const q = Math.sqrt(-2 * Math.log(p));
return (((((c[0]*q+c[1])*q+c[2])*q+c[3])*q+c[4])*q+c[5]) /
((((d[0]*q+d[1])*q+d[2])*q+d[3])*q+1);
} else if (p <= phigh) {
const q = p - 0.5;
const r = q * q;
return (((((a[0]*r+a[1])*r+a[2])*r+a[3])*r+a[4])*r+a[5])*q /
(((((b[0]*r+b[1])*r+b[2])*r+b[3])*r+b[4])*r+1);
} else {
const q = Math.sqrt(-2 * Math.log(1 - p));
return -(((((c[0]*q+c[1])*q+c[2])*q+c[3])*q+c[4])*q+c[5]) /
((((d[0]*q+d[1])*q+d[2])*q+d[3])*q+1);
}
}
function lgamma(x) {
const g = 7;
const c = [
0.99999999999980993, 676.5203681218851, -1259.1392167224028,
771.32342877765313, -176.61502916214059, 12.507343278686905,
-0.13857109526572012, 9.9843695780195716e-6, 1.5056327351493116e-7
];
if (x < 0.5) {
return Math.log(Math.PI / Math.sin(Math.PI * x)) - lgamma(1 - x);
}
x -= 1;
let a = c[0];
const t = x + g + 0.5;
for (let i = 1; i < g + 2; i++) {
a += c[i] / (x + i);
}
return 0.5 * Math.log(2 * Math.PI) + (x + 0.5) * Math.log(t) - t + Math.log(a);
}
function naiveMean(theta, b, a) {
return 0.5 + a * (theta - b);
}
function naiveDensity(x, theta, b, a, sigma) {
return normalPDF(x, naiveMean(theta, b, a), sigma);
}
function betaShape1(theta, delta, tau) {
return Math.exp((theta - delta + tau) / 2);
}
function betaShape2(theta, delta, tau) {
return Math.exp((-theta + delta + tau) / 2);
}
function betaIrtMean(theta, delta) {
return 1 / (1 + Math.exp(-(theta - delta)));
}
function betaPDF(x, a, b) {
if (x <= 0 || x >= 1) return 0;
const logB = lgamma(a) + lgamma(b) - lgamma(a + b);
return Math.exp((a - 1) * Math.log(x) + (b - 1) * Math.log(1 - x) - logB);
}
function betaIrtDensity(x, theta, delta, tau) {
return betaPDF(x, betaShape1(theta, delta, tau), betaShape2(theta, delta, tau));
}
function samejimaMean(theta, alpha, b, sigma) {
const loc = alpha * (theta - b);
return simpson((z) => (1 / (1 + Math.exp(-z))) * normalPDF(z, loc, sigma), loc - 6 * sigma, loc + 6 * sigma, 60);
}
function samejimaDensity(x, theta, alpha, b, sigma) {
if (x <= 0 || x >= 1) return 0;
return normalPDF(logit(x), alpha * (theta - b), sigma) / (x * (1 - x));
}
function muellerUnnorm(x, theta, b, lambda) {
return Math.exp(x * (theta - b) + x * (1 - x) * lambda);
}
function muellerNormalizer(theta, b, lambda) {
return simpson((x) => muellerUnnorm(x, theta, b, lambda), 0, 1, 100);
}
function muellerMean(theta, b, lambda) {
return simpson((x) => x * muellerUnnorm(x, theta, b, lambda), 0, 1, 100) / muellerNormalizer(theta, b, lambda);
}
function muellerDensity(x, theta, b, lambda) {
if (x <= 0 || x >= 1) return 0;
return muellerUnnorm(x, theta, b, lambda) / muellerNormalizer(theta, b, lambda);
}
model_colors = ({
naive_linear: "#808080",
beta_irt: "#2166ac",
samejima_crm: "#d73027",
muller_corsm: "#4daf4a"
})
model_labels = ({
naive_linear: "Naive linear",
beta_irt: "Beta IRT",
samejima_crm: "Samejima CRM",
muller_corsm: "Mueller CRSM"
})
theta_grid = d3.range(-3, 3.01, 0.1)
fixed_nl_sigma = 0.15
fixed_sam_sigma = 1
fixed_mu_lambda = 2