CR comments from #192

This commit is contained in:
Quinn Dougherty 2022-04-07 18:38:49 -04:00
parent d582e29e8b
commit 72be08a516
5 changed files with 78 additions and 76 deletions

View File

@ -1,19 +1,8 @@
open Jest open Jest
open Expect open Expect
open TestHelpers
let env: DistributionOperation.env = { // TODO: use Normal.make (etc.), but preferably after the new validation dispatch is in.
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
let mkNormal = (mean, stdev) => GenericDist_Types.Symbolic(#Normal({mean: mean, stdev: stdev})) 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 mkBeta = (alpha, beta) => GenericDist_Types.Symbolic(#Beta({alpha: alpha, beta: beta}))
let mkExponential = rate => GenericDist_Types.Symbolic(#Exponential({rate: rate})) 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", () => { describe("mixture", () => {
testAll("fair mean of two normal distributions", list{(0.0, 1e2), (-1e1, -1e-4), (-1e1, 1e2), (-1e1, 1e1)}, tup => { // should be property 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 (mean1, mean2) = tup
let theMean = { let meanValue = {
run(Mixture([(mkNormal(mean1, 9e-1), 0.5), (mkNormal(mean2, 9e-1), 0.5)])) run(Mixture([(mkNormal(mean1, 9e-1), 0.5), (mkNormal(mean2, 9e-1), 0.5)]))
-> outputMap(FromDist(ToFloat(#Mean))) -> 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( testAll(
"weighted mean of a beta and an exponential", "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. // 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)}, list{((128.0, 1.0), 2.0), ((2e-1, 64.0), 16.0), ((1e0, 1e0), 64.0)},
tup => { tup => {
let (betaParams, rate) = tup let ((alpha, beta), rate) = tup
let (alpha, beta) = betaParams let betaWeight = 0.25
let theMean = { let exponentialWeight = 0.75
let meanValue = {
run(Mixture( run(Mixture(
[ [
(mkBeta(alpha, beta), 0.25), (mkBeta(alpha, beta), betaWeight),
(mkExponential(rate), 0.75) (mkExponential(rate), exponentialWeight)
] ]
)) -> outputMap(FromDist(ToFloat(#Mean))) )) -> outputMap(FromDist(ToFloat(#Mean)))
} }
theMean let betaMean = 1.0 /. (1.0 +. beta /. alpha)
let exponentialMean = 1.0 /. rate
meanValue
-> unpackFloat -> unpackFloat
-> expect -> expect
-> toBeSoCloseTo( -> toBeSoCloseTo(
0.25 *. 1.0 /. (1.0 +. beta /. alpha) +. 0.75 *. 1.0 /. rate, betaWeight *. betaMean +. exponentialWeight *. exponentialMean,
~digits=-1 ~digits=-1
) )
} }
@ -59,17 +51,19 @@ describe("mixture", () => {
// Would not survive property tests: very easy to find cases that NaN out. // 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))}, list{((-1e2,1e1), (2e0,1e0)), ((-1e-16,1e-16), (1e-8,1e0)), ((0.0,1e0), (1e0,1e-2))},
tup => { tup => {
let (uniformParams, lognormalParams) = tup let ((low, high), (mu, sigma)) = tup
let (low, high) = uniformParams let uniformWeight = 0.6
let (mu, sigma) = lognormalParams let lognormalWeight = 0.4
let theMean = { let meanValue = {
run(Mixture([(mkUniform(low, high), 0.6), (mkLognormal(mu, sigma), 0.4)])) run(Mixture([(mkUniform(low, high), uniformWeight), (mkLognormal(mu, sigma), lognormalWeight)]))
-> outputMap(FromDist(ToFloat(#Mean))) -> outputMap(FromDist(ToFloat(#Mean)))
} }
theMean let uniformMean = (low +. high) /. 2.0
let lognormalMean = mu +. sigma ** 2.0 /. 2.0
meanValue
-> unpackFloat -> unpackFloat
-> expect -> 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)
} }
) )
}) })

View File

@ -1,19 +1,14 @@
open Jest open Jest
open Expect open TestHelpers
let makeTest = (~only=false, str, item1, item2) =>
only
? Only.test(str, () => expect(item1) -> toEqual(item2))
: test(str, () => expect(item1) -> toEqual(item2))
describe("Continuous and discrete splits", () => { describe("Continuous and discrete splits", () => {
makeTest( makeTest(
"check splits one", "splits (1)",
SampleSet.Internals.T.splitContinuousAndDiscrete([1.432, 1.33455, 2.0]), SampleSet.Internals.T.splitContinuousAndDiscrete([1.432, 1.33455, 2.0]),
([1.432, 1.33455, 2.0], E.FloatFloatMap.empty()), ([1.432, 1.33455, 2.0], E.FloatFloatMap.empty()),
) )
makeTest( makeTest(
"check splits two", "splits (2)",
SampleSet.Internals.T.splitContinuousAndDiscrete([ SampleSet.Internals.T.splitContinuousAndDiscrete([
1.432, 1.432,
1.33455, 1.33455,

View File

@ -1,34 +1,18 @@
open Jest open Jest
open Expect open Expect
open TestHelpers
let fnImage = (theFn, inps) => Js.Array.map(theFn, inps) // TODO: use Normal.make (but preferably after teh new validation dispatch is in)
let env: DistributionOperation.env = {
sampleCount: 100,
xyPointLength: 100,
}
let mkNormal = (mean, stdev) => GenericDist_Types.Symbolic(#Normal({mean: mean, stdev: stdev})) 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> => float = E.O.toExt(
"Should be impossible to reach (This error is in test file)",
)
let toExtDist: option<GenericDist_Types.genericDist> => 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", () => { describe("(Symbolic) normalize", () => {
testAll("has no impact on normal distributions", list{-1e8, -16.0, -1e-2, 0.0, 1e-4, 32.0, 1e16}, mean => { testAll("has no impact on normal distributions", list{-1e8, -1e-2, 0.0, 1e-4, 1e16}, mean => {
let theNormal = mkNormal(mean, 2.0) let normalValue = mkNormal(mean, 2.0)
let theNormalized = run(FromDist(ToDist(Normalize), theNormal)) let normalizedValue = run(FromDist(ToDist(Normalize), normalValue))
theNormalized normalizedValue
-> unpackDist -> unpackDist
-> expect -> 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 => { 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})))) let meanValue = 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 meanValue -> unpackFloat -> expect -> toBeCloseTo(1.0 /. rate) // https://en.wikipedia.org/wiki/Exponential_distribution#Mean,_variance,_moments,_and_median
}) })
test("of a cauchy distribution", () => { test("of a cauchy distribution", () => {
let theMean = run(FromDist(ToFloat(#Mean), GenericDist_Types.Symbolic(#Cauchy({local: 1.0, scale: 1.0})))) let meanValue = run(FromDist(ToFloat(#Mean), GenericDist_Types.Symbolic(#Cauchy({local: 1.0, scale: 1.0}))))
theMean meanValue
-> unpackFloat -> unpackFloat
-> expect -> expect
-> toBeCloseTo(2.01868297874546) -> 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 => { 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 (low, medium, high) = tup
let theMean = run(FromDist( let meanValue = run(FromDist(
ToFloat(#Mean), ToFloat(#Mean),
GenericDist_Types.Symbolic(#Triangular({low: low, medium: medium, high: high})) GenericDist_Types.Symbolic(#Triangular({low: low, medium: medium, high: high}))
)) ))
theMean meanValue
-> unpackFloat -> unpackFloat
-> expect -> expect
-> toBeCloseTo((low +. medium +. high) /. 3.0) // https://www.statology.org/triangular-distribution/ -> 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. // 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 => { 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 (alpha, beta) = tup
let theMean = run(FromDist( let meanValue = run(FromDist(
ToFloat(#Mean), ToFloat(#Mean),
GenericDist_Types.Symbolic(#Beta({alpha: alpha, beta: beta})) GenericDist_Types.Symbolic(#Beta({alpha: alpha, beta: beta}))
)) ))
theMean meanValue
-> unpackFloat -> unpackFloat
-> expect -> expect
-> toBeCloseTo(1.0 /. (1.0 +. (beta /. alpha))) // https://en.wikipedia.org/wiki/Beta_distribution#Mean -> 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. // 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)", () => { test("of beta(0, 0)", () => {
let theMean = run(FromDist( let meanValue = run(FromDist(
ToFloat(#Mean), ToFloat(#Mean),
GenericDist_Types.Symbolic(#Beta({alpha: 0.0, beta: 0.0})) GenericDist_Types.Symbolic(#Beta({alpha: 0.0, beta: 0.0}))
)) ))
theMean meanValue
-> unpackFloat -> unpackFloat
-> expect -> expect
-> ExpectJs.toBeFalsy -> 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 => { 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 (mu, sigma) = tup
let theMean = run(FromDist( let meanValue = run(FromDist(
ToFloat(#Mean), ToFloat(#Mean),
GenericDist_Types.Symbolic(#Lognormal({mu: mu, sigma: sigma})) GenericDist_Types.Symbolic(#Lognormal({mu: mu, sigma: sigma}))
)) ))
theMean meanValue
-> unpackFloat -> unpackFloat
-> expect -> expect
-> toBeCloseTo(Js.Math.exp(mu +. sigma ** 2.0 /. 2.0 )) // https://brilliant.org/wiki/log-normal-distribution/ -> 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 => { testAll("of uniform distributions", list{(1e-5, 12.345), (-1e4, 1e4), (-1e16, -1e2), (5.3e3, 9e9)}, tup => {
let (low, high) = tup let (low, high) = tup
let theMean = run(FromDist( let meanValue = run(FromDist(
ToFloat(#Mean), ToFloat(#Mean),
GenericDist_Types.Symbolic(#Uniform({low: low, high: high})) GenericDist_Types.Symbolic(#Uniform({low: low, high: high}))
)) ))
theMean meanValue
-> unpackFloat -> unpackFloat
-> expect -> expect
-> toBeCloseTo((low +. high) /. 2.0) // https://en.wikipedia.org/wiki/Continuous_uniform_distribution#Moments -> toBeCloseTo((low +. high) /. 2.0) // https://en.wikipedia.org/wiki/Continuous_uniform_distribution#Moments
}) })
test("of a float", () => { test("of a float", () => {
let theMean = run(FromDist( let meanValue = run(FromDist(
ToFloat(#Mean), ToFloat(#Mean),
GenericDist_Types.Symbolic(#Float(7.7)) GenericDist_Types.Symbolic(#Float(7.7))
)) ))
theMean -> unpackFloat -> expect -> toBeCloseTo(7.7) meanValue -> unpackFloat -> expect -> toBeCloseTo(7.7)
}) })
}) })

View File

@ -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> => float = E.O.toExt(unreachableInTestFileMessage)
let toExtDist: option<GenericDist_Types.genericDist> => 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

View File

@ -5,4 +5,7 @@ module.exports = {
setupFilesAfterEnv: [ setupFilesAfterEnv: [
"<rootdir>/../../node_modules/bisect_ppx/src/runtime/js/jest.bs.js" "<rootdir>/../../node_modules/bisect_ppx/src/runtime/js/jest.bs.js"
], ],
testPathIgnorePatterns: [
"__tests__/TestHelpers.bs.js"
],
}; };