6/6 tasks done

This commit is contained in:
Quinn Dougherty 2022-04-13 19:17:49 -04:00
parent 7251f5864a
commit 24fe66c9d3
2 changed files with 89 additions and 83 deletions

View File

@ -1,15 +1,65 @@
/*
This is the most basic file in our invariants family of tests.
See document in https://github.com/quantified-uncertainty/squiggle/pull/238 for details
Validate that the addition of means equals the mean of the addition, similar for subtraction and multiplication.
Note: digits parameter should be higher than -4.
Details in https://deploy-preview-251--squiggle-documentation.netlify.app/docs/internal/invariants/
Note: epsilon of 1e3 means the invariants are, in general, not being satisfied.
*/
open Jest
open Expect
open TestHelpers
module Internals = {
let epsilon = 1e3
let mean = GenericDist_Types.Constructors.UsingDists.mean
let expectImpossiblePath: string => assertion = algebraicOp =>
`${algebraicOp} has`->expect->toEqual("failed")
let distributions = list{
normalMake(0.0, 1e0),
betaMake(2e0, 4e0),
exponentialMake(1.234e0),
uniformMake(7e0, 1e1),
// cauchyMake(1e0, 1e0),
lognormalMake(1e0, 1e0),
triangularMake(1e0, 1e1, 5e1),
Ok(floatMake(1e1)),
}
let pairsOfDifferentDistributions = E.L.combinations2(distributions)
let runMean: DistributionTypes.genericDist => float = dist => {
dist->mean->run->toFloat->E.O2.toExn("Shouldn't see this because we trust testcase input")
}
let testOperationMean = (
distOp: (DistributionTypes.genericDist, DistributionTypes.genericDist) => result<DistributionTypes.genericDist, DistributionTypes.error>,
description: string,
floatOp: (float, float) => float,
dist1': SymbolicDistTypes.symbolicDist,
dist2': SymbolicDistTypes.symbolicDist,
~epsilon: float
) => {
let dist1 = dist1'->DistributionTypes.Symbolic // ->DistributionTypes.Other
let dist2 = dist2'->DistributionTypes.Symbolic // ->DistributionTypes.Other
let received =
distOp(dist1, dist2)
->E.R2.fmap(mean)
->E.R2.fmap(run)
->E.R2.fmap(toFloat)
->E.R.toExn
let expected = floatOp(runMean(dist1), runMean(dist2))
switch received {
| None => expectImpossiblePath(description)
| Some(x) => expectErrorToBeBounded(x, expected, ~epsilon=epsilon)
}
}
}
let {
algebraicAdd,
algebraicMultiply,
@ -26,115 +76,61 @@ let algebraicSubtract = algebraicSubtract(~env)
let algebraicLogarithm = algebraicLogarithm(~env)
let algebraicPower = algebraicPower(~env)
describe("Mean", () => {
let digits = -4
let {testOperationMean, distributions, pairsOfDifferentDistributions, epsilon} = module(Internals)
let mean = GenericDist_Types.Constructors.UsingDists.mean
describe("Means invariant", () => {
let runMean: result<DistributionTypes.genericDist, DistributionTypes.error> => float = distR => {
distR
->E.R2.fmap(mean)
->E.R2.fmap(run)
->E.R2.fmap(toFloat)
->E.R.toExn
->E.O2.toExn("Shouldn't see this because we trust testcase input")
}
describe("for addition", () => {
let testAdditionMean = testOperationMean(algebraicAdd, "algebraicAdd", \"+.", ~epsilon=epsilon)
let impossiblePath: string => assertion = algebraicOp =>
`${algebraicOp} has`->expect->toEqual("failed")
testAll("of two of the same distribution", distributions, dist => {
E.R.liftM2(testAdditionMean, dist, dist) -> E.R.toExn
})
let distributions = list{
normalMake(0.0, 1e0),
betaMake(2e0, 4e0),
exponentialMake(1.234e0),
uniformMake(7e0, 1e1),
// cauchyMake(1e0, 1e0),
lognormalMake(1e0, 1e0),
triangularMake(1e0, 1e1, 5e1),
Ok(floatMake(1e1)),
}
let combinations = E.L.combinations2(distributions)
let zipDistsDists = E.L.zip(distributions, distributions)
let testOperationMean = (
distOp: (DistributionTypes.genericDist, DistributionTypes.genericDist) => result<DistributionTypes.genericDist, DistributionTypes.error>,
description: string,
floatOp: (float, float) => float,
dist1': result<SymbolicDistTypes.symbolicDist, string>,
dist2': result<SymbolicDistTypes.symbolicDist, string>
) => {
let dist1 = dist1'->E.R2.fmap(x=>DistributionTypes.Symbolic(x))->E.R2.fmap2(s=>DistributionTypes.Other(s))
let dist2 = dist2'->E.R2.fmap(x=>DistributionTypes.Symbolic(x))->E.R2.fmap2(s=>DistributionTypes.Other(s))
let received =
E.R.liftJoin2(distOp, dist1, dist2)
->E.R2.fmap(mean)
->E.R2.fmap(run)
->E.R2.fmap(toFloat)
let expected = floatOp(runMean(dist1), runMean(dist2))
switch received {
| Error(err) => impossiblePath(description)
| Ok(x) =>
switch x {
| None => impossiblePath(description)
| Some(x) => x->expect->toBeSoCloseTo(expected, ~digits)
}
}
}
describe("addition", () => {
let testAdditionMean = testOperationMean(algebraicAdd, "algebraicAdd", \"+.")
testAll("homogeneous addition", zipDistsDists, dists => {
testAll("of two different distributions", pairsOfDifferentDistributions, dists => {
let (dist1, dist2) = dists
testAdditionMean(dist1, dist2)
E.R.liftM2(testAdditionMean, dist1, dist2) -> E.R.toExn
})
testAll("heterogeneous addition (1)", combinations, dists => {
testAll("of two difference distributions", pairsOfDifferentDistributions, dists => {
let (dist1, dist2) = dists
testAdditionMean(dist1, dist2)
E.R.liftM2(testAdditionMean, dist2, dist1) -> E.R.toExn
})
})
testAll("heterogeneous addition (commuted of 1 (or; 2))", combinations, dists => {
describe("for subtraction", () => {
let testSubtractionMean = testOperationMean(algebraicSubtract, "algebraicSubtract", \"-.", ~epsilon=epsilon)
testAll("of two of the same distribution", distributions, dist => {
E.R.liftM2(testSubtractionMean, dist, dist) -> E.R.toExn
})
testAll("of two different distributions", pairsOfDifferentDistributions, dists => {
let (dist1, dist2) = dists
testAdditionMean(dist2, dist1)
})
E.R.liftM2(testSubtractionMean, dist1, dist2) -> E.R.toExn
})
describe("subtraction", () => {
let testSubtractionMean = testOperationMean(algebraicSubtract, "algebraicSubtract", \"-.")
testAll("homogeneous subtraction", zipDistsDists, dists => {
testAll("of two different distributions", pairsOfDifferentDistributions, dists => {
let (dist1, dist2) = dists
testSubtractionMean(dist1, dist2)
E.R.liftM2(testSubtractionMean, dist2, dist1) -> E.R.toExn
})
})
testAll("heterogeneous subtraction (1)", combinations, dists => {
describe("for multiplication", () => {
let testMultiplicationMean = testOperationMean(algebraicMultiply, "algebraicMultiply", \"*.", ~epsilon=epsilon)
testAll("of two of the same distribution", distributions, dist => {
E.R.liftM2(testMultiplicationMean, dist, dist) -> E.R.toExn
})
testAll("of two different distributions", pairsOfDifferentDistributions, dists => {
let (dist1, dist2) = dists
testSubtractionMean(dist1, dist2)
E.R.liftM2(testMultiplicationMean, dist1, dist2) -> E.R.toExn
})
testAll("heterogeneous subtraction (commuted of 1 (or; 2))", combinations, dists => {
testAll("of two different distributions", pairsOfDifferentDistributions, dists => {
let (dist1, dist2) = dists
testSubtractionMean(dist2, dist1)
})
})
describe("multiplication", () => {
let testMultiplicationMean = testOperationMean(algebraicMultiply, "algebraicMultiply", \"*.")
testAll("homogeneous subtraction", zipDistsDists, dists => {
let (dist1, dist2) = dists
testMultiplicationMean(dist1, dist2)
})
testAll("heterogeneoous subtraction (1)", combinations, dists => {
let (dist1, dist2) = dists
testMultiplicationMean(dist1, dist2)
})
testAll("heterogeneoous subtraction (commuted of 1 (or; 2))", combinations, dists => {
let (dist1, dist2) = dists
testMultiplicationMean(dist2, dist1)
E.R.liftM2(testMultiplicationMean, dist2, dist1) -> E.R.toExn
})
})
})

View File

@ -1,6 +1,16 @@
open Jest
open Expect
let expectErrorToBeBounded = (received, expected, ~epsilon) => {
let distance = Js.Math.abs_float(received -. expected)
let error = if expected < epsilon ** 2.5 {
distance /. epsilon
} else {
distance /. Js.Math.abs_float(expected)
}
error -> expect -> toBeLessThan(epsilon)
}
let makeTest = (~only=false, str, item1, item2) =>
only
? Only.test(str, () => expect(item1)->toEqual(item2))