From e42ac0b58d14b68624596415f1652a02096cccad Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Wed, 6 Apr 2022 16:02:32 -0400 Subject: [PATCH 01/13] renamed `t` to `T` --- .../DistributionOperation__Test.res} | 0 .../__tests__/{Lodash__test.res => Lodash__Test.res} | 0 ...patch_BuiltIn_test.res => Reducer_Dispatch_BuiltIn_Test.res} | 0 ...{Reducer_MathJsEval_test.res => Reducer_MathJsEval_Test.res} | 0 ...educer_MathJsParse_test.res => Reducer_MathJsParse_Test.res} | 0 .../__tests__/Reducer/{Reducer_test.res => Reducer_Test.res} | 0 ...ribution_test.res => ReducerInterface_Distribution_Test.res} | 0 ...Value_test.res => ReducerInterface_ExpressionValue_Test.res} | 0 .../__tests__/{Samples__test.res => Samples__Test.res} | 0 .../__tests__/{Symbolic_test.res => Symbolic_Test.res} | 0 packages/squiggle-lang/package.json | 2 +- 11 files changed, 1 insertion(+), 1 deletion(-) rename packages/squiggle-lang/__tests__/{GenericDist/GenericOperation__Test.res => Distributions/DistributionOperation__Test.res} (100%) rename packages/squiggle-lang/__tests__/{Lodash__test.res => Lodash__Test.res} (100%) rename packages/squiggle-lang/__tests__/Reducer/Reducer_Dispatch/{Reducer_Dispatch_BuiltIn_test.res => Reducer_Dispatch_BuiltIn_Test.res} (100%) rename packages/squiggle-lang/__tests__/Reducer/Reducer_MathJs/{Reducer_MathJsEval_test.res => Reducer_MathJsEval_Test.res} (100%) rename packages/squiggle-lang/__tests__/Reducer/Reducer_MathJs/{Reducer_MathJsParse_test.res => Reducer_MathJsParse_Test.res} (100%) rename packages/squiggle-lang/__tests__/Reducer/{Reducer_test.res => Reducer_Test.res} (100%) rename packages/squiggle-lang/__tests__/ReducerInterface/{ReducerInterface_Distribution_test.res => ReducerInterface_Distribution_Test.res} (100%) rename packages/squiggle-lang/__tests__/ReducerInterface/{ReducerInterface_ExpressionValue_test.res => ReducerInterface_ExpressionValue_Test.res} (100%) rename packages/squiggle-lang/__tests__/{Samples__test.res => Samples__Test.res} (100%) rename packages/squiggle-lang/__tests__/{Symbolic_test.res => Symbolic_Test.res} (100%) diff --git a/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res b/packages/squiggle-lang/__tests__/Distributions/DistributionOperation__Test.res similarity index 100% rename from packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res rename to packages/squiggle-lang/__tests__/Distributions/DistributionOperation__Test.res diff --git a/packages/squiggle-lang/__tests__/Lodash__test.res b/packages/squiggle-lang/__tests__/Lodash__Test.res similarity index 100% rename from packages/squiggle-lang/__tests__/Lodash__test.res rename to packages/squiggle-lang/__tests__/Lodash__Test.res diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltIn_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltIn_Test.res similarity index 100% rename from packages/squiggle-lang/__tests__/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltIn_test.res rename to packages/squiggle-lang/__tests__/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltIn_Test.res diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_MathJs/Reducer_MathJsEval_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_MathJs/Reducer_MathJsEval_Test.res similarity index 100% rename from packages/squiggle-lang/__tests__/Reducer/Reducer_MathJs/Reducer_MathJsEval_test.res rename to packages/squiggle-lang/__tests__/Reducer/Reducer_MathJs/Reducer_MathJsEval_Test.res diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_MathJs/Reducer_MathJsParse_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_MathJs/Reducer_MathJsParse_Test.res similarity index 100% rename from packages/squiggle-lang/__tests__/Reducer/Reducer_MathJs/Reducer_MathJsParse_test.res rename to packages/squiggle-lang/__tests__/Reducer/Reducer_MathJs/Reducer_MathJsParse_Test.res diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Test.res similarity index 100% rename from packages/squiggle-lang/__tests__/Reducer/Reducer_test.res rename to packages/squiggle-lang/__tests__/Reducer/Reducer_Test.res diff --git a/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res b/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_Test.res similarity index 100% rename from packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res rename to packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_Test.res diff --git a/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_ExpressionValue_test.res b/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_ExpressionValue_Test.res similarity index 100% rename from packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_ExpressionValue_test.res rename to packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_ExpressionValue_Test.res diff --git a/packages/squiggle-lang/__tests__/Samples__test.res b/packages/squiggle-lang/__tests__/Samples__Test.res similarity index 100% rename from packages/squiggle-lang/__tests__/Samples__test.res rename to packages/squiggle-lang/__tests__/Samples__Test.res diff --git a/packages/squiggle-lang/__tests__/Symbolic_test.res b/packages/squiggle-lang/__tests__/Symbolic_Test.res similarity index 100% rename from packages/squiggle-lang/__tests__/Symbolic_test.res rename to packages/squiggle-lang/__tests__/Symbolic_Test.res diff --git a/packages/squiggle-lang/package.json b/packages/squiggle-lang/package.json index 459c3d2b..03983cd2 100644 --- a/packages/squiggle-lang/package.json +++ b/packages/squiggle-lang/package.json @@ -10,7 +10,7 @@ "test:reducer": "jest --testPathPattern '.*__tests__/Reducer.*'", "test": "jest", "test:watch": "jest --watchAll", - "coverage": "rm -f *.coverage; yarn clean; BISECT_ENABLE=yes yarn build; yarn test; ./node_modules/.bin/bisect-ppx-report html", + "coverage": "rm -f *.coverage; yarn clean; BISECT_ENABLE=yes yarn build; yarn test; bisect-ppx-report html", "all": "yarn build && yarn bundle && yarn test" }, "keywords": [ From 0a5a8a51982cb7dc5c5fda2f3228e49c4fd0cd94 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Wed, 6 Apr 2022 18:57:51 -0400 Subject: [PATCH 02/13] property test framework installed but not used; describe(means) section of unit tests filled out --- packages/squiggle-lang/README.md | 3 + .../DistributionOperation__Test.res | 97 +++++++++++++++++-- packages/squiggle-lang/bsconfig.json | 3 + packages/squiggle-lang/package.json | 1 + .../SymbolicDist/SymbolicDist.res | 8 +- yarn.lock | 19 ++++ 6 files changed, 121 insertions(+), 10 deletions(-) diff --git a/packages/squiggle-lang/README.md b/packages/squiggle-lang/README.md index a7d0d5d0..87bc76fe 100644 --- a/packages/squiggle-lang/README.md +++ b/packages/squiggle-lang/README.md @@ -13,6 +13,9 @@ Other: yarn start # listens to files and recompiles at every mutation yarn test yarn test:watch # keeps an active session and runs all tests at every mutation + +# where o := open in osx and o := xdg-open in linux, +yarn coverage; o _coverage/index.html # produces coverage report and opens it in browser ``` # TODO: clean up this README.md diff --git a/packages/squiggle-lang/__tests__/Distributions/DistributionOperation__Test.res b/packages/squiggle-lang/__tests__/Distributions/DistributionOperation__Test.res index dc456865..d0135cf1 100644 --- a/packages/squiggle-lang/__tests__/Distributions/DistributionOperation__Test.res +++ b/packages/squiggle-lang/__tests__/Distributions/DistributionOperation__Test.res @@ -1,14 +1,18 @@ open Jest open Expect +open FastCheck +// open Arbitrary +open Property.Sync let env: DistributionOperation.env = { sampleCount: 100, xyPointLength: 100, } -let normalDist5: GenericDist_Types.genericDist = Symbolic(#Normal({mean: 5.0, stdev: 2.0})) -let normalDist10: GenericDist_Types.genericDist = Symbolic(#Normal({mean: 10.0, stdev: 2.0})) -let normalDist20: GenericDist_Types.genericDist = Symbolic(#Normal({mean: 20.0, stdev: 2.0})) +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) @@ -19,18 +23,99 @@ 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("for a normal distribution", () => { - let result = DistributionOperation.run(~env, FromDist(ToFloat(#Mean), normalDist5)) - expect(result)->toEqual(Float(5.0)) + 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) }) }) diff --git a/packages/squiggle-lang/bsconfig.json b/packages/squiggle-lang/bsconfig.json index 4f38e124..9bd15e11 100644 --- a/packages/squiggle-lang/bsconfig.json +++ b/packages/squiggle-lang/bsconfig.json @@ -30,6 +30,9 @@ "rationale", "bisect_ppx" ], + "bs-dev-dependencies": [ + "rescript-fast-check" + ], "gentypeconfig": { "language": "typescript", "module": "commonjs", diff --git a/packages/squiggle-lang/package.json b/packages/squiggle-lang/package.json index 03983cd2..92554dd9 100644 --- a/packages/squiggle-lang/package.json +++ b/packages/squiggle-lang/package.json @@ -35,6 +35,7 @@ "docsify": "^4.12.2", "gentype": "^4.3.0", "jest": "^27.5.1", + "rescript-fast-check": "^1.1.1", "moduleserve": "0.9.1", "ts-jest": "^27.1.4", "ts-loader": "^9.2.8", diff --git a/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res b/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res index cd4132b3..626b8099 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res +++ b/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res @@ -55,7 +55,7 @@ module Exponential = { rate: rate, }), ) - : Error("Exponential distributions mean must be larger than 0") + : Error("Exponential distributions rate must be larger than 0.") let pdf = (x, t: t) => Jstat.Exponential.pdf(x, t.rate) let cdf = (x, t: t) => Jstat.Exponential.cdf(x, t.rate) let inv = (p, t: t) => Jstat.Exponential.inv(p, t.rate) @@ -71,7 +71,7 @@ module Cauchy = { let cdf = (x, t: t) => Jstat.Cauchy.cdf(x, t.local, t.scale) let inv = (p, t: t) => Jstat.Cauchy.inv(p, t.local, t.scale) let sample = (t: t) => Jstat.Cauchy.sample(t.local, t.scale) - let mean = (_: t) => Error("Cauchy distributions have no mean value.") + let mean = (_: t) => Error("Cauchy distributions may have no mean value.") let toString = ({local, scale}: t) => j`Cauchy($local, $scale)` } @@ -80,8 +80,8 @@ module Triangular = { let make = (low, medium, high): result => low < medium && medium < high ? Ok(#Triangular({low: low, medium: medium, high: high})) - : Error("Triangular values must be increasing order") - let pdf = (x, t: t) => Jstat.Triangular.pdf(x, t.low, t.high, t.medium) + : Error("Triangular values must be increasing order.") + let pdf = (x, t: t) => Jstat.Triangular.pdf(x, t.low, t.high, t.medium) // not obvious in jstat docs that high comes before medium? let cdf = (x, t: t) => Jstat.Triangular.cdf(x, t.low, t.high, t.medium) let inv = (p, t: t) => Jstat.Triangular.inv(p, t.low, t.high, t.medium) let sample = (t: t) => Jstat.Triangular.sample(t.low, t.high, t.medium) diff --git a/yarn.lock b/yarn.lock index 6d0a7619..8a0e9676 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8392,6 +8392,13 @@ extglob@^2.0.4: snapdragon "^0.8.1" to-regex "^3.0.1" +fast-check@^2.17.0: + version "2.24.0" + resolved "https://registry.yarnpkg.com/fast-check/-/fast-check-2.24.0.tgz#39f85586862108a4de6394c5196ebcf8b76b6c8b" + integrity sha512-iNXbN90lbabaCUfnW5jyXYPwMJLFYl09eJDkXA9ZoidFlBK63gNRvcKxv+8D1OJ1kIYjwBef4bO/K3qesUeWLQ== + dependencies: + pure-rand "^5.0.1" + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3, fast-deep-equal@~3.1.3: version "3.1.3" resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" @@ -13524,6 +13531,11 @@ pure-color@^1.2.0: resolved "https://registry.yarnpkg.com/pure-color/-/pure-color-1.3.0.tgz#1fe064fb0ac851f0de61320a8bf796836422f33e" integrity sha1-H+Bk+wrIUfDeYTIKi/eWg2Qi8z4= +pure-rand@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-5.0.1.tgz#97a287b4b4960b2a3448c0932bf28f2405cac51d" + integrity sha512-ksWccjmXOHU2gJBnH0cK1lSYdvSZ0zLoCMSz/nTGh6hDvCSgcRxDyIcOBD6KNxFz3xhMPm/T267Tbe2JRymKEQ== + q@^1.1.2: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" @@ -14712,6 +14724,13 @@ requires-port@^1.0.0: resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= +rescript-fast-check@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/rescript-fast-check/-/rescript-fast-check-1.1.1.tgz#ef153cb01254b2f01a738faf85b73327d423d5e1" + integrity sha512-wxeW0TsL/prkRvEYGbhEiLaLKmYJaECyDcKEWh65hDqP2i76lARzVW3QmYujSYK4OnjAC70dln3X6UC/2m2Huw== + dependencies: + fast-check "^2.17.0" + rescript@^9.1.4: version "9.1.4" resolved "https://registry.npmjs.org/rescript/-/rescript-9.1.4.tgz" From 6b15698d4e135937f794b168efcbe4178c8ce442 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Wed, 6 Apr 2022 19:38:54 -0400 Subject: [PATCH 03/13] replaced `*__Test.res` with `*_test.res` --- .../__tests__/{Bandwidth__Test.res => Bandwidth_test.res} | 0 ...ibutionOperation__Test.res => DistributionOperation__test.res} | 0 .../squiggle-lang/__tests__/{Lodash__Test.res => Lodash_test.res} | 0 ...ispatch_BuiltIn_Test.res => Reducer_Dispatch_BuiltIn_test.res} | 0 .../{Reducer_MathJsEval_Test.res => Reducer_MathJsEval_test.res} | 0 ...{Reducer_MathJsParse_Test.res => Reducer_MathJsParse_test.res} | 0 .../__tests__/Reducer/{Reducer_Test.res => Reducer_test.res} | 0 ...stribution_Test.res => ReducerInterface_Distribution_test.res} | 0 ...onValue_Test.res => ReducerInterface_ExpressionValue_test.res} | 0 .../__tests__/{Samples__Test.res => Samples_test.res} | 0 .../__tests__/{Symbolic_Test.res => Symbolic_test.res} | 0 .../__tests__/{XYShape__Test.res => XYShape_test.res} | 0 12 files changed, 0 insertions(+), 0 deletions(-) rename packages/squiggle-lang/__tests__/{Bandwidth__Test.res => Bandwidth_test.res} (100%) rename packages/squiggle-lang/__tests__/Distributions/{DistributionOperation__Test.res => DistributionOperation__test.res} (100%) rename packages/squiggle-lang/__tests__/{Lodash__Test.res => Lodash_test.res} (100%) rename packages/squiggle-lang/__tests__/Reducer/Reducer_Dispatch/{Reducer_Dispatch_BuiltIn_Test.res => Reducer_Dispatch_BuiltIn_test.res} (100%) rename packages/squiggle-lang/__tests__/Reducer/Reducer_MathJs/{Reducer_MathJsEval_Test.res => Reducer_MathJsEval_test.res} (100%) rename packages/squiggle-lang/__tests__/Reducer/Reducer_MathJs/{Reducer_MathJsParse_Test.res => Reducer_MathJsParse_test.res} (100%) rename packages/squiggle-lang/__tests__/Reducer/{Reducer_Test.res => Reducer_test.res} (100%) rename packages/squiggle-lang/__tests__/ReducerInterface/{ReducerInterface_Distribution_Test.res => ReducerInterface_Distribution_test.res} (100%) rename packages/squiggle-lang/__tests__/ReducerInterface/{ReducerInterface_ExpressionValue_Test.res => ReducerInterface_ExpressionValue_test.res} (100%) rename packages/squiggle-lang/__tests__/{Samples__Test.res => Samples_test.res} (100%) rename packages/squiggle-lang/__tests__/{Symbolic_Test.res => Symbolic_test.res} (100%) rename packages/squiggle-lang/__tests__/{XYShape__Test.res => XYShape_test.res} (100%) diff --git a/packages/squiggle-lang/__tests__/Bandwidth__Test.res b/packages/squiggle-lang/__tests__/Bandwidth_test.res similarity index 100% rename from packages/squiggle-lang/__tests__/Bandwidth__Test.res rename to packages/squiggle-lang/__tests__/Bandwidth_test.res diff --git a/packages/squiggle-lang/__tests__/Distributions/DistributionOperation__Test.res b/packages/squiggle-lang/__tests__/Distributions/DistributionOperation__test.res similarity index 100% rename from packages/squiggle-lang/__tests__/Distributions/DistributionOperation__Test.res rename to packages/squiggle-lang/__tests__/Distributions/DistributionOperation__test.res diff --git a/packages/squiggle-lang/__tests__/Lodash__Test.res b/packages/squiggle-lang/__tests__/Lodash_test.res similarity index 100% rename from packages/squiggle-lang/__tests__/Lodash__Test.res rename to packages/squiggle-lang/__tests__/Lodash_test.res diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltIn_Test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltIn_test.res similarity index 100% rename from packages/squiggle-lang/__tests__/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltIn_Test.res rename to packages/squiggle-lang/__tests__/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltIn_test.res diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_MathJs/Reducer_MathJsEval_Test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_MathJs/Reducer_MathJsEval_test.res similarity index 100% rename from packages/squiggle-lang/__tests__/Reducer/Reducer_MathJs/Reducer_MathJsEval_Test.res rename to packages/squiggle-lang/__tests__/Reducer/Reducer_MathJs/Reducer_MathJsEval_test.res diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_MathJs/Reducer_MathJsParse_Test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_MathJs/Reducer_MathJsParse_test.res similarity index 100% rename from packages/squiggle-lang/__tests__/Reducer/Reducer_MathJs/Reducer_MathJsParse_Test.res rename to packages/squiggle-lang/__tests__/Reducer/Reducer_MathJs/Reducer_MathJsParse_test.res diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_test.res similarity index 100% rename from packages/squiggle-lang/__tests__/Reducer/Reducer_Test.res rename to packages/squiggle-lang/__tests__/Reducer/Reducer_test.res diff --git a/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_Test.res b/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res similarity index 100% rename from packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_Test.res rename to packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res diff --git a/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_ExpressionValue_Test.res b/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_ExpressionValue_test.res similarity index 100% rename from packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_ExpressionValue_Test.res rename to packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_ExpressionValue_test.res diff --git a/packages/squiggle-lang/__tests__/Samples__Test.res b/packages/squiggle-lang/__tests__/Samples_test.res similarity index 100% rename from packages/squiggle-lang/__tests__/Samples__Test.res rename to packages/squiggle-lang/__tests__/Samples_test.res diff --git a/packages/squiggle-lang/__tests__/Symbolic_Test.res b/packages/squiggle-lang/__tests__/Symbolic_test.res similarity index 100% rename from packages/squiggle-lang/__tests__/Symbolic_Test.res rename to packages/squiggle-lang/__tests__/Symbolic_test.res diff --git a/packages/squiggle-lang/__tests__/XYShape__Test.res b/packages/squiggle-lang/__tests__/XYShape_test.res similarity index 100% rename from packages/squiggle-lang/__tests__/XYShape__Test.res rename to packages/squiggle-lang/__tests__/XYShape_test.res From 45c6eec7da80636abf08ee69e287660f74433ad2 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Wed, 6 Apr 2022 22:24:00 -0400 Subject: [PATCH 04/13] some fun with `testAll`. --- .../DistributionOperation__test.res | 111 -------------- .../__tests__/Distributions/Mixture_test.res | 57 +++++++ .../__tests__/Distributions/Symbolic_test.res | 145 ++++++++++++++++++ .../squiggle-lang/__tests__/Symbolic_test.res | 33 ---- 4 files changed, 202 insertions(+), 144 deletions(-) create mode 100644 packages/squiggle-lang/__tests__/Distributions/Mixture_test.res create mode 100644 packages/squiggle-lang/__tests__/Distributions/Symbolic_test.res delete mode 100644 packages/squiggle-lang/__tests__/Symbolic_test.res diff --git a/packages/squiggle-lang/__tests__/Distributions/DistributionOperation__test.res b/packages/squiggle-lang/__tests__/Distributions/DistributionOperation__test.res index d0135cf1..c4410f68 100644 --- a/packages/squiggle-lang/__tests__/Distributions/DistributionOperation__test.res +++ b/packages/squiggle-lang/__tests__/Distributions/DistributionOperation__test.res @@ -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", () => { diff --git a/packages/squiggle-lang/__tests__/Distributions/Mixture_test.res b/packages/squiggle-lang/__tests__/Distributions/Mixture_test.res new file mode 100644 index 00000000..c0cef5a6 --- /dev/null +++ b/packages/squiggle-lang/__tests__/Distributions/Mixture_test.res @@ -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 + ) + } + ) +}) + diff --git a/packages/squiggle-lang/__tests__/Distributions/Symbolic_test.res b/packages/squiggle-lang/__tests__/Distributions/Symbolic_test.res new file mode 100644 index 00000000..8e48b3df --- /dev/null +++ b/packages/squiggle-lang/__tests__/Distributions/Symbolic_test.res @@ -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 = 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("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(`▁▁▁▁▁▁▁▁▁▁▂▃▅▇███▇▅▃▂`) + }) +}) diff --git a/packages/squiggle-lang/__tests__/Symbolic_test.res b/packages/squiggle-lang/__tests__/Symbolic_test.res deleted file mode 100644 index 8ec3be22..00000000 --- a/packages/squiggle-lang/__tests__/Symbolic_test.res +++ /dev/null @@ -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, ()), `▁▁▁▁▁▁▁▁▁▁▂▃▅▇███▇▅▃▂`) -}) From e89042406be6d07ab86d6358b66f01adb5120f8f Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Wed, 6 Apr 2022 23:01:17 -0400 Subject: [PATCH 05/13] some cleanup of files --- ...est.res => DistributionOperation_test.res} | 6 +-- .../__tests__/Distributions/Samples_test.res | 46 ++++++++++++++++++ .../squiggle-lang/__tests__/Samples_test.res | 47 ------------------- packages/squiggle-lang/bsconfig.json | 2 +- 4 files changed, 50 insertions(+), 51 deletions(-) rename packages/squiggle-lang/__tests__/Distributions/{DistributionOperation__test.res => DistributionOperation_test.res} (91%) create mode 100644 packages/squiggle-lang/__tests__/Distributions/Samples_test.res delete mode 100644 packages/squiggle-lang/__tests__/Samples_test.res diff --git a/packages/squiggle-lang/__tests__/Distributions/DistributionOperation__test.res b/packages/squiggle-lang/__tests__/Distributions/DistributionOperation_test.res similarity index 91% rename from packages/squiggle-lang/__tests__/Distributions/DistributionOperation__test.res rename to packages/squiggle-lang/__tests__/Distributions/DistributionOperation_test.res index c4410f68..bfe630ae 100644 --- a/packages/squiggle-lang/__tests__/Distributions/DistributionOperation__test.res +++ b/packages/squiggle-lang/__tests__/Distributions/DistributionOperation_test.res @@ -26,7 +26,7 @@ describe("toPointSet", () => { ->outputMap(FromDist(ToFloat(#Mean))) ->toFloat ->toExt - expect(result)->toBeCloseTo(5.09) + expect(result)->toBeSoCloseTo(5.0, ~digits=0) }) test("on sample set distribution with under 4 points", () => { @@ -37,7 +37,7 @@ describe("toPointSet", () => { expect(result)->toEqual(GenDistError(Other("Converting sampleSet to pointSet failed"))) }) - Skip.test("on sample set", () => { + test("on sample set", () => { let result = run(FromDist(ToDist(ToPointSet), normalDist5)) ->outputMap(FromDist(ToDist(ToSampleSet(1000)))) @@ -45,6 +45,6 @@ describe("toPointSet", () => { ->outputMap(FromDist(ToFloat(#Mean))) ->toFloat ->toExt - expect(result)->toBeCloseTo(5.09) + expect(result)->toBeSoCloseTo(5.0, ~digits=-1) }) }) diff --git a/packages/squiggle-lang/__tests__/Distributions/Samples_test.res b/packages/squiggle-lang/__tests__/Distributions/Samples_test.res new file mode 100644 index 00000000..170ede1c --- /dev/null +++ b/packages/squiggle-lang/__tests__/Distributions/Samples_test.res @@ -0,0 +1,46 @@ +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)) + +describe("Continuous and discrete splits", () => { + makeTest( + "check splits one", + SampleSet.Internals.T.splitContinuousAndDiscrete([1.432, 1.33455, 2.0]), + ([1.432, 1.33455, 2.0], E.FloatFloatMap.empty()), + ) + makeTest( + "check splits two", + SampleSet.Internals.T.splitContinuousAndDiscrete([ + 1.432, + 1.33455, + 2.0, + 2.0, + 2.0, + 2.0, + ]) |> (((c, disc)) => (c, disc |> E.FloatFloatMap.toArray)), + ([1.432, 1.33455], [(2.0, 4.0)]), + ) + + let makeDuplicatedArray = count => { + let arr = Belt.Array.range(1, count) |> E.A.fmap(float_of_int) + let sorted = arr |> Belt.SortArray.stableSortBy(_, compare) + E.A.concatMany([sorted, sorted, sorted, sorted]) |> Belt.SortArray.stableSortBy(_, compare) + } + + let (_, discrete) = SampleSet.Internals.T.splitContinuousAndDiscrete( + makeDuplicatedArray(10), + ) + let toArr = discrete |> E.FloatFloatMap.toArray + makeTest("splitMedium at count=10", toArr |> Belt.Array.length, 10) + + let (_c, discrete) = SampleSet.Internals.T.splitContinuousAndDiscrete( + makeDuplicatedArray(500), + ) + let toArr = discrete |> E.FloatFloatMap.toArray + makeTest("splitMedium at count=500", toArr |> Belt.Array.length, 500) +}) + diff --git a/packages/squiggle-lang/__tests__/Samples_test.res b/packages/squiggle-lang/__tests__/Samples_test.res deleted file mode 100644 index 01c248f0..00000000 --- a/packages/squiggle-lang/__tests__/Samples_test.res +++ /dev/null @@ -1,47 +0,0 @@ -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)) - -describe("Lodash", () => - describe("Lodash", () => { - makeTest( - "split", - SampleSet.Internals.T.splitContinuousAndDiscrete([1.432, 1.33455, 2.0]), - ([1.432, 1.33455, 2.0], E.FloatFloatMap.empty()), - ) - makeTest( - "split", - SampleSet.Internals.T.splitContinuousAndDiscrete([ - 1.432, - 1.33455, - 2.0, - 2.0, - 2.0, - 2.0, - ]) |> (((c, disc)) => (c, disc |> E.FloatFloatMap.toArray)), - ([1.432, 1.33455], [(2.0, 4.0)]), - ) - - let makeDuplicatedArray = count => { - let arr = Belt.Array.range(1, count) |> E.A.fmap(float_of_int) - let sorted = arr |> Belt.SortArray.stableSortBy(_, compare) - E.A.concatMany([sorted, sorted, sorted, sorted]) |> Belt.SortArray.stableSortBy(_, compare) - } - - let (_, discrete) = SampleSet.Internals.T.splitContinuousAndDiscrete( - makeDuplicatedArray(10), - ) - let toArr = discrete |> E.FloatFloatMap.toArray - makeTest("splitMedium", toArr |> Belt.Array.length, 10) - - let (_c, discrete) = SampleSet.Internals.T.splitContinuousAndDiscrete( - makeDuplicatedArray(500), - ) - let toArr = discrete |> E.FloatFloatMap.toArray - makeTest("splitMedium", toArr |> Belt.Array.length, 500) - }) -) diff --git a/packages/squiggle-lang/bsconfig.json b/packages/squiggle-lang/bsconfig.json index 9bd15e11..76df3eda 100644 --- a/packages/squiggle-lang/bsconfig.json +++ b/packages/squiggle-lang/bsconfig.json @@ -50,7 +50,7 @@ [ "../../node_modules/bisect_ppx/ppx", "--exclude-files", - ".*_Test\\.res$$" + ".*_test\\.res$$" ] ] } From c50f8a3273d45d0bd05066635fe9f66e55ff9941 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Thu, 7 Apr 2022 08:55:52 -0400 Subject: [PATCH 06/13] backed out of `rescript-fast-check` --- .../__tests__/Distributions/Mixture_test.res | 12 ++++++------ packages/squiggle-lang/bsconfig.json | 3 --- packages/squiggle-lang/package.json | 1 - 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/packages/squiggle-lang/__tests__/Distributions/Mixture_test.res b/packages/squiggle-lang/__tests__/Distributions/Mixture_test.res index c0cef5a6..1993feb1 100644 --- a/packages/squiggle-lang/__tests__/Distributions/Mixture_test.res +++ b/packages/squiggle-lang/__tests__/Distributions/Mixture_test.res @@ -37,12 +37,12 @@ describe("mixture", () => { 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))) + run(Mixture( + [ + (mkBeta(alpha, beta), 0.25), + (mkExponential(rate), 0.75) + ] + )) -> outputMap(FromDist(ToFloat(#Mean))) } theMean -> unpackFloat diff --git a/packages/squiggle-lang/bsconfig.json b/packages/squiggle-lang/bsconfig.json index 76df3eda..e936eb82 100644 --- a/packages/squiggle-lang/bsconfig.json +++ b/packages/squiggle-lang/bsconfig.json @@ -30,9 +30,6 @@ "rationale", "bisect_ppx" ], - "bs-dev-dependencies": [ - "rescript-fast-check" - ], "gentypeconfig": { "language": "typescript", "module": "commonjs", diff --git a/packages/squiggle-lang/package.json b/packages/squiggle-lang/package.json index 92554dd9..03983cd2 100644 --- a/packages/squiggle-lang/package.json +++ b/packages/squiggle-lang/package.json @@ -35,7 +35,6 @@ "docsify": "^4.12.2", "gentype": "^4.3.0", "jest": "^27.5.1", - "rescript-fast-check": "^1.1.1", "moduleserve": "0.9.1", "ts-jest": "^27.1.4", "ts-loader": "^9.2.8", From a00772ef5c2e1b99c073a26aea684d1709a3b315 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Thu, 7 Apr 2022 10:21:48 -0400 Subject: [PATCH 07/13] tiny cleanup --- .../DistributionOperation/DistributionOperation.res | 4 ++-- .../rescript/Distributions/GenericDist/GenericDist_Types.res | 2 +- .../src/rescript/Distributions/SampleSetDist/SampleSet.res | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.res b/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.res index 351389ae..87195ec5 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.res +++ b/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.res @@ -10,10 +10,10 @@ type env = { } type outputType = - | Dist(GenericDist_Types.genericDist) + | Dist(genericDist) | Float(float) | String(string) - | GenDistError(GenericDist_Types.error) + | GenDistError(error) /* We're going to add another function to this module later, so first define a diff --git a/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist_Types.res b/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist_Types.res index 43ce5d74..df7549bb 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist_Types.res +++ b/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist_Types.res @@ -1,6 +1,6 @@ type genericDist = | PointSet(PointSetTypes.pointSetDist) - | SampleSet(array) + | SampleSet(SampleSet.t) | Symbolic(SymbolicDistTypes.symbolicDist) type error = diff --git a/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSet.res b/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSet.res index 746f13d7..eebb9efc 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSet.res +++ b/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSet.res @@ -1,3 +1,5 @@ +type t = array + // TODO: Refactor to raise correct error when not enough samples module Internals = { From db05541a7b8b924c9b271c6315b5de5eec860940 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Thu, 7 Apr 2022 10:55:51 -0400 Subject: [PATCH 08/13] up to 186 tests --- .../__tests__/Distributions/Mixture_test.res | 10 +-- .../__tests__/Distributions/Symbolic_test.res | 87 ++++++++++++------- 2 files changed, 62 insertions(+), 35 deletions(-) diff --git a/packages/squiggle-lang/__tests__/Distributions/Mixture_test.res b/packages/squiggle-lang/__tests__/Distributions/Mixture_test.res index 1993feb1..537a657f 100644 --- a/packages/squiggle-lang/__tests__/Distributions/Mixture_test.res +++ b/packages/squiggle-lang/__tests__/Distributions/Mixture_test.res @@ -6,10 +6,8 @@ let env: DistributionOperation.env = { xyPointLength: 100, } -let {toFloat, toDist, toString, toError} = module(DistributionOperation.Output) -let {run} = module(DistributionOperation) -let {fmap} = module(DistributionOperation.Output) -let run = run(~env) +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)", @@ -39,8 +37,8 @@ describe("mixture", () => { let theMean = { run(Mixture( [ - (mkBeta(alpha, beta), 0.25), - (mkExponential(rate), 0.75) + (mkBeta(alpha, beta), 0.25), + (mkExponential(rate), 0.75) ] )) -> outputMap(FromDist(ToFloat(#Mean))) } diff --git a/packages/squiggle-lang/__tests__/Distributions/Symbolic_test.res b/packages/squiggle-lang/__tests__/Distributions/Symbolic_test.res index 8e48b3df..ae5d17c4 100644 --- a/packages/squiggle-lang/__tests__/Distributions/Symbolic_test.res +++ b/packages/squiggle-lang/__tests__/Distributions/Symbolic_test.res @@ -1,7 +1,7 @@ open Jest open Expect -let pdfImage = (thePdf, inps) => Js.Array.map(thePdf, inps) +let fnImage = (theFn, inps) => Js.Array.map(theFn, inps) let env: DistributionOperation.env = { sampleCount: 100, @@ -10,8 +10,7 @@ let env: DistributionOperation.env = { 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 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)", @@ -33,7 +32,7 @@ describe("normalize", () => { }) }) -describe("mean", () => { +describe("(Symbolic) 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 @@ -41,15 +40,33 @@ describe("mean", () => { -> toBeCloseTo(mean) }) + Skip.test("of normal(0, -1) (it NaNs out)", () => { + run(FromDist(ToFloat(#Mean), mkNormal(1e1, -1e0))) + -> unpackFloat + -> expect + -> ExpectJs.toBeFalsy + }) + + test("of normal(0, 1e-8) (it doesn't freak out at tiny stdev)", () => { + run(FromDist(ToFloat(#Mean), mkNormal(0.0, 1e-8))) + -> unpackFloat + -> expect + -> toBeCloseTo(0.0) + }) + 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 cauchy distribution", () => { + let theMean = run(FromDist(ToFloat(#Mean), GenericDist_Types.Symbolic(#Cauchy({local: 1.0, scale: 1.0})))) + theMean + -> unpackFloat + -> expect + -> toBeCloseTo(2.01868297874546) + //-> toBe(GenDistError(Other("Cauchy distributions may have no mean value."))) + }) test("of a triangular distribution", () => { // should be property let theMean = run(FromDist( @@ -62,48 +79,51 @@ describe("mean", () => { -> 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 + 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( ToFloat(#Mean), - GenericDist_Types.Symbolic(#Beta({alpha: 2e-4, beta: 64.0})) + GenericDist_Types.Symbolic(#Beta({alpha: alpha, beta: beta})) )) theMean -> unpackFloat -> expect - -> toBeCloseTo(1.0 /. (1.0 +. (64.0 /. 2e-4))) // https://en.wikipedia.org/wiki/Beta_distribution#Mean + -> toBeCloseTo(1.0 /. (1.0 +. (beta /. alpha))) // https://en.wikipedia.org/wiki/Beta_distribution#Mean }) - test("of a beta distribution with alpha much larger than beta", () => { // should be property + test("of beta(0, 0)", () => { let theMean = run(FromDist( ToFloat(#Mean), - GenericDist_Types.Symbolic(#Beta({alpha: 128.0, beta: 1.0})) + GenericDist_Types.Symbolic(#Beta({alpha: 0.0, beta: 0.0})) )) - theMean - -> unpackFloat - -> expect - -> toBeCloseTo(1.0 /. (1.0 +. (1.0 /. 128.0))) // https://en.wikipedia.org/wiki/Beta_distribution#Mean + theMean + -> unpackFloat + -> expect + -> ExpectJs.toBeFalsy }) - test("of a lognormal", () => { // should be property + 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( ToFloat(#Mean), - GenericDist_Types.Symbolic(#Lognormal({mu: 2.0, sigma: 4.0})) + GenericDist_Types.Symbolic(#Lognormal({mu: mu, sigma: sigma})) )) theMean -> unpackFloat -> expect - -> toBeCloseTo(Js.Math.exp(2.0 +. 4.0 ** 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/ }) - test("of a uniform", () => { + 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( ToFloat(#Mean), - GenericDist_Types.Symbolic(#Uniform({low: 1e-5, high: 12.345})) + GenericDist_Types.Symbolic(#Uniform({low: low, high: high})) )) theMean -> unpackFloat -> expect - -> toBeCloseTo((1e-5 +. 12.345) /. 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", () => { @@ -117,7 +137,7 @@ describe("mean", () => { describe("Normal distribution with sparklines", () => { - let parameterWiseAdditionHelper = (n1: SymbolicDistTypes.normal, n2: SymbolicDistTypes.normal) => { + let parameterWiseAdditionPdf = (n1: SymbolicDistTypes.normal, n2: SymbolicDistTypes.normal) => { let normalDistAtSumMeanConstr = SymbolicDist.Normal.add(n1, n2) let normalDistAtSumMean: SymbolicDistTypes.normal = switch normalDistAtSumMeanConstr { | #Normal(params) => params @@ -129,17 +149,26 @@ describe("Normal distribution with sparklines", () => { 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", () => { + test("mean=5 pdf", () => { + let pdfNormalDistAtMean5 = x => SymbolicDist.Normal.pdf(x, normalDistAtMean5) + let sparklineMean5 = fnImage(pdfNormalDistAtMean5, range20Float) Sparklines.create(sparklineMean5, ()) -> expect -> toEqual(`▁▂▃▅███▅▃▂▁▁▁▁▁▁▁▁▁▁▁`) }) - let sparklineMean15 = normalDistAtMean5 -> parameterWiseAdditionHelper(normalDistAtMean10) -> pdfImage(range20Float) - test("parameter-wise addition of two normal distributions", () => { + + test("parameter-wise addition of two normal distributions", () => { + let sparklineMean15 = normalDistAtMean5 -> parameterWiseAdditionPdf(normalDistAtMean10) -> fnImage(range20Float) Sparklines.create(sparklineMean15, ()) -> expect -> toEqual(`▁▁▁▁▁▁▁▁▁▁▂▃▅▇███▇▅▃▂`) }) + + test("mean=5 cdf", () => { + let cdfNormalDistAtMean10 = x => SymbolicDist.Normal.cdf(x, normalDistAtMean10) + let sparklineMean10 = fnImage(cdfNormalDistAtMean10, range20Float) + Sparklines.create(sparklineMean10, ()) + -> expect + -> toEqual(`▁▁▁▁▁▁▁▁▂▃▅▆▇████████`) + }) }) From 94db348db5242ddc93b607df0017c017ec995d65 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Thu, 7 Apr 2022 13:33:12 -0400 Subject: [PATCH 09/13] calling it a night on 192 (pending CR) --- .../__tests__/Distributions/Mixture_test.res | 20 +++++++++++++++++++ .../__tests__/Distributions/Symbolic_test.res | 11 ++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/packages/squiggle-lang/__tests__/Distributions/Mixture_test.res b/packages/squiggle-lang/__tests__/Distributions/Mixture_test.res index 537a657f..dc19bfd2 100644 --- a/packages/squiggle-lang/__tests__/Distributions/Mixture_test.res +++ b/packages/squiggle-lang/__tests__/Distributions/Mixture_test.res @@ -17,6 +17,9 @@ 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})) +let mkUniform = (low, high) => GenericDist_Types.Symbolic(#Uniform({low: low, high: high})) +let mkCauchy = (local, scale) => GenericDist_Types.Symbolic(#Cauchy({local: local, scale: scale})) +let mkLognormal = (mu, sigma) => GenericDist_Types.Symbolic(#Lognormal({mu: mu, sigma: sigma})) describe("mixture", () => { testAll("fair mean of two normal distributions", list{(0.0, 1e2), (-1e1, -1e-4), (-1e1, 1e2), (-1e1, 1e1)}, tup => { // should be property @@ -51,5 +54,22 @@ describe("mixture", () => { ) } ) + testAll( + "weighted mean of lognormal and uniform", + list{}, + 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)])) + -> outputMap(FromDist(ToFloat(#Mean))) + } + theMean + -> unpackFloat + -> expect + -> toBeSoCloseTo(0.6 *. (low +. high) /. 2.0 +. 0.4 *. (mu +. sigma ** 2.0 /. 2.0), ~digits=0) + } + ) }) diff --git a/packages/squiggle-lang/__tests__/Distributions/Symbolic_test.res b/packages/squiggle-lang/__tests__/Distributions/Symbolic_test.res index ae5d17c4..4f17fab2 100644 --- a/packages/squiggle-lang/__tests__/Distributions/Symbolic_test.res +++ b/packages/squiggle-lang/__tests__/Distributions/Symbolic_test.res @@ -21,7 +21,7 @@ let toExtDist: option => GenericDist_Types.generi let unpackFloat = x => x -> toFloat -> toExtFloat let unpackDist = y => y -> toDist -> toExtDist -describe("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 => { let theNormal = mkNormal(mean, 2.0) let theNormalized = run(FromDist(ToDist(Normalize), theNormal)) @@ -68,17 +68,19 @@ describe("(Symbolic) mean", () => { //-> toBe(GenDistError(Other("Cauchy distributions may have no mean value."))) }) - test("of a triangular distribution", () => { // should be property + 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( ToFloat(#Mean), - GenericDist_Types.Symbolic(#Triangular({low: - 5.0, medium: 1e-3, high: 10.0})) + GenericDist_Types.Symbolic(#Triangular({low: low, medium: medium, high: high})) )) theMean -> unpackFloat -> expect - -> toBeCloseTo((-5.0 +. 1e-3 +. 10.0) /. 3.0) // https://www.statology.org/triangular-distribution/ + -> toBeCloseTo((low +. medium +. high) /. 3.0) // https://www.statology.org/triangular-distribution/ }) + // 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( @@ -91,6 +93,7 @@ describe("(Symbolic) mean", () => { -> toBeCloseTo(1.0 /. (1.0 +. (beta /. alpha))) // https://en.wikipedia.org/wiki/Beta_distribution#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( ToFloat(#Mean), From 72cfbf14c2c43aae4d465c09ded2bd20b345be5e Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Thu, 7 Apr 2022 13:50:11 -0400 Subject: [PATCH 10/13] test cases for lognormal uniform mixture --- .../__tests__/Distributions/Mixture_test.res | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/squiggle-lang/__tests__/Distributions/Mixture_test.res b/packages/squiggle-lang/__tests__/Distributions/Mixture_test.res index dc19bfd2..ece229be 100644 --- a/packages/squiggle-lang/__tests__/Distributions/Mixture_test.res +++ b/packages/squiggle-lang/__tests__/Distributions/Mixture_test.res @@ -2,8 +2,8 @@ open Jest open Expect let env: DistributionOperation.env = { - sampleCount: 1000, - xyPointLength: 100, + sampleCount: 10000, + xyPointLength: 1000, } let {toFloat, toDist, toString, toError, fmap} = module(DistributionOperation.Output) @@ -56,7 +56,8 @@ describe("mixture", () => { ) testAll( "weighted mean of lognormal and uniform", - list{}, + // 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 @@ -68,7 +69,7 @@ describe("mixture", () => { theMean -> unpackFloat -> expect - -> toBeSoCloseTo(0.6 *. (low +. high) /. 2.0 +. 0.4 *. (mu +. sigma ** 2.0 /. 2.0), ~digits=0) + -> toBeSoCloseTo(0.6 *. (low +. high) /. 2.0 +. 0.4 *. (mu +. sigma ** 2.0 /. 2.0), ~digits=-1) } ) }) From d582e29e8b310788ff4a2fdda62d5ca94effcc11 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Thu, 7 Apr 2022 14:33:49 -0400 Subject: [PATCH 11/13] CR comment #1 --- .../squiggle-lang/__tests__/Distributions/Symbolic_test.res | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/squiggle-lang/__tests__/Distributions/Symbolic_test.res b/packages/squiggle-lang/__tests__/Distributions/Symbolic_test.res index 4f17fab2..c2df8674 100644 --- a/packages/squiggle-lang/__tests__/Distributions/Symbolic_test.res +++ b/packages/squiggle-lang/__tests__/Distributions/Symbolic_test.res @@ -167,7 +167,7 @@ describe("Normal distribution with sparklines", () => { -> toEqual(`▁▁▁▁▁▁▁▁▁▁▂▃▅▇███▇▅▃▂`) }) - test("mean=5 cdf", () => { + test("mean=10 cdf", () => { let cdfNormalDistAtMean10 = x => SymbolicDist.Normal.cdf(x, normalDistAtMean10) let sparklineMean10 = fnImage(cdfNormalDistAtMean10, range20Float) Sparklines.create(sparklineMean10, ()) From 72be08a516bf8d85d38e4c247fe933959cf4b38f Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Thu, 7 Apr 2022 18:38:49 -0400 Subject: [PATCH 12/13] 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" + ], }; From 9764a4cab8a799602590c11b5a230f3c7da3f35c Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Thu, 7 Apr 2022 20:13:08 -0400 Subject: [PATCH 13/13] one last CR comment --- .../__tests__/Distributions/Samples_test.res | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/squiggle-lang/__tests__/Distributions/Samples_test.res b/packages/squiggle-lang/__tests__/Distributions/Samples_test.res index 6da9efe3..db80f9f7 100644 --- a/packages/squiggle-lang/__tests__/Distributions/Samples_test.res +++ b/packages/squiggle-lang/__tests__/Distributions/Samples_test.res @@ -26,16 +26,16 @@ describe("Continuous and discrete splits", () => { E.A.concatMany([sorted, sorted, sorted, sorted]) |> Belt.SortArray.stableSortBy(_, compare) } - let (_, discrete) = SampleSet.Internals.T.splitContinuousAndDiscrete( + let (_, discrete1) = SampleSet.Internals.T.splitContinuousAndDiscrete( makeDuplicatedArray(10), ) - let toArr = discrete |> E.FloatFloatMap.toArray - makeTest("splitMedium at count=10", toArr |> Belt.Array.length, 10) + let toArr1 = discrete1 |> E.FloatFloatMap.toArray + makeTest("splitMedium at count=10", toArr1 |> Belt.Array.length, 10) - let (_c, discrete) = SampleSet.Internals.T.splitContinuousAndDiscrete( + let (_c, discrete2) = SampleSet.Internals.T.splitContinuousAndDiscrete( makeDuplicatedArray(500), ) - let toArr = discrete |> E.FloatFloatMap.toArray - makeTest("splitMedium at count=500", toArr |> Belt.Array.length, 500) + let toArr2 = discrete2 |> E.FloatFloatMap.toArray + makeTest("splitMedium at count=500", toArr2 |> Belt.Array.length, 500) })