From 3ff810ee1b5c12b5070d8ee10565915e860c5a9e Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Wed, 20 Apr 2022 00:50:46 -0400 Subject: [PATCH] commented out tests are now explained --- .../__tests__/TS/PointSet_test.ts | 49 ++++++++ .../__tests__/TS/SampleSet_test.ts | 118 +++++++----------- .../__tests__/TS/Symbolic_test.ts | 8 +- .../squiggle-lang/__tests__/TS/TestHelpers.ts | 21 +++- 4 files changed, 114 insertions(+), 82 deletions(-) create mode 100644 packages/squiggle-lang/__tests__/TS/PointSet_test.ts diff --git a/packages/squiggle-lang/__tests__/TS/PointSet_test.ts b/packages/squiggle-lang/__tests__/TS/PointSet_test.ts new file mode 100644 index 00000000..b519fc5f --- /dev/null +++ b/packages/squiggle-lang/__tests__/TS/PointSet_test.ts @@ -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( + "" + ); + 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; + } + } + ) + ); + }); +}); diff --git a/packages/squiggle-lang/__tests__/TS/SampleSet_test.ts b/packages/squiggle-lang/__tests__/TS/SampleSet_test.ts index 2588eb51..44f93317 100644 --- a/packages/squiggle-lang/__tests__/TS/SampleSet_test.ts +++ b/packages/squiggle-lang/__tests__/TS/SampleSet_test.ts @@ -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( -// "" -// ); -// 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`); -// } -// } -// ) -// ); -// }); // }); diff --git a/packages/squiggle-lang/__tests__/TS/Symbolic_test.ts b/packages/squiggle-lang/__tests__/TS/Symbolic_test.ts index 15edbf48..d2d7846a 100644 --- a/packages/squiggle-lang/__tests__/TS/Symbolic_test.ts +++ b/packages/squiggle-lang/__tests__/TS/Symbolic_test.ts @@ -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"; diff --git a/packages/squiggle-lang/__tests__/TS/TestHelpers.ts b/packages/squiggle-lang/__tests__/TS/TestHelpers.ts index 79dba36b..145c41af 100644 --- a/packages/squiggle-lang/__tests__/TS/TestHelpers.ts +++ b/packages/squiggle-lang/__tests__/TS/TestHelpers.ts @@ -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 + ); }