some fun with testAll
.
This commit is contained in:
parent
6b15698d4e
commit
45c6eec7da
|
@ -1,8 +1,5 @@
|
|||
open Jest
|
||||
open Expect
|
||||
open FastCheck
|
||||
// open Arbitrary
|
||||
open Property.Sync
|
||||
|
||||
let env: DistributionOperation.env = {
|
||||
sampleCount: 100,
|
||||
|
@ -11,8 +8,6 @@ let env: DistributionOperation.env = {
|
|||
|
||||
let mkNormal = (mean, stdev) => GenericDist_Types.Symbolic(#Normal({mean: mean, stdev: stdev}))
|
||||
let normalDist5: GenericDist_Types.genericDist = mkNormal(5.0, 2.0)
|
||||
let normalDist10: GenericDist_Types.genericDist = mkNormal(10.0, 2.0)
|
||||
let normalDist20: GenericDist_Types.genericDist = mkNormal(20.0, 2.0)
|
||||
let uniformDist: GenericDist_Types.genericDist = Symbolic(#Uniform({low: 9.0, high: 10.0}))
|
||||
|
||||
let {toFloat, toDist, toString, toError} = module(DistributionOperation.Output)
|
||||
|
@ -23,112 +18,6 @@ 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
|
||||
|
||||
describe("normalize", () => {
|
||||
test("has no impact on normal dist", () => {
|
||||
let result = run(FromDist(ToDist(Normalize), normalDist5))
|
||||
expect(result)->toEqual(Dist(normalDist5))
|
||||
})
|
||||
|
||||
// Test is vapid while I figure out how to get jest to work with fast-check
|
||||
// monitor situation here maybe https://github.com/TheSpyder/rescript-fast-check/issues/8 ?
|
||||
test("all normals are already normalized", () => {
|
||||
expect(assert_(
|
||||
property2(
|
||||
Arbitrary.double(()),
|
||||
Arbitrary.double(()),
|
||||
(mean, stdev) => {
|
||||
// open! Expect.Operators
|
||||
open GenericDist_Types.Operation
|
||||
run(FromDist(ToDist(Normalize), mkNormal(mean, stdev))) == DistributionOperation.Dist(mkNormal(mean, stdev))
|
||||
}
|
||||
)
|
||||
)) -> toEqual(())
|
||||
})
|
||||
})
|
||||
|
||||
describe("mean", () => {
|
||||
test("of a normal distribution", () => { // should be property
|
||||
run(FromDist(ToFloat(#Mean), normalDist5)) -> unpackFloat -> expect -> toBeCloseTo(5.0)
|
||||
})
|
||||
|
||||
test("of an exponential distribution at a small rate", () => { // should be property
|
||||
let rate = 1e-7
|
||||
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
|
||||
})
|
||||
|
||||
test("of an exponential distribution at a larger rate", () => {
|
||||
let rate = 10.0
|
||||
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
|
||||
})
|
||||
|
||||
// test("of a cauchy distribution", () => {
|
||||
// let result = run(FromDist(ToFloat(#Mean), GenericDist_Types.Symbolic(#Cauchy({local: 1.0, scale: 1.0}))))
|
||||
// expect(result) -> toEqual(Error("Cauchy distributions may have no mean value."))
|
||||
// })
|
||||
|
||||
test("of a triangular distribution", () => { // should be property
|
||||
let theMean = run(FromDist(
|
||||
ToFloat(#Mean),
|
||||
GenericDist_Types.Symbolic(#Triangular({low: - 5.0, medium: 1e-3, high: 10.0}))
|
||||
))
|
||||
theMean -> unpackFloat -> expect -> toBeCloseTo((-5.0 +. 1e-3 +. 10.0) /. 3.0) // https://www.statology.org/triangular-distribution/
|
||||
})
|
||||
|
||||
test("of a beta distribution with alpha much smaller than beta", () => { // should be property
|
||||
let theMean = run(FromDist(
|
||||
ToFloat(#Mean),
|
||||
GenericDist_Types.Symbolic(#Beta({alpha: 2e-4, beta: 64.0}))
|
||||
))
|
||||
theMean -> unpackFloat -> expect -> toBeCloseTo(1.0 /. (1.0 +. (64.0 /. 2e-4))) // https://en.wikipedia.org/wiki/Beta_distribution#Mean
|
||||
})
|
||||
|
||||
test("of a beta distribution with alpha much larger than beta", () => { // should be property
|
||||
let theMean = run(FromDist(
|
||||
ToFloat(#Mean),
|
||||
GenericDist_Types.Symbolic(#Beta({alpha: 128.0, beta: 1.0}))
|
||||
))
|
||||
theMean -> unpackFloat -> expect -> toBeCloseTo(1.0 /. (1.0 +. (1.0 /. 128.0))) // https://en.wikipedia.org/wiki/Beta_distribution#Mean
|
||||
})
|
||||
|
||||
test("of a lognormal", () => { // should be property
|
||||
let theMean = run(FromDist(
|
||||
ToFloat(#Mean),
|
||||
GenericDist_Types.Symbolic(#Lognormal({mu: 2.0, sigma: 4.0}))
|
||||
))
|
||||
theMean -> unpackFloat -> expect -> toBeCloseTo(Js.Math.exp(2.0 +. 4.0 ** 2.0 /. 2.0 )) // https://brilliant.org/wiki/log-normal-distribution/
|
||||
})
|
||||
|
||||
test("of a uniform", () => {
|
||||
let theMean = run(FromDist(
|
||||
ToFloat(#Mean),
|
||||
GenericDist_Types.Symbolic(#Uniform({low: 1e-5, high: 12.345}))
|
||||
))
|
||||
theMean -> unpackFloat -> expect -> toBeCloseTo((1e-5 +. 12.345) /. 2.0) // https://en.wikipedia.org/wiki/Continuous_uniform_distribution#Moments
|
||||
})
|
||||
|
||||
test("of a float", () => {
|
||||
let theMean = run(FromDist(
|
||||
ToFloat(#Mean),
|
||||
GenericDist_Types.Symbolic(#Float(7.7))
|
||||
))
|
||||
theMean -> unpackFloat -> expect -> toBeCloseTo(7.7)
|
||||
})
|
||||
})
|
||||
|
||||
describe("mixture", () => {
|
||||
test("on two normal distributions", () => {
|
||||
let result =
|
||||
run(Mixture([(normalDist10, 0.5), (normalDist20, 0.5)]))
|
||||
->outputMap(FromDist(ToFloat(#Mean)))
|
||||
->toFloat
|
||||
->toExt
|
||||
expect(result)->toBeCloseTo(15.28)
|
||||
})
|
||||
})
|
||||
|
||||
describe("toPointSet", () => {
|
||||
test("on symbolic normal distribution", () => {
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
open Jest
|
||||
open Expect
|
||||
|
||||
let env: DistributionOperation.env = {
|
||||
sampleCount: 1000,
|
||||
xyPointLength: 100,
|
||||
}
|
||||
|
||||
let {toFloat, toDist, toString, toError} = module(DistributionOperation.Output)
|
||||
let {run} = module(DistributionOperation)
|
||||
let {fmap} = module(DistributionOperation.Output)
|
||||
let run = 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 mkBeta = (alpha, beta) => GenericDist_Types.Symbolic(#Beta({alpha: alpha, beta: beta}))
|
||||
let mkExponential = rate => GenericDist_Types.Symbolic(#Exponential({rate: rate}))
|
||||
|
||||
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 = {
|
||||
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?
|
||||
})
|
||||
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 = {
|
||||
run(Mixture(
|
||||
[
|
||||
(mkBeta(alpha, beta), 0.25),
|
||||
(mkExponential(rate), 0.75)
|
||||
]
|
||||
)) -> outputMap(FromDist(ToFloat(#Mean)))
|
||||
}
|
||||
theMean
|
||||
-> unpackFloat
|
||||
-> expect
|
||||
-> toBeSoCloseTo(
|
||||
0.25 *. 1.0 /. (1.0 +. beta /. alpha) +. 0.75 *. 1.0 /. rate,
|
||||
~digits=-1
|
||||
)
|
||||
}
|
||||
)
|
||||
})
|
||||
|
145
packages/squiggle-lang/__tests__/Distributions/Symbolic_test.res
Normal file
145
packages/squiggle-lang/__tests__/Distributions/Symbolic_test.res
Normal file
|
@ -0,0 +1,145 @@
|
|||
open Jest
|
||||
open Expect
|
||||
|
||||
let pdfImage = (thePdf, inps) => Js.Array.map(thePdf, inps)
|
||||
|
||||
let env: DistributionOperation.env = {
|
||||
sampleCount: 100,
|
||||
xyPointLength: 100,
|
||||
}
|
||||
|
||||
let mkNormal = (mean, stdev) => GenericDist_Types.Symbolic(#Normal({mean: mean, stdev: stdev}))
|
||||
let {toFloat, toDist, toString, toError, fmap} = module(DistributionOperation.Output)
|
||||
let {run} = module(DistributionOperation)
|
||||
let run = 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("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
|
||||
-> unpackDist
|
||||
-> expect
|
||||
-> toEqual(theNormal)
|
||||
})
|
||||
})
|
||||
|
||||
describe("mean", () => {
|
||||
testAll("of normal distributions", list{-1e8, -16.0, -1e-2, 0.0, 1e-4, 32.0, 1e16}, mean => {
|
||||
run(FromDist(ToFloat(#Mean), mkNormal(mean, 4.0)))
|
||||
-> unpackFloat
|
||||
-> expect
|
||||
-> toBeCloseTo(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
|
||||
})
|
||||
|
||||
// test("of a cauchy distribution", () => {
|
||||
// let result = run(FromDist(ToFloat(#Mean), GenericDist_Types.Symbolic(#Cauchy({local: 1.0, scale: 1.0}))))
|
||||
// expect(result) -> toEqual(Error("Cauchy distributions may have no mean value."))
|
||||
// })
|
||||
|
||||
test("of a triangular distribution", () => { // should be property
|
||||
let theMean = run(FromDist(
|
||||
ToFloat(#Mean),
|
||||
GenericDist_Types.Symbolic(#Triangular({low: - 5.0, medium: 1e-3, high: 10.0}))
|
||||
))
|
||||
theMean
|
||||
-> unpackFloat
|
||||
-> expect
|
||||
-> toBeCloseTo((-5.0 +. 1e-3 +. 10.0) /. 3.0) // https://www.statology.org/triangular-distribution/
|
||||
})
|
||||
|
||||
test("of a beta distribution with alpha much smaller than beta", () => { // should be property
|
||||
let theMean = run(FromDist(
|
||||
ToFloat(#Mean),
|
||||
GenericDist_Types.Symbolic(#Beta({alpha: 2e-4, beta: 64.0}))
|
||||
))
|
||||
theMean
|
||||
-> unpackFloat
|
||||
-> expect
|
||||
-> toBeCloseTo(1.0 /. (1.0 +. (64.0 /. 2e-4))) // https://en.wikipedia.org/wiki/Beta_distribution#Mean
|
||||
})
|
||||
|
||||
test("of a beta distribution with alpha much larger than beta", () => { // should be property
|
||||
let theMean = run(FromDist(
|
||||
ToFloat(#Mean),
|
||||
GenericDist_Types.Symbolic(#Beta({alpha: 128.0, beta: 1.0}))
|
||||
))
|
||||
theMean
|
||||
-> unpackFloat
|
||||
-> expect
|
||||
-> toBeCloseTo(1.0 /. (1.0 +. (1.0 /. 128.0))) // https://en.wikipedia.org/wiki/Beta_distribution#Mean
|
||||
})
|
||||
|
||||
test("of a lognormal", () => { // should be property
|
||||
let theMean = run(FromDist(
|
||||
ToFloat(#Mean),
|
||||
GenericDist_Types.Symbolic(#Lognormal({mu: 2.0, sigma: 4.0}))
|
||||
))
|
||||
theMean
|
||||
-> unpackFloat
|
||||
-> expect
|
||||
-> toBeCloseTo(Js.Math.exp(2.0 +. 4.0 ** 2.0 /. 2.0 )) // https://brilliant.org/wiki/log-normal-distribution/
|
||||
})
|
||||
|
||||
test("of a uniform", () => {
|
||||
let theMean = run(FromDist(
|
||||
ToFloat(#Mean),
|
||||
GenericDist_Types.Symbolic(#Uniform({low: 1e-5, high: 12.345}))
|
||||
))
|
||||
theMean
|
||||
-> unpackFloat
|
||||
-> expect
|
||||
-> toBeCloseTo((1e-5 +. 12.345) /. 2.0) // https://en.wikipedia.org/wiki/Continuous_uniform_distribution#Moments
|
||||
})
|
||||
|
||||
test("of a float", () => {
|
||||
let theMean = run(FromDist(
|
||||
ToFloat(#Mean),
|
||||
GenericDist_Types.Symbolic(#Float(7.7))
|
||||
))
|
||||
theMean -> unpackFloat -> expect -> toBeCloseTo(7.7)
|
||||
})
|
||||
})
|
||||
|
||||
describe("Normal distribution with sparklines", () => {
|
||||
|
||||
let parameterWiseAdditionHelper = (n1: SymbolicDistTypes.normal, n2: SymbolicDistTypes.normal) => {
|
||||
let normalDistAtSumMeanConstr = SymbolicDist.Normal.add(n1, n2)
|
||||
let normalDistAtSumMean: SymbolicDistTypes.normal = switch normalDistAtSumMeanConstr {
|
||||
| #Normal(params) => params
|
||||
}
|
||||
x => SymbolicDist.Normal.pdf(x, normalDistAtSumMean)
|
||||
}
|
||||
|
||||
let normalDistAtMean5: SymbolicDistTypes.normal = {mean: 5.0, stdev: 2.0}
|
||||
let normalDistAtMean10: SymbolicDistTypes.normal = {mean: 10.0, stdev: 2.0}
|
||||
let range20Float = E.A.rangeFloat(0, 20) // [0.0,1.0,2.0,3.0,4.0,...19.0,]
|
||||
|
||||
let pdfNormalDistAtMean5 = x => SymbolicDist.Normal.pdf(x, normalDistAtMean5)
|
||||
let sparklineMean5 = pdfImage(pdfNormalDistAtMean5, range20Float)
|
||||
test("mean=5", () => {
|
||||
Sparklines.create(sparklineMean5, ())
|
||||
-> expect
|
||||
-> toEqual(`▁▂▃▅███▅▃▂▁▁▁▁▁▁▁▁▁▁▁`)
|
||||
})
|
||||
let sparklineMean15 = normalDistAtMean5 -> parameterWiseAdditionHelper(normalDistAtMean10) -> pdfImage(range20Float)
|
||||
test("parameter-wise addition of two normal distributions", () => {
|
||||
Sparklines.create(sparklineMean15, ())
|
||||
-> expect
|
||||
-> toEqual(`▁▁▁▁▁▁▁▁▁▁▂▃▅▇███▇▅▃▂`)
|
||||
})
|
||||
})
|
|
@ -1,33 +0,0 @@
|
|||
open Jest
|
||||
open Expect
|
||||
open Js.Array
|
||||
open SymbolicDist
|
||||
|
||||
let makeTest = (~only=false, str, item1, item2) =>
|
||||
only
|
||||
? Only.test(str, () => expect(item1) -> toEqual(item2))
|
||||
: test(str, () => expect(item1) -> toEqual(item2))
|
||||
|
||||
let pdfImage = (thePdf, inps) => map(thePdf, inps)
|
||||
|
||||
let parameterWiseAdditionHelper = (n1: SymbolicDistTypes.normal, n2: SymbolicDistTypes.normal) => {
|
||||
let normalDistAtSumMeanConstr = Normal.add(n1, n2)
|
||||
let normalDistAtSumMean: SymbolicDistTypes.normal = switch normalDistAtSumMeanConstr {
|
||||
| #Normal(params) => params
|
||||
}
|
||||
x => Normal.pdf(x, normalDistAtSumMean)
|
||||
}
|
||||
|
||||
describe("Normal distribution with sparklines", () => {
|
||||
|
||||
let normalDistAtMean5: SymbolicDistTypes.normal = {mean: 5.0, stdev: 2.0}
|
||||
let normalDistAtMean10: SymbolicDistTypes.normal = {mean: 10.0, stdev: 2.0}
|
||||
let range20Float = E.A.rangeFloat(0, 20) // [0.0,1.0,2.0,3.0,4.0,...19.0,]
|
||||
|
||||
let pdfNormalDistAtMean5 = x => Normal.pdf(x, normalDistAtMean5)
|
||||
let sparklineMean5 = pdfImage(pdfNormalDistAtMean5, range20Float)
|
||||
makeTest("mean=5", Sparklines.create(sparklineMean5, ()), `▁▂▃▅███▅▃▂▁▁▁▁▁▁▁▁▁▁▁`)
|
||||
|
||||
let sparklineMean15 = normalDistAtMean5 -> parameterWiseAdditionHelper(normalDistAtMean10) -> pdfImage(range20Float)
|
||||
makeTest("parameter-wise addition of two normal distributions", Sparklines.create(sparklineMean15, ()), `▁▁▁▁▁▁▁▁▁▁▂▃▅▇███▇▅▃▂`)
|
||||
})
|
Loading…
Reference in New Issue
Block a user