From 72be08a516bf8d85d38e4c247fe933959cf4b38f Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Thu, 7 Apr 2022 18:38:49 -0400 Subject: [PATCH] CR comments from #192 --- .../__tests__/Distributions/Mixture_test.res | 52 +++++++--------- .../__tests__/Distributions/Samples_test.res | 11 +--- .../__tests__/Distributions/Symbolic_test.res | 62 +++++++------------ .../squiggle-lang/__tests__/TestHelpers.res | 26 ++++++++ packages/squiggle-lang/jest.config.js | 3 + 5 files changed, 78 insertions(+), 76 deletions(-) create mode 100644 packages/squiggle-lang/__tests__/TestHelpers.res diff --git a/packages/squiggle-lang/__tests__/Distributions/Mixture_test.res b/packages/squiggle-lang/__tests__/Distributions/Mixture_test.res index ece229be..2ab1de08 100644 --- a/packages/squiggle-lang/__tests__/Distributions/Mixture_test.res +++ b/packages/squiggle-lang/__tests__/Distributions/Mixture_test.res @@ -1,19 +1,8 @@ open Jest open Expect +open TestHelpers -let env: DistributionOperation.env = { - sampleCount: 10000, - xyPointLength: 1000, -} - -let {toFloat, toDist, toString, toError, fmap} = module(DistributionOperation.Output) -let run = DistributionOperation.run(~env) -let outputMap = fmap(~env) -let toExt: option<'a> => 'a = E.O.toExt( - "Should be impossible to reach (This error is in test file)", -) -let unpackFloat = x => x -> toFloat -> toExt - +// TODO: use Normal.make (etc.), but preferably after the new validation dispatch is in. let mkNormal = (mean, stdev) => GenericDist_Types.Symbolic(#Normal({mean: mean, stdev: stdev})) let mkBeta = (alpha, beta) => GenericDist_Types.Symbolic(#Beta({alpha: alpha, beta: beta})) let mkExponential = rate => GenericDist_Types.Symbolic(#Exponential({rate: rate})) @@ -24,32 +13,35 @@ let mkLognormal = (mu, sigma) => GenericDist_Types.Symbolic(#Lognormal({mu: mu, describe("mixture", () => { testAll("fair mean of two normal distributions", list{(0.0, 1e2), (-1e1, -1e-4), (-1e1, 1e2), (-1e1, 1e1)}, tup => { // should be property let (mean1, mean2) = tup - let theMean = { + let meanValue = { run(Mixture([(mkNormal(mean1, 9e-1), 0.5), (mkNormal(mean2, 9e-1), 0.5)])) -> outputMap(FromDist(ToFloat(#Mean))) } - theMean -> unpackFloat -> expect -> toBeSoCloseTo((mean1 +. mean2) /. 2.0, ~digits=-1) // the .56 is arbitrary? should be 15.0 with a looser tolerance? + meanValue -> unpackFloat -> expect -> toBeSoCloseTo((mean1 +. mean2) /. 2.0, ~digits=-1) }) testAll( "weighted mean of a beta and an exponential", // This would not survive property testing, it was easy for me to find cases that NaN'd out. list{((128.0, 1.0), 2.0), ((2e-1, 64.0), 16.0), ((1e0, 1e0), 64.0)}, tup => { - let (betaParams, rate) = tup - let (alpha, beta) = betaParams - let theMean = { + let ((alpha, beta), rate) = tup + let betaWeight = 0.25 + let exponentialWeight = 0.75 + let meanValue = { run(Mixture( [ - (mkBeta(alpha, beta), 0.25), - (mkExponential(rate), 0.75) + (mkBeta(alpha, beta), betaWeight), + (mkExponential(rate), exponentialWeight) ] )) -> outputMap(FromDist(ToFloat(#Mean))) } - theMean + let betaMean = 1.0 /. (1.0 +. beta /. alpha) + let exponentialMean = 1.0 /. rate + meanValue -> unpackFloat -> expect -> toBeSoCloseTo( - 0.25 *. 1.0 /. (1.0 +. beta /. alpha) +. 0.75 *. 1.0 /. rate, + betaWeight *. betaMean +. exponentialWeight *. exponentialMean, ~digits=-1 ) } @@ -59,17 +51,19 @@ describe("mixture", () => { // Would not survive property tests: very easy to find cases that NaN out. list{((-1e2,1e1), (2e0,1e0)), ((-1e-16,1e-16), (1e-8,1e0)), ((0.0,1e0), (1e0,1e-2))}, tup => { - let (uniformParams, lognormalParams) = tup - let (low, high) = uniformParams - let (mu, sigma) = lognormalParams - let theMean = { - run(Mixture([(mkUniform(low, high), 0.6), (mkLognormal(mu, sigma), 0.4)])) + let ((low, high), (mu, sigma)) = tup + let uniformWeight = 0.6 + let lognormalWeight = 0.4 + let meanValue = { + run(Mixture([(mkUniform(low, high), uniformWeight), (mkLognormal(mu, sigma), lognormalWeight)])) -> outputMap(FromDist(ToFloat(#Mean))) } - theMean + let uniformMean = (low +. high) /. 2.0 + let lognormalMean = mu +. sigma ** 2.0 /. 2.0 + meanValue -> unpackFloat -> expect - -> toBeSoCloseTo(0.6 *. (low +. high) /. 2.0 +. 0.4 *. (mu +. sigma ** 2.0 /. 2.0), ~digits=-1) + -> toBeSoCloseTo(uniformWeight *. uniformMean +. lognormalWeight *. lognormalMean, ~digits=-1) } ) }) diff --git a/packages/squiggle-lang/__tests__/Distributions/Samples_test.res b/packages/squiggle-lang/__tests__/Distributions/Samples_test.res index 170ede1c..6da9efe3 100644 --- a/packages/squiggle-lang/__tests__/Distributions/Samples_test.res +++ b/packages/squiggle-lang/__tests__/Distributions/Samples_test.res @@ -1,19 +1,14 @@ open Jest -open Expect - -let makeTest = (~only=false, str, item1, item2) => - only - ? Only.test(str, () => expect(item1) -> toEqual(item2)) - : test(str, () => expect(item1) -> toEqual(item2)) +open TestHelpers describe("Continuous and discrete splits", () => { makeTest( - "check splits one", + "splits (1)", SampleSet.Internals.T.splitContinuousAndDiscrete([1.432, 1.33455, 2.0]), ([1.432, 1.33455, 2.0], E.FloatFloatMap.empty()), ) makeTest( - "check splits two", + "splits (2)", SampleSet.Internals.T.splitContinuousAndDiscrete([ 1.432, 1.33455, diff --git a/packages/squiggle-lang/__tests__/Distributions/Symbolic_test.res b/packages/squiggle-lang/__tests__/Distributions/Symbolic_test.res index c2df8674..59ab2a8b 100644 --- a/packages/squiggle-lang/__tests__/Distributions/Symbolic_test.res +++ b/packages/squiggle-lang/__tests__/Distributions/Symbolic_test.res @@ -1,34 +1,18 @@ open Jest open Expect +open TestHelpers -let fnImage = (theFn, inps) => Js.Array.map(theFn, inps) - -let env: DistributionOperation.env = { - sampleCount: 100, - xyPointLength: 100, -} - +// TODO: use Normal.make (but preferably after teh new validation dispatch is in) let mkNormal = (mean, stdev) => GenericDist_Types.Symbolic(#Normal({mean: mean, stdev: stdev})) -let {toFloat, toDist, toString, toError, fmap} = module(DistributionOperation.Output) -let run = DistributionOperation.run(~env) -let outputMap = fmap(~env) -let toExtFloat: option => float = E.O.toExt( - "Should be impossible to reach (This error is in test file)", -) -let toExtDist: option => GenericDist_Types.genericDist = E.O.toExt( - "Should be impossible to reach (This error is in a test file)", -) -let unpackFloat = x => x -> toFloat -> toExtFloat -let unpackDist = y => y -> toDist -> toExtDist describe("(Symbolic) normalize", () => { - testAll("has no impact on normal distributions", list{-1e8, -16.0, -1e-2, 0.0, 1e-4, 32.0, 1e16}, mean => { - let theNormal = mkNormal(mean, 2.0) - let theNormalized = run(FromDist(ToDist(Normalize), theNormal)) - theNormalized + testAll("has no impact on normal distributions", list{-1e8, -1e-2, 0.0, 1e-4, 1e16}, mean => { + let normalValue = mkNormal(mean, 2.0) + let normalizedValue = run(FromDist(ToDist(Normalize), normalValue)) + normalizedValue -> unpackDist -> expect - -> toEqual(theNormal) + -> toEqual(normalValue) }) }) @@ -55,13 +39,13 @@ describe("(Symbolic) mean", () => { }) testAll("of exponential distributions", list{1e-7, 2.0, 10.0, 100.0}, rate => { - let theMean = run(FromDist(ToFloat(#Mean), GenericDist_Types.Symbolic(#Exponential({rate: rate})))) - theMean -> unpackFloat -> expect -> toBeCloseTo(1.0 /. rate) // https://en.wikipedia.org/wiki/Exponential_distribution#Mean,_variance,_moments,_and_median + let meanValue = run(FromDist(ToFloat(#Mean), GenericDist_Types.Symbolic(#Exponential({rate: rate})))) + meanValue -> unpackFloat -> expect -> toBeCloseTo(1.0 /. rate) // https://en.wikipedia.org/wiki/Exponential_distribution#Mean,_variance,_moments,_and_median }) test("of a cauchy distribution", () => { - let theMean = run(FromDist(ToFloat(#Mean), GenericDist_Types.Symbolic(#Cauchy({local: 1.0, scale: 1.0})))) - theMean + let meanValue = run(FromDist(ToFloat(#Mean), GenericDist_Types.Symbolic(#Cauchy({local: 1.0, scale: 1.0})))) + meanValue -> unpackFloat -> expect -> toBeCloseTo(2.01868297874546) @@ -70,11 +54,11 @@ describe("(Symbolic) mean", () => { testAll("of triangular distributions", list{(1.0,2.0,3.0), (-1e7,-1e-7,1e-7), (-1e-7,1e0,1e7), (-1e-16,0.0,1e-16)}, tup => { let (low, medium, high) = tup - let theMean = run(FromDist( + let meanValue = run(FromDist( ToFloat(#Mean), GenericDist_Types.Symbolic(#Triangular({low: low, medium: medium, high: high})) )) - theMean + meanValue -> unpackFloat -> expect -> toBeCloseTo((low +. medium +. high) /. 3.0) // https://www.statology.org/triangular-distribution/ @@ -83,11 +67,11 @@ describe("(Symbolic) mean", () => { // TODO: nonpositive inputs are SUPPOSED to crash. testAll("of beta distributions", list{(1e-4, 6.4e1), (1.28e2, 1e0), (1e-16, 1e-16), (1e16, 1e16), (-1e4, 1e1), (1e1, -1e4)}, tup => { let (alpha, beta) = tup - let theMean = run(FromDist( + let meanValue = run(FromDist( ToFloat(#Mean), GenericDist_Types.Symbolic(#Beta({alpha: alpha, beta: beta})) )) - theMean + meanValue -> unpackFloat -> expect -> toBeCloseTo(1.0 /. (1.0 +. (beta /. alpha))) // https://en.wikipedia.org/wiki/Beta_distribution#Mean @@ -95,11 +79,11 @@ describe("(Symbolic) mean", () => { // TODO: When we have our theory of validators we won't want this to be NaN but to be an error. test("of beta(0, 0)", () => { - let theMean = run(FromDist( + let meanValue = run(FromDist( ToFloat(#Mean), GenericDist_Types.Symbolic(#Beta({alpha: 0.0, beta: 0.0})) )) - theMean + meanValue -> unpackFloat -> expect -> ExpectJs.toBeFalsy @@ -107,11 +91,11 @@ describe("(Symbolic) mean", () => { testAll("of lognormal distributions", list{(2.0, 4.0), (1e-7, 1e-2), (-1e6, 10.0), (1e3, -1e2), (-1e8, -1e4), (1e2, 1e-5)}, tup => { let (mu, sigma) = tup - let theMean = run(FromDist( + let meanValue = run(FromDist( ToFloat(#Mean), GenericDist_Types.Symbolic(#Lognormal({mu: mu, sigma: sigma})) )) - theMean + meanValue -> unpackFloat -> expect -> toBeCloseTo(Js.Math.exp(mu +. sigma ** 2.0 /. 2.0 )) // https://brilliant.org/wiki/log-normal-distribution/ @@ -119,22 +103,22 @@ describe("(Symbolic) mean", () => { testAll("of uniform distributions", list{(1e-5, 12.345), (-1e4, 1e4), (-1e16, -1e2), (5.3e3, 9e9)}, tup => { let (low, high) = tup - let theMean = run(FromDist( + let meanValue = run(FromDist( ToFloat(#Mean), GenericDist_Types.Symbolic(#Uniform({low: low, high: high})) )) - theMean + meanValue -> unpackFloat -> expect -> toBeCloseTo((low +. high) /. 2.0) // https://en.wikipedia.org/wiki/Continuous_uniform_distribution#Moments }) test("of a float", () => { - let theMean = run(FromDist( + let meanValue = run(FromDist( ToFloat(#Mean), GenericDist_Types.Symbolic(#Float(7.7)) )) - theMean -> unpackFloat -> expect -> toBeCloseTo(7.7) + meanValue -> unpackFloat -> expect -> toBeCloseTo(7.7) }) }) diff --git a/packages/squiggle-lang/__tests__/TestHelpers.res b/packages/squiggle-lang/__tests__/TestHelpers.res new file mode 100644 index 00000000..a61f57d0 --- /dev/null +++ b/packages/squiggle-lang/__tests__/TestHelpers.res @@ -0,0 +1,26 @@ +open Jest +open Expect + +let makeTest = (~only=false, str, item1, item2) => + only + ? Only.test(str, () => expect(item1) -> toEqual(item2)) + : test(str, () => expect(item1) -> toEqual(item2)) + + +let {toFloat, toDist, toString, toError, fmap} = module(DistributionOperation.Output) + +let fnImage = (theFn, inps) => Js.Array.map(theFn, inps) + +let env: DistributionOperation.env = { + sampleCount: 100, + xyPointLength: 100, +} + +let run = DistributionOperation.run(~env) +let outputMap = fmap(~env) +let unreachableInTestFileMessage = "Should be impossible to reach (This error is in test file)" +let toExtFloat: option => float = E.O.toExt(unreachableInTestFileMessage) +let toExtDist: option => GenericDist_Types.genericDist = E.O.toExt(unreachableInTestFileMessage) +// let toExt: option<'a> => 'a = E.O.toExt(unreachableInTestFileMessage) +let unpackFloat = x => x -> toFloat -> toExtFloat +let unpackDist = y => y -> toDist -> toExtDist diff --git a/packages/squiggle-lang/jest.config.js b/packages/squiggle-lang/jest.config.js index 09bf05a8..f539ef30 100644 --- a/packages/squiggle-lang/jest.config.js +++ b/packages/squiggle-lang/jest.config.js @@ -5,4 +5,7 @@ module.exports = { setupFilesAfterEnv: [ "/../../node_modules/bisect_ppx/src/runtime/js/jest.bs.js" ], + testPathIgnorePatterns: [ + "__tests__/TestHelpers.bs.js" + ], };