commented out tests are now explained

This commit is contained in:
Quinn Dougherty 2022-04-20 00:50:46 -04:00
parent 4f5a1ff946
commit 3ff810ee1b
4 changed files with 114 additions and 82 deletions

View File

@ -0,0 +1,49 @@
import { errorValueToString } from "../../src/js/index";
import { testRun, failDefault, expectErrorToBeBounded } from "./TestHelpers";
import * as fc from "fast-check";
describe("Mean of mixture is weighted average of means", () => {
test("mx(beta(a,b), lognormal(m,s), [x,y])", () => {
fc.assert(
fc.property(
fc.float({ min: 1e-1 }), // alpha
fc.float({ min: 1 }), // beta
fc.float(), // mu
fc.float({ min: 1e-1 }), // sigma
fc.float({ min: 1e-7 }),
fc.float({ min: 1e-7 }),
(a, b, m, s, x, y) => {
let squiggleString = `mean(mx(beta(${a},${b}), lognormal(${m},${s}), [${x}, ${y}]))`;
let res = testRun(squiggleString);
switch (res.tag) {
case "Error":
expect(errorValueToString(res.value)).toEqual(
"<I wonder if test cases will find this>"
);
break;
case "Ok":
let weightDenom = x + y;
let betaWeight = x / weightDenom;
let lognormalWeight = y / weightDenom;
let betaMean = 1 / (1 + b / a);
let lognormalMean = m + s ** 2 / 2;
if (res.value.tag == "number") {
expectErrorToBeBounded(
res.value.value,
betaWeight * betaMean + lognormalWeight * lognormalMean,
1,
-1
);
} else {
expect(res.value.value).toEqual("some error message");
}
break;
default:
failDefault();
break;
}
}
)
);
});
});

View File

@ -1,4 +1,5 @@
import { Distribution } from "../../src/js/index";
import { expectErrorToBeBounded, failDefault } from "./TestHelpers";
import * as fc from "fast-check";
// Beware: float64Array makes it appear in an infinite loop.
@ -126,80 +127,49 @@ describe("SampleSet: cdf", () => {
// });
// });
// This should be true, but I can't get it to work.
// describe("SampleSet: mean is mean", () => {
// test("mean(samples(xs)) sampling twice as widely as the input", () => {
// fc.assert(
// fc.property(
// fc.float64Array({ minLength: 10, maxLength: 100000 }),
// (xs) => {
// let ys = Array.from(xs);
// let n = ys.length;
// let dist = new Distribution(
// { tag: "SampleSet", value: ys },
// { sampleCount: 2 * n, xyPointLength: 4 * n }
// );
//
// expect(dist.mean().value).toBeCloseTo(
// ys.reduce((a, b) => a + b, 0.0) / n
// );
// }
// )
// );
// });
//
// test("mean(samples(xs)) sampling half as widely as the input", () => {
// fc.assert(
// fc.property(
// fc.float64Array({ minLength: 10, maxLength: 100000 }),
// (xs) => {
// let ys = Array.from(xs);
// let n = ys.length;
// let dist = new Distribution(
// { tag: "SampleSet", value: ys },
// { sampleCount: Math.floor(5 / 2), xyPointLength: 4 * n }
// );
//
// expect(dist.mean().value).toBeCloseTo(
// ys.reduce((a, b) => a + b, 0.0) / n
// );
// }
// )
// );
// });
// test("mean(samples(xs)) sampling twice as widely as the input", () => {
// fc.assert(
// fc.property(
// fc.float64Array({ minLength: 10, maxLength: 100000 }),
// (xs) => {
// let ys = Array.from(xs);
// let n = ys.length;
// let dist = new Distribution(
// { tag: "SampleSet", value: ys },
// { sampleCount: 2 * n, xyPointLength: 4 * n }
// );
// let mean = dist.mean()
// if (typeof mean.value == "number") {
// expectErrorToBeBounded(mean.value, ys.reduce((a, b) => a + b, 0.0) / n, 5e-1, 1)
// } else {
// failDefault()
// }
// }
// )
// );
// });
//
// test("mean(samples(xs)) sampling half as widely as the input", () => {
// fc.assert(
// fc.property(
// fc.float64Array({ minLength: 10, maxLength: 100000 }),
// (xs) => {
// let ys = Array.from(xs);
// let n = ys.length;
// let dist = new Distribution(
// { tag: "SampleSet", value: ys },
// { sampleCount: Math.floor(n / 2), xyPointLength: 4 * n }
// );
// let mean = dist.mean()
// if (typeof mean.value == "number") {
// expectErrorToBeBounded(mean.value, ys.reduce((a, b) => a + b, 0.0) / n, 5e-1, 1)
// } else {
// failDefault()
// }
// }
// )
// );
// });
// describe("Mean of mixture is weighted average of means", () => {
// test("mx(beta(a,b), lognormal(m,s), [x,y])", () => {
// fc.assert(
// fc.property(
// fc.float({ min: 1e-1 }), // alpha
// fc.float({ min: 1 }), // beta
// fc.float(), // mu
// fc.float({ min: 1e-1 }), // sigma
// fc.float({ min: 1e-7 }),
// fc.float({ min: 1e-7 }),
// (a, b, m, s, x, y) => {
// let squiggleString = `mean(mx(beta(${a},${b}), lognormal(${m},${s}), [${x}, ${y}]))`;
// let res = testRun(squiggleString);
// switch (res.tag) {
// case "Error":
// expect(errorValueToString(res.value)).toEqual(
// "<I wonder if test cases will find this>"
// );
// case "Ok":
// let betaWeight = x / (x + y);
// let lognormalWeight = y / (x + y);
// let betaMean = 1 / (1 + b / a);
// let lognormalMean = m + s ** 2 / 2;
// expect(res.value).toEqual({
// tag: "number",
// value: betaWeight * betaMean + lognormalWeight * lognormalMean,
// });
// default:
// expect("mean returned").toBe(`something other than a number`);
// }
// }
// )
// );
// });
// });

View File

@ -1,10 +1,4 @@
import {
run,
squiggleExpression,
errorValueToString,
errorValue,
result,
} from "../../src/js/index";
import { errorValueToString } from "../../src/js/index";
import { testRun } from "./TestHelpers";
import * as fc from "fast-check";

View File

@ -13,5 +13,24 @@ export function testRun(x: string): any {
}
export function failDefault() {
expect("be reached").toBe("codepath should never be");
expect("be reached").toBe("codepath should never");
}
/**
* This appears also in `TestHelpers.res`. According to https://www.math.net/percent-error, it computes
* absolute error when numerical stability concerns make me not want to compute relative error.
* */
export function expectErrorToBeBounded(
received: number,
expected: number,
epsilon: number,
digits: number
) {
let distance = Math.abs(received - expected);
let expectedAbs = Math.abs(expected);
let normalizingDenom = Math.max(expectedAbs, 1);
let error = distance / normalizingDenom;
expect(Math.round(10 ** digits * error) / 10 ** digits).toBeLessThanOrEqual(
epsilon
);
}