Merge branch 'staging' into Components-improvement-april-2
* staging: (41 commits) Fixed tests Samples_test -> SampleSetDist_test Bandwidth -> SampleSetDist_Bandwidth Added genType to SampleSetDist to make pass tests, other minor fixes Power should be ** to be consistent Cleaned up resultStringToResultError Cleanup from previous refactor Start of refactor for toPointSetDist Gave SampleSetDist a private type Namechange: Exponential -> Power Cleanup and commenting for PR Added more tests to JS__Test.ts, and added SampleN functionality to SampleSetDist Added tests for index.js and fixed some corresponding functionality Minor refactor of DistributionOperation Constructors Fix from CR Added to index.ts Added a bunch of manual functions for DistributionOperation Update README.md Cleanup from merge Added sparkline and toString to ReducerInterface ...
This commit is contained in:
commit
ce4eaa7daa
|
@ -1,4 +1,5 @@
|
||||||
# Squiggle
|
# Squiggle
|
||||||
|
![Packages check](https://github.com/QURIresearch/squiggle/actions/workflows/ci.yaml/badge.svg)
|
||||||
|
|
||||||
This is an experiment DSL/language for making probabilistic estimates. The full story can be found [here](https://www.lesswrong.com/s/rDe8QE5NvXcZYzgZ3).
|
This is an experiment DSL/language for making probabilistic estimates. The full story can be found [here](https://www.lesswrong.com/s/rDe8QE5NvXcZYzgZ3).
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,9 @@ Other:
|
||||||
yarn start # listens to files and recompiles at every mutation
|
yarn start # listens to files and recompiles at every mutation
|
||||||
yarn test
|
yarn test
|
||||||
yarn test:watch # keeps an active session and runs all tests at every mutation
|
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
|
# TODO: clean up this README.md
|
||||||
|
|
|
@ -4,10 +4,10 @@ open Expect
|
||||||
describe("Bandwidth", () => {
|
describe("Bandwidth", () => {
|
||||||
test("nrd0()", () => {
|
test("nrd0()", () => {
|
||||||
let data = [1., 4., 3., 2.]
|
let data = [1., 4., 3., 2.]
|
||||||
expect(Bandwidth.nrd0(data)) -> toEqual(0.7625801874014622)
|
expect(SampleSetDist_Bandwidth.nrd0(data)) -> toEqual(0.7625801874014622)
|
||||||
})
|
})
|
||||||
test("nrd()", () => {
|
test("nrd()", () => {
|
||||||
let data = [1., 4., 3., 2.]
|
let data = [1., 4., 3., 2.]
|
||||||
expect(Bandwidth.nrd(data)) -> toEqual(0.8981499984950554)
|
expect(SampleSetDist_Bandwidth.nrd(data)) -> toEqual(0.8981499984950554)
|
||||||
})
|
})
|
||||||
})
|
})
|
|
@ -0,0 +1,103 @@
|
||||||
|
open Jest
|
||||||
|
open Expect
|
||||||
|
|
||||||
|
let env: DistributionOperation.env = {
|
||||||
|
sampleCount: 100,
|
||||||
|
xyPointLength: 100,
|
||||||
|
}
|
||||||
|
|
||||||
|
let {
|
||||||
|
normalDist5,
|
||||||
|
normalDist10,
|
||||||
|
normalDist20,
|
||||||
|
normalDist,
|
||||||
|
uniformDist,
|
||||||
|
betaDist,
|
||||||
|
lognormalDist,
|
||||||
|
cauchyDist,
|
||||||
|
triangularDist,
|
||||||
|
exponentialDist,
|
||||||
|
} = module(GenericDist_Fixtures)
|
||||||
|
let mkNormal = (mean, stdev) => GenericDist_Types.Symbolic(#Normal({mean: mean, stdev: stdev}))
|
||||||
|
|
||||||
|
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)",
|
||||||
|
)
|
||||||
|
|
||||||
|
describe("sparkline", () => {
|
||||||
|
let runTest = (
|
||||||
|
name: string,
|
||||||
|
dist: GenericDist_Types.genericDist,
|
||||||
|
expected: DistributionOperation.outputType,
|
||||||
|
) => {
|
||||||
|
test(name, () => {
|
||||||
|
let result = DistributionOperation.run(~env, FromDist(ToString(ToSparkline(20)), dist))
|
||||||
|
expect(result)->toEqual(expected)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
runTest(
|
||||||
|
"normal",
|
||||||
|
normalDist,
|
||||||
|
String(`▁▁▁▁▁▂▄▆▇██▇▆▄▂▁▁▁▁▁`),
|
||||||
|
)
|
||||||
|
|
||||||
|
runTest(
|
||||||
|
"uniform",
|
||||||
|
uniformDist,
|
||||||
|
String(`████████████████████`),
|
||||||
|
)
|
||||||
|
|
||||||
|
runTest("beta", betaDist, String(`▁▄▇████▇▆▅▄▃▃▂▁▁▁▁▁▁`))
|
||||||
|
|
||||||
|
runTest(
|
||||||
|
"lognormal",
|
||||||
|
lognormalDist,
|
||||||
|
String(`▁█▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁`),
|
||||||
|
)
|
||||||
|
|
||||||
|
runTest(
|
||||||
|
"cauchy",
|
||||||
|
cauchyDist,
|
||||||
|
String(`▁▁▁▁▁▁▁▁▁██▁▁▁▁▁▁▁▁▁`),
|
||||||
|
)
|
||||||
|
|
||||||
|
runTest(
|
||||||
|
"triangular",
|
||||||
|
triangularDist,
|
||||||
|
String(`▁▁▂▃▄▅▆▇████▇▆▅▄▃▂▁▁`),
|
||||||
|
)
|
||||||
|
|
||||||
|
runTest(
|
||||||
|
"exponential",
|
||||||
|
exponentialDist,
|
||||||
|
String(`█▅▄▂▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁`),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("toPointSet", () => {
|
||||||
|
test("on symbolic normal distribution", () => {
|
||||||
|
let result =
|
||||||
|
run(FromDist(ToDist(ToPointSet), normalDist5))
|
||||||
|
->outputMap(FromDist(ToFloat(#Mean)))
|
||||||
|
->toFloat
|
||||||
|
->toExt
|
||||||
|
expect(result)->toBeSoCloseTo(5.0, ~digits=0)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("on sample set", () => {
|
||||||
|
let result =
|
||||||
|
run(FromDist(ToDist(ToPointSet), normalDist5))
|
||||||
|
->outputMap(FromDist(ToDist(ToSampleSet(1000))))
|
||||||
|
->outputMap(FromDist(ToDist(ToPointSet)))
|
||||||
|
->outputMap(FromDist(ToFloat(#Mean)))
|
||||||
|
->toFloat
|
||||||
|
->toExt
|
||||||
|
expect(result)->toBeSoCloseTo(5.0, ~digits=-1)
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,11 @@
|
||||||
|
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 normalDist: GenericDist_Types.genericDist = normalDist5
|
||||||
|
|
||||||
|
let betaDist: GenericDist_Types.genericDist = Symbolic(#Beta({alpha: 2.0, beta: 5.0}))
|
||||||
|
let lognormalDist: GenericDist_Types.genericDist = Symbolic(#Lognormal({mu: 0.0, sigma: 1.0}))
|
||||||
|
let cauchyDist: GenericDist_Types.genericDist = Symbolic(#Cauchy({local: 1.0, scale: 1.0}))
|
||||||
|
let triangularDist: GenericDist_Types.genericDist = Symbolic(#Triangular({low: 1.0, medium: 2.0, high: 3.0}))
|
||||||
|
let exponentialDist: GenericDist_Types.genericDist = Symbolic(#Exponential({rate: 2.0}))
|
||||||
|
let uniformDist: GenericDist_Types.genericDist = Symbolic(#Uniform({low: 9.0, high: 10.0}))
|
|
@ -0,0 +1,70 @@
|
||||||
|
open Jest
|
||||||
|
open Expect
|
||||||
|
open TestHelpers
|
||||||
|
|
||||||
|
// 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}))
|
||||||
|
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
|
||||||
|
let (mean1, mean2) = tup
|
||||||
|
let meanValue = {
|
||||||
|
run(Mixture([(mkNormal(mean1, 9e-1), 0.5), (mkNormal(mean2, 9e-1), 0.5)]))
|
||||||
|
-> outputMap(FromDist(ToFloat(#Mean)))
|
||||||
|
}
|
||||||
|
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 ((alpha, beta), rate) = tup
|
||||||
|
let betaWeight = 0.25
|
||||||
|
let exponentialWeight = 0.75
|
||||||
|
let meanValue = {
|
||||||
|
run(Mixture(
|
||||||
|
[
|
||||||
|
(mkBeta(alpha, beta), betaWeight),
|
||||||
|
(mkExponential(rate), exponentialWeight)
|
||||||
|
]
|
||||||
|
)) -> outputMap(FromDist(ToFloat(#Mean)))
|
||||||
|
}
|
||||||
|
let betaMean = 1.0 /. (1.0 +. beta /. alpha)
|
||||||
|
let exponentialMean = 1.0 /. rate
|
||||||
|
meanValue
|
||||||
|
-> unpackFloat
|
||||||
|
-> expect
|
||||||
|
-> toBeSoCloseTo(
|
||||||
|
betaWeight *. betaMean +. exponentialWeight *. exponentialMean,
|
||||||
|
~digits=-1
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
testAll(
|
||||||
|
"weighted mean of lognormal and uniform",
|
||||||
|
// 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 ((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)))
|
||||||
|
}
|
||||||
|
let uniformMean = (low +. high) /. 2.0
|
||||||
|
let lognormalMean = mu +. sigma ** 2.0 /. 2.0
|
||||||
|
meanValue
|
||||||
|
-> unpackFloat
|
||||||
|
-> expect
|
||||||
|
-> toBeSoCloseTo(uniformWeight *. uniformMean +. lognormalWeight *. lognormalMean, ~digits=-1)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
open Jest
|
||||||
|
open TestHelpers
|
||||||
|
|
||||||
|
describe("Continuous and discrete splits", () => {
|
||||||
|
makeTest(
|
||||||
|
"splits (1)",
|
||||||
|
SampleSetDist_ToPointSet.Internals.T.splitContinuousAndDiscrete([1.432, 1.33455, 2.0]),
|
||||||
|
([1.432, 1.33455, 2.0], E.FloatFloatMap.empty()),
|
||||||
|
)
|
||||||
|
makeTest(
|
||||||
|
"splits (2)",
|
||||||
|
SampleSetDist_ToPointSet.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 (_, discrete1) = SampleSetDist_ToPointSet.Internals.T.splitContinuousAndDiscrete(
|
||||||
|
makeDuplicatedArray(10),
|
||||||
|
)
|
||||||
|
let toArr1 = discrete1 |> E.FloatFloatMap.toArray
|
||||||
|
makeTest("splitMedium at count=10", toArr1 |> Belt.Array.length, 10)
|
||||||
|
|
||||||
|
let (_c, discrete2) = SampleSetDist_ToPointSet.Internals.T.splitContinuousAndDiscrete(
|
||||||
|
makeDuplicatedArray(500),
|
||||||
|
)
|
||||||
|
let toArr2 = discrete2 |> E.FloatFloatMap.toArray
|
||||||
|
makeTest("splitMedium at count=500", toArr2 |> Belt.Array.length, 500)
|
||||||
|
})
|
||||||
|
|
161
packages/squiggle-lang/__tests__/Distributions/Symbolic_test.res
Normal file
161
packages/squiggle-lang/__tests__/Distributions/Symbolic_test.res
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
open Jest
|
||||||
|
open Expect
|
||||||
|
open TestHelpers
|
||||||
|
|
||||||
|
// 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}))
|
||||||
|
|
||||||
|
describe("(Symbolic) normalize", () => {
|
||||||
|
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(normalValue)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
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
|
||||||
|
-> expect
|
||||||
|
-> 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 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 meanValue = run(FromDist(ToFloat(#Mean), GenericDist_Types.Symbolic(#Cauchy({local: 1.0, scale: 1.0}))))
|
||||||
|
meanValue
|
||||||
|
-> unpackFloat
|
||||||
|
-> expect
|
||||||
|
-> toBeCloseTo(2.01868297874546)
|
||||||
|
//-> toBe(GenDistError(Other("Cauchy distributions may have no mean value.")))
|
||||||
|
})
|
||||||
|
|
||||||
|
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 meanValue = run(FromDist(
|
||||||
|
ToFloat(#Mean),
|
||||||
|
GenericDist_Types.Symbolic(#Triangular({low: low, medium: medium, high: high}))
|
||||||
|
))
|
||||||
|
meanValue
|
||||||
|
-> unpackFloat
|
||||||
|
-> expect
|
||||||
|
-> 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 meanValue = run(FromDist(
|
||||||
|
ToFloat(#Mean),
|
||||||
|
GenericDist_Types.Symbolic(#Beta({alpha: alpha, beta: beta}))
|
||||||
|
))
|
||||||
|
meanValue
|
||||||
|
-> unpackFloat
|
||||||
|
-> expect
|
||||||
|
-> 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 meanValue = run(FromDist(
|
||||||
|
ToFloat(#Mean),
|
||||||
|
GenericDist_Types.Symbolic(#Beta({alpha: 0.0, beta: 0.0}))
|
||||||
|
))
|
||||||
|
meanValue
|
||||||
|
-> unpackFloat
|
||||||
|
-> expect
|
||||||
|
-> ExpectJs.toBeFalsy
|
||||||
|
})
|
||||||
|
|
||||||
|
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 meanValue = run(FromDist(
|
||||||
|
ToFloat(#Mean),
|
||||||
|
GenericDist_Types.Symbolic(#Lognormal({mu: mu, sigma: sigma}))
|
||||||
|
))
|
||||||
|
meanValue
|
||||||
|
-> unpackFloat
|
||||||
|
-> expect
|
||||||
|
-> toBeCloseTo(Js.Math.exp(mu +. sigma ** 2.0 /. 2.0 )) // https://brilliant.org/wiki/log-normal-distribution/
|
||||||
|
})
|
||||||
|
|
||||||
|
testAll("of uniform distributions", list{(1e-5, 12.345), (-1e4, 1e4), (-1e16, -1e2), (5.3e3, 9e9)}, tup => {
|
||||||
|
let (low, high) = tup
|
||||||
|
let meanValue = run(FromDist(
|
||||||
|
ToFloat(#Mean),
|
||||||
|
GenericDist_Types.Symbolic(#Uniform({low: low, high: high}))
|
||||||
|
))
|
||||||
|
meanValue
|
||||||
|
-> unpackFloat
|
||||||
|
-> expect
|
||||||
|
-> toBeCloseTo((low +. high) /. 2.0) // https://en.wikipedia.org/wiki/Continuous_uniform_distribution#Moments
|
||||||
|
})
|
||||||
|
|
||||||
|
test("of a float", () => {
|
||||||
|
let meanValue = run(FromDist(
|
||||||
|
ToFloat(#Mean),
|
||||||
|
GenericDist_Types.Symbolic(#Float(7.7))
|
||||||
|
))
|
||||||
|
meanValue -> unpackFloat -> expect -> toBeCloseTo(7.7)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("Normal distribution with sparklines", () => {
|
||||||
|
|
||||||
|
let parameterWiseAdditionPdf = (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.Floats.range(0.0, 20.0, 20) // [0.0,1.0,2.0,3.0,4.0,...19.0,]
|
||||||
|
|
||||||
|
test("mean=5 pdf", () => {
|
||||||
|
let pdfNormalDistAtMean5 = x => SymbolicDist.Normal.pdf(x, normalDistAtMean5)
|
||||||
|
let sparklineMean5 = fnImage(pdfNormalDistAtMean5, range20Float)
|
||||||
|
Sparklines.create(sparklineMean5, ())
|
||||||
|
-> expect
|
||||||
|
-> toEqual(`▁▂▃▆██▇▅▂▁▁▁▁▁▁▁▁▁▁▁`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("parameter-wise addition of two normal distributions", () => {
|
||||||
|
let sparklineMean15 = normalDistAtMean5 -> parameterWiseAdditionPdf(normalDistAtMean10) -> fnImage(range20Float)
|
||||||
|
Sparklines.create(sparklineMean15, ())
|
||||||
|
-> expect
|
||||||
|
-> toEqual(`▁▁▁▁▁▁▁▁▁▂▃▄▆███▇▅▄▂`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("mean=10 cdf", () => {
|
||||||
|
let cdfNormalDistAtMean10 = x => SymbolicDist.Normal.cdf(x, normalDistAtMean10)
|
||||||
|
let sparklineMean10 = fnImage(cdfNormalDistAtMean10, range20Float)
|
||||||
|
Sparklines.create(sparklineMean10, ())
|
||||||
|
-> expect
|
||||||
|
-> toEqual(`▁▁▁▁▁▁▁▁▂▄▅▇████████`)
|
||||||
|
})
|
||||||
|
})
|
|
@ -1,76 +0,0 @@
|
||||||
open Jest
|
|
||||||
open Expect
|
|
||||||
|
|
||||||
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 uniformDist: GenericDist_Types.genericDist = Symbolic(#Uniform({low: 9.0, high: 10.0}))
|
|
||||||
|
|
||||||
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)",
|
|
||||||
)
|
|
||||||
|
|
||||||
describe("normalize", () => {
|
|
||||||
test("has no impact on normal dist", () => {
|
|
||||||
let result = run(FromDist(ToDist(Normalize), normalDist5))
|
|
||||||
expect(result)->toEqual(Dist(normalDist5))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("mean", () => {
|
|
||||||
test("for a normal distribution", () => {
|
|
||||||
let result = DistributionOperation.run(~env, FromDist(ToFloat(#Mean), normalDist5))
|
|
||||||
expect(result)->toEqual(Float(5.0))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
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", () => {
|
|
||||||
let result =
|
|
||||||
run(FromDist(ToDist(ToPointSet), normalDist5))
|
|
||||||
->outputMap(FromDist(ToFloat(#Mean)))
|
|
||||||
->toFloat
|
|
||||||
->toExt
|
|
||||||
expect(result)->toBeCloseTo(5.09)
|
|
||||||
})
|
|
||||||
|
|
||||||
test("on sample set distribution with under 4 points", () => {
|
|
||||||
let result =
|
|
||||||
run(FromDist(ToDist(ToPointSet), SampleSet([0.0, 1.0, 2.0, 3.0])))->outputMap(
|
|
||||||
FromDist(ToFloat(#Mean)),
|
|
||||||
)
|
|
||||||
expect(result)->toEqual(GenDistError(Other("Converting sampleSet to pointSet failed")))
|
|
||||||
})
|
|
||||||
|
|
||||||
Skip.test("on sample set", () => {
|
|
||||||
let result =
|
|
||||||
run(FromDist(ToDist(ToPointSet), normalDist5))
|
|
||||||
->outputMap(FromDist(ToDist(ToSampleSet(1000))))
|
|
||||||
->outputMap(FromDist(ToDist(ToPointSet)))
|
|
||||||
->outputMap(FromDist(ToFloat(#Mean)))
|
|
||||||
->toFloat
|
|
||||||
->toExt
|
|
||||||
expect(result)->toBeCloseTo(5.09)
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,34 +1,87 @@
|
||||||
import { run } from '../src/js/index';
|
import { run, GenericDist, resultMap, makeSampleSetDist } from "../src/js/index";
|
||||||
|
|
||||||
let testRun = (x: string) => {
|
let testRun = (x: string) => {
|
||||||
let result = run(x)
|
let result = run(x);
|
||||||
if(result.tag == 'Ok'){
|
if (result.tag == "Ok") {
|
||||||
return { tag: 'Ok', value: result.value.exports }
|
return { tag: "Ok", value: result.value.exports };
|
||||||
}
|
} else {
|
||||||
else {
|
return result;
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
describe("Simple calculations and results", () => {
|
describe("Simple calculations and results", () => {
|
||||||
test("mean(normal(5,2))", () => {
|
test("mean(normal(5,2))", () => {
|
||||||
expect(testRun("mean(normal(5,2))")).toEqual({ tag: 'Ok', value: [ { NAME: 'Float', VAL: 5 } ] })
|
expect(testRun("mean(normal(5,2))")).toEqual({
|
||||||
})
|
tag: "Ok",
|
||||||
|
value: [{ NAME: "Float", VAL: 5 }],
|
||||||
|
});
|
||||||
|
});
|
||||||
test("10+10", () => {
|
test("10+10", () => {
|
||||||
let foo = testRun("10 + 10")
|
let foo = testRun("10 + 10");
|
||||||
expect(foo).toEqual({ tag: 'Ok', value: [ { NAME: 'Float', VAL: 20 } ] })
|
expect(foo).toEqual({ tag: "Ok", value: [{ NAME: "Float", VAL: 20 }] });
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
describe("Log function", () => {
|
describe("Log function", () => {
|
||||||
test("log(1) = 0", () => {
|
test("log(1) = 0", () => {
|
||||||
let foo = testRun("log(1)")
|
let foo = testRun("log(1)");
|
||||||
expect(foo).toEqual({ tag: 'Ok', value: [ { NAME: 'Float', VAL: 0} ]})
|
expect(foo).toEqual({ tag: "Ok", value: [{ NAME: "Float", VAL: 0 }] });
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
describe("Multimodal too many weights error", () => {
|
describe("Multimodal too many weights error", () => {
|
||||||
test("mm(0,0,[0,0,0])", () => {
|
test("mm(0,0,[0,0,0])", () => {
|
||||||
let foo = testRun("mm(0,0,[0,0,0])")
|
let foo = testRun("mm(0,0,[0,0,0])");
|
||||||
expect(foo).toEqual({ "tag": "Error", "value": "Function multimodal error: Too many weights provided" })
|
expect(foo).toEqual({
|
||||||
})
|
tag: "Error",
|
||||||
|
value: "Function multimodal error: Too many weights provided",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("GenericDist", () => {
|
||||||
|
|
||||||
|
//It's important that sampleCount is less than 9. If it's more, than that will create randomness
|
||||||
|
//Also, note, the value should be created using makeSampleSetDist() later on.
|
||||||
|
let env = { sampleCount: 8, xyPointLength: 100 };
|
||||||
|
let dist = new GenericDist(
|
||||||
|
{ tag: "SampleSet", value: [3, 4, 5, 6, 6, 7, 10, 15, 30] },
|
||||||
|
env
|
||||||
|
);
|
||||||
|
let dist2 = new GenericDist(
|
||||||
|
{ tag: "SampleSet", value: [20, 22, 24, 29, 30, 35, 38, 44, 52] },
|
||||||
|
env
|
||||||
|
);
|
||||||
|
|
||||||
|
test("mean", () => {
|
||||||
|
expect(dist.mean().value).toBeCloseTo(3.737);
|
||||||
|
});
|
||||||
|
test("pdf", () => {
|
||||||
|
expect(dist.pdf(5.0).value).toBeCloseTo(0.0431);
|
||||||
|
});
|
||||||
|
test("cdf", () => {
|
||||||
|
expect(dist.cdf(5.0).value).toBeCloseTo(0.155);
|
||||||
|
});
|
||||||
|
test("inv", () => {
|
||||||
|
expect(dist.inv(0.5).value).toBeCloseTo(9.458);
|
||||||
|
});
|
||||||
|
test("toPointSet", () => {
|
||||||
|
expect(
|
||||||
|
resultMap(dist.toPointSet(), (r: GenericDist) => r.toString()).value.value
|
||||||
|
).toBe("Point Set Distribution");
|
||||||
|
});
|
||||||
|
test("toSparkline", () => {
|
||||||
|
expect(dist.toSparkline(20).value).toBe("▁▁▃▅███▆▄▃▂▁▁▂▂▃▂▁▁▁");
|
||||||
|
});
|
||||||
|
test("algebraicAdd", () => {
|
||||||
|
expect(
|
||||||
|
resultMap(dist.algebraicAdd(dist2), (r: GenericDist) => r.toSparkline(20))
|
||||||
|
.value.value
|
||||||
|
).toBe("▁▁▂▄▆████▇▆▄▄▃▃▃▂▁▁▁");
|
||||||
|
});
|
||||||
|
test("pointwiseAdd", () => {
|
||||||
|
expect(
|
||||||
|
resultMap(dist.pointwiseAdd(dist2), (r: GenericDist) => r.toSparkline(20))
|
||||||
|
.value.value
|
||||||
|
).toBe("▁▂▅██▅▅▅▆▇█▆▅▃▃▂▂▁▁▁");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -9,6 +9,3 @@ let expectParseToBe = (expr: string, answer: string) =>
|
||||||
|
|
||||||
let expectEvalToBe = (expr: string, answer: string) =>
|
let expectEvalToBe = (expr: string, answer: string) =>
|
||||||
Reducer.eval(expr)->ExpressionValue.toStringResult->expect->toBe(answer)
|
Reducer.eval(expr)->ExpressionValue.toStringResult->expect->toBe(answer)
|
||||||
|
|
||||||
// Current configuration does not ignore this file so we have to have a test
|
|
||||||
test("test helpers", () => expect(1)->toBe(1))
|
|
||||||
|
|
|
@ -30,6 +30,9 @@ describe("eval on distribution functions", () => {
|
||||||
testEval("mean(normal(5,2))", "Ok(5)")
|
testEval("mean(normal(5,2))", "Ok(5)")
|
||||||
testEval("mean(lognormal(1,2))", "Ok(20.085536923187668)")
|
testEval("mean(lognormal(1,2))", "Ok(20.085536923187668)")
|
||||||
})
|
})
|
||||||
|
describe("toString", () => {
|
||||||
|
testEval("toString(normal(5,2))", "Ok('Normal(5,2)')")
|
||||||
|
})
|
||||||
describe("normalize", () => {
|
describe("normalize", () => {
|
||||||
testEval("normalize(normal(5,2))", "Ok(Normal(5,2))")
|
testEval("normalize(normal(5,2))", "Ok(Normal(5,2))")
|
||||||
})
|
})
|
||||||
|
|
|
@ -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)
|
|
||||||
})
|
|
||||||
)
|
|
|
@ -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, ()), `▁▁▁▁▁▁▁▁▁▁▂▃▅▇███▇▅▃▂`)
|
|
||||||
})
|
|
26
packages/squiggle-lang/__tests__/TestHelpers.res
Normal file
26
packages/squiggle-lang/__tests__/TestHelpers.res
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
open Jest
|
||||||
|
open Expect
|
||||||
|
|
||||||
|
let makeTest = (~only=false, str, item1, item2) =>
|
||||||
|
only
|
||||||
|
? Only.test(str, () => expect(item1) -> toEqual(item2))
|
||||||
|
: test(str, () => expect(item1) -> toEqual(item2))
|
||||||
|
|
||||||
|
|
||||||
|
let {toFloat, toDist, toString, toError, fmap} = module(DistributionOperation.Output)
|
||||||
|
|
||||||
|
let fnImage = (theFn, inps) => Js.Array.map(theFn, inps)
|
||||||
|
|
||||||
|
let env: DistributionOperation.env = {
|
||||||
|
sampleCount: 100,
|
||||||
|
xyPointLength: 100,
|
||||||
|
}
|
||||||
|
|
||||||
|
let run = DistributionOperation.run(~env)
|
||||||
|
let outputMap = fmap(~env)
|
||||||
|
let unreachableInTestFileMessage = "Should be impossible to reach (This error is in test file)"
|
||||||
|
let toExtFloat: option<float> => float = E.O.toExt(unreachableInTestFileMessage)
|
||||||
|
let toExtDist: option<GenericDist_Types.genericDist> => GenericDist_Types.genericDist = E.O.toExt(unreachableInTestFileMessage)
|
||||||
|
// let toExt: option<'a> => 'a = E.O.toExt(unreachableInTestFileMessage)
|
||||||
|
let unpackFloat = x => x -> toFloat -> toExtFloat
|
||||||
|
let unpackDist = y => y -> toDist -> toExtDist
|
|
@ -33,7 +33,7 @@
|
||||||
"gentypeconfig": {
|
"gentypeconfig": {
|
||||||
"language": "typescript",
|
"language": "typescript",
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"shims": {},
|
"shims": {"Js": "Js"},
|
||||||
"debug": {
|
"debug": {
|
||||||
"all": false,
|
"all": false,
|
||||||
"basic": false
|
"basic": false
|
||||||
|
@ -47,7 +47,7 @@
|
||||||
[
|
[
|
||||||
"../../node_modules/bisect_ppx/ppx",
|
"../../node_modules/bisect_ppx/ppx",
|
||||||
"--exclude-files",
|
"--exclude-files",
|
||||||
".*_Test\\.res$$"
|
".*_test\\.res$$"
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
|
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
|
||||||
module.exports = {
|
module.exports = {
|
||||||
preset: 'ts-jest',
|
preset: "ts-jest",
|
||||||
testEnvironment: 'node',
|
testEnvironment: "node",
|
||||||
setupFilesAfterEnv: [
|
setupFilesAfterEnv: [
|
||||||
"<rootdir>/../../node_modules/bisect_ppx/src/runtime/js/jest.bs.js"
|
"<rootdir>/../../node_modules/bisect_ppx/src/runtime/js/jest.bs.js",
|
||||||
|
],
|
||||||
|
testPathIgnorePatterns: [
|
||||||
|
".*Fixtures.bs.js",
|
||||||
|
"/node_modules/",
|
||||||
|
".*Helpers.bs.js",
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
"test:reducer": "jest --testPathPattern '.*__tests__/Reducer.*'",
|
"test:reducer": "jest --testPathPattern '.*__tests__/Reducer.*'",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"test:watch": "jest --watchAll",
|
"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"
|
"all": "yarn build && yarn bundle && yarn test"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|
|
@ -1,17 +1,228 @@
|
||||||
import {runAll} from '../rescript/ProgramEvaluator.gen';
|
import { runAll } from "../rescript/ProgramEvaluator.gen";
|
||||||
import type { Inputs_SamplingInputs_t as SamplingInputs, exportEnv, exportType, exportDistribution} from '../rescript/ProgramEvaluator.gen';
|
import type {
|
||||||
export type { SamplingInputs, exportEnv, exportDistribution }
|
Inputs_SamplingInputs_t as SamplingInputs,
|
||||||
export type {t as DistPlus} from '../rescript/OldInterpreter/DistPlus.gen';
|
exportEnv,
|
||||||
|
exportType,
|
||||||
|
exportDistribution,
|
||||||
|
} from "../rescript/ProgramEvaluator.gen";
|
||||||
|
export type { SamplingInputs, exportEnv, exportDistribution };
|
||||||
|
export type { t as DistPlus } from "../rescript/OldInterpreter/DistPlus.gen";
|
||||||
|
import {
|
||||||
|
genericDist,
|
||||||
|
env,
|
||||||
|
resultDist,
|
||||||
|
resultFloat,
|
||||||
|
resultString,
|
||||||
|
} from "../rescript/TypescriptInterface.gen";
|
||||||
|
export {makeSampleSetDist} from "../rescript/TypescriptInterface.gen";
|
||||||
|
import {
|
||||||
|
Constructors_mean,
|
||||||
|
Constructors_sample,
|
||||||
|
Constructors_pdf,
|
||||||
|
Constructors_cdf,
|
||||||
|
Constructors_inv,
|
||||||
|
Constructors_normalize,
|
||||||
|
Constructors_toPointSet,
|
||||||
|
Constructors_toSampleSet,
|
||||||
|
Constructors_truncate,
|
||||||
|
Constructors_inspect,
|
||||||
|
Constructors_toString,
|
||||||
|
Constructors_toSparkline,
|
||||||
|
Constructors_algebraicAdd,
|
||||||
|
Constructors_algebraicMultiply,
|
||||||
|
Constructors_algebraicDivide,
|
||||||
|
Constructors_algebraicSubtract,
|
||||||
|
Constructors_algebraicLogarithm,
|
||||||
|
Constructors_algebraicPower,
|
||||||
|
Constructors_pointwiseAdd,
|
||||||
|
Constructors_pointwiseMultiply,
|
||||||
|
Constructors_pointwiseDivide,
|
||||||
|
Constructors_pointwiseSubtract,
|
||||||
|
Constructors_pointwiseLogarithm,
|
||||||
|
Constructors_pointwisePower,
|
||||||
|
} from "../rescript/Distributions/DistributionOperation/DistributionOperation.gen";
|
||||||
|
|
||||||
export let defaultSamplingInputs: SamplingInputs = {
|
export let defaultSamplingInputs: SamplingInputs = {
|
||||||
sampleCount: 10000,
|
sampleCount: 10000,
|
||||||
outputXYPoints: 10000,
|
outputXYPoints: 10000,
|
||||||
pointDistLength : 1000
|
pointDistLength: 1000,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function run(
|
||||||
|
squiggleString: string,
|
||||||
|
samplingInputs?: SamplingInputs,
|
||||||
|
environment?: exportEnv
|
||||||
|
): { tag: "Ok"; value: exportType } | { tag: "Error"; value: string } {
|
||||||
|
let si: SamplingInputs = samplingInputs
|
||||||
|
? samplingInputs
|
||||||
|
: defaultSamplingInputs;
|
||||||
|
let env: exportEnv = environment ? environment : [];
|
||||||
|
return runAll(squiggleString, si, env);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function run(squiggleString : string, samplingInputs? : SamplingInputs, environment?: exportEnv) : { tag: "Ok"; value: exportType }
|
//This is clearly not fully typed. I think later we should use a functional library to
|
||||||
| { tag: "Error"; value: string } {
|
// provide a better Either type and corresponding functions.
|
||||||
let si : SamplingInputs = samplingInputs ? samplingInputs : defaultSamplingInputs
|
type result =
|
||||||
let env : exportEnv = environment ? environment : []
|
| {
|
||||||
return runAll(squiggleString, si, env)
|
tag: "Ok";
|
||||||
|
value: any;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
tag: "Error";
|
||||||
|
value: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function resultMap(r: result, mapFn: any): result {
|
||||||
|
if (r.tag === "Ok") {
|
||||||
|
return { tag: "Ok", value: mapFn(r.value) };
|
||||||
|
} else {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resultExn(r: result): any {
|
||||||
|
r.value
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GenericDist {
|
||||||
|
t: genericDist;
|
||||||
|
env: env;
|
||||||
|
|
||||||
|
constructor(t: genericDist, env: env) {
|
||||||
|
this.t = t;
|
||||||
|
this.env = env;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
mapResultDist(r: resultDist) {
|
||||||
|
return resultMap(r, (v: genericDist) => new GenericDist(v, this.env));
|
||||||
|
}
|
||||||
|
|
||||||
|
mean() {
|
||||||
|
return Constructors_mean({ env: this.env }, this.t);
|
||||||
|
}
|
||||||
|
|
||||||
|
sample(): resultFloat {
|
||||||
|
return Constructors_sample({ env: this.env }, this.t);
|
||||||
|
}
|
||||||
|
|
||||||
|
pdf(n: number): resultFloat {
|
||||||
|
return Constructors_pdf({ env: this.env }, this.t, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
cdf(n: number): resultFloat {
|
||||||
|
return Constructors_cdf({ env: this.env }, this.t, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
inv(n: number): resultFloat {
|
||||||
|
return Constructors_inv({ env: this.env }, this.t, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
normalize() {
|
||||||
|
return this.mapResultDist(
|
||||||
|
Constructors_normalize({ env: this.env }, this.t)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
toPointSet() {
|
||||||
|
return this.mapResultDist(
|
||||||
|
Constructors_toPointSet({ env: this.env }, this.t)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
toSampleSet(n: number) {
|
||||||
|
return this.mapResultDist(
|
||||||
|
Constructors_toSampleSet({ env: this.env }, this.t, n)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
truncate(left: number, right: number) {
|
||||||
|
return this.mapResultDist(
|
||||||
|
Constructors_truncate({ env: this.env }, this.t, left, right)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
inspect() {
|
||||||
|
return this.mapResultDist(Constructors_inspect({ env: this.env }, this.t));
|
||||||
|
}
|
||||||
|
|
||||||
|
toString(): resultString {
|
||||||
|
return Constructors_toString({ env: this.env }, this.t);
|
||||||
|
}
|
||||||
|
|
||||||
|
toSparkline(n: number): resultString {
|
||||||
|
return Constructors_toSparkline({ env: this.env }, this.t, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
algebraicAdd(d2: GenericDist) {
|
||||||
|
return this.mapResultDist(
|
||||||
|
Constructors_algebraicAdd({ env: this.env }, this.t, d2.t)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
algebraicMultiply(d2: GenericDist) {
|
||||||
|
return this.mapResultDist(
|
||||||
|
Constructors_algebraicMultiply({ env: this.env }, this.t, d2.t)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
algebraicDivide(d2: GenericDist) {
|
||||||
|
return this.mapResultDist(
|
||||||
|
Constructors_algebraicDivide({ env: this.env }, this.t, d2.t)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
algebraicSubtract(d2: GenericDist) {
|
||||||
|
return this.mapResultDist(
|
||||||
|
Constructors_algebraicSubtract({ env: this.env }, this.t, d2.t)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
algebraicLogarithm(d2: GenericDist) {
|
||||||
|
return this.mapResultDist(
|
||||||
|
Constructors_algebraicLogarithm({ env: this.env }, this.t, d2.t)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
algebraicPower(d2: GenericDist) {
|
||||||
|
return this.mapResultDist(
|
||||||
|
Constructors_algebraicPower({ env: this.env }, this.t, d2.t)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pointwiseAdd(d2: GenericDist) {
|
||||||
|
return this.mapResultDist(
|
||||||
|
Constructors_pointwiseAdd({ env: this.env }, this.t, d2.t)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pointwiseMultiply(d2: GenericDist) {
|
||||||
|
return this.mapResultDist(
|
||||||
|
Constructors_pointwiseMultiply({ env: this.env }, this.t, d2.t)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pointwiseDivide(d2: GenericDist) {
|
||||||
|
return this.mapResultDist(
|
||||||
|
Constructors_pointwiseDivide({ env: this.env }, this.t, d2.t)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pointwiseSubtract(d2: GenericDist) {
|
||||||
|
return this.mapResultDist(
|
||||||
|
Constructors_pointwiseSubtract({ env: this.env }, this.t, d2.t)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pointwiseLogarithm(d2: GenericDist) {
|
||||||
|
return this.mapResultDist(
|
||||||
|
Constructors_pointwiseLogarithm({ env: this.env }, this.t, d2.t)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pointwisePower(d2: GenericDist) {
|
||||||
|
return this.mapResultDist(
|
||||||
|
Constructors_pointwisePower({ env: this.env }, this.t, d2.t)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,10 +10,10 @@ type env = {
|
||||||
}
|
}
|
||||||
|
|
||||||
type outputType =
|
type outputType =
|
||||||
| Dist(GenericDist_Types.genericDist)
|
| Dist(genericDist)
|
||||||
| Float(float)
|
| Float(float)
|
||||||
| String(string)
|
| String(string)
|
||||||
| GenDistError(GenericDist_Types.error)
|
| GenDistError(error)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
We're going to add another function to this module later, so first define a
|
We're going to add another function to this module later, so first define a
|
||||||
|
@ -113,7 +113,11 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => {
|
||||||
GenericDist.toFloatOperation(dist, ~toPointSetFn, ~distToFloatOperation)
|
GenericDist.toFloatOperation(dist, ~toPointSetFn, ~distToFloatOperation)
|
||||||
->E.R2.fmap(r => Float(r))
|
->E.R2.fmap(r => Float(r))
|
||||||
->OutputLocal.fromResult
|
->OutputLocal.fromResult
|
||||||
| ToString => dist->GenericDist.toString->String
|
| ToString(ToString) => dist->GenericDist.toString->String
|
||||||
|
| ToString(ToSparkline(bucketCount)) =>
|
||||||
|
GenericDist.toSparkline(dist, ~sampleCount, ~bucketCount, ())
|
||||||
|
->E.R2.fmap(r => String(r))
|
||||||
|
->OutputLocal.fromResult
|
||||||
| ToDist(Inspect) => {
|
| ToDist(Inspect) => {
|
||||||
Js.log2("Console log requested: ", dist)
|
Js.log2("Console log requested: ", dist)
|
||||||
Dist(dist)
|
Dist(dist)
|
||||||
|
@ -124,10 +128,13 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => {
|
||||||
->E.R2.fmap(r => Dist(r))
|
->E.R2.fmap(r => Dist(r))
|
||||||
->OutputLocal.fromResult
|
->OutputLocal.fromResult
|
||||||
| ToDist(ToSampleSet(n)) =>
|
| ToDist(ToSampleSet(n)) =>
|
||||||
dist->GenericDist.sampleN(n)->E.R2.fmap(r => Dist(SampleSet(r)))->OutputLocal.fromResult
|
dist
|
||||||
|
->GenericDist.toSampleSetDist(n)
|
||||||
|
->E.R2.fmap(r => Dist(SampleSet(r)))
|
||||||
|
->OutputLocal.fromResult
|
||||||
| ToDist(ToPointSet) =>
|
| ToDist(ToPointSet) =>
|
||||||
dist
|
dist
|
||||||
->GenericDist.toPointSet(~xyPointLength, ~sampleCount)
|
->GenericDist.toPointSet(~xyPointLength, ~sampleCount, ())
|
||||||
->E.R2.fmap(r => Dist(PointSet(r)))
|
->E.R2.fmap(r => Dist(PointSet(r)))
|
||||||
->OutputLocal.fromResult
|
->OutputLocal.fromResult
|
||||||
| ToDistCombination(Algebraic, _, #Float(_)) => GenDistError(NotYetImplemented)
|
| ToDistCombination(Algebraic, _, #Float(_)) => GenDistError(NotYetImplemented)
|
||||||
|
@ -181,3 +188,43 @@ module Output = {
|
||||||
newFnCall->E.R2.fmap(run(~env))->OutputLocal.fromResult
|
newFnCall->E.R2.fmap(run(~env))->OutputLocal.fromResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// See comment above GenericDist_Types.Constructors to explain the purpose of this module.
|
||||||
|
// I tried having another internal module called UsingDists, similar to how its done in
|
||||||
|
// GenericDist_Types.Constructors. However, this broke GenType for me, so beware.
|
||||||
|
module Constructors = {
|
||||||
|
module C = GenericDist_Types.Constructors.UsingDists
|
||||||
|
open OutputLocal
|
||||||
|
let mean = (~env, dist) => C.mean(dist)->run(~env)->toFloatR
|
||||||
|
let sample = (~env, dist) => C.sample(dist)->run(~env)->toFloatR
|
||||||
|
let cdf = (~env, dist, f) => C.cdf(dist, f)->run(~env)->toFloatR
|
||||||
|
let inv = (~env, dist, f) => C.inv(dist, f)->run(~env)->toFloatR
|
||||||
|
let pdf = (~env, dist, f) => C.pdf(dist, f)->run(~env)->toFloatR
|
||||||
|
let normalize = (~env, dist) => C.normalize(dist)->run(~env)->toDistR
|
||||||
|
let toPointSet = (~env, dist) => C.toPointSet(dist)->run(~env)->toDistR
|
||||||
|
let toSampleSet = (~env, dist, n) => C.toSampleSet(dist, n)->run(~env)->toDistR
|
||||||
|
let truncate = (~env, dist, leftCutoff, rightCutoff) =>
|
||||||
|
C.truncate(dist, leftCutoff, rightCutoff)->run(~env)->toDistR
|
||||||
|
let inspect = (~env, dist) => C.inspect(dist)->run(~env)->toDistR
|
||||||
|
let toString = (~env, dist) => C.toString(dist)->run(~env)->toStringR
|
||||||
|
let toSparkline = (~env, dist, bucketCount) =>
|
||||||
|
C.toSparkline(dist, bucketCount)->run(~env)->toStringR
|
||||||
|
let algebraicAdd = (~env, dist1, dist2) => C.algebraicAdd(dist1, dist2)->run(~env)->toDistR
|
||||||
|
let algebraicMultiply = (~env, dist1, dist2) =>
|
||||||
|
C.algebraicMultiply(dist1, dist2)->run(~env)->toDistR
|
||||||
|
let algebraicDivide = (~env, dist1, dist2) => C.algebraicDivide(dist1, dist2)->run(~env)->toDistR
|
||||||
|
let algebraicSubtract = (~env, dist1, dist2) =>
|
||||||
|
C.algebraicSubtract(dist1, dist2)->run(~env)->toDistR
|
||||||
|
let algebraicLogarithm = (~env, dist1, dist2) =>
|
||||||
|
C.algebraicLogarithm(dist1, dist2)->run(~env)->toDistR
|
||||||
|
let algebraicPower = (~env, dist1, dist2) => C.algebraicPower(dist1, dist2)->run(~env)->toDistR
|
||||||
|
let pointwiseAdd = (~env, dist1, dist2) => C.pointwiseAdd(dist1, dist2)->run(~env)->toDistR
|
||||||
|
let pointwiseMultiply = (~env, dist1, dist2) =>
|
||||||
|
C.pointwiseMultiply(dist1, dist2)->run(~env)->toDistR
|
||||||
|
let pointwiseDivide = (~env, dist1, dist2) => C.pointwiseDivide(dist1, dist2)->run(~env)->toDistR
|
||||||
|
let pointwiseSubtract = (~env, dist1, dist2) =>
|
||||||
|
C.pointwiseSubtract(dist1, dist2)->run(~env)->toDistR
|
||||||
|
let pointwiseLogarithm = (~env, dist1, dist2) =>
|
||||||
|
C.pointwiseLogarithm(dist1, dist2)->run(~env)->toDistR
|
||||||
|
let pointwisePower = (~env, dist1, dist2) => C.pointwisePower(dist1, dist2)->run(~env)->toDistR
|
||||||
|
}
|
||||||
|
|
|
@ -1,19 +1,24 @@
|
||||||
|
@genType
|
||||||
type env = {
|
type env = {
|
||||||
sampleCount: int,
|
sampleCount: int,
|
||||||
xyPointLength: int,
|
xyPointLength: int,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open GenericDist_Types
|
||||||
|
|
||||||
|
@genType
|
||||||
type outputType =
|
type outputType =
|
||||||
| Dist(GenericDist_Types.genericDist)
|
| Dist(genericDist)
|
||||||
| Float(float)
|
| Float(float)
|
||||||
| String(string)
|
| String(string)
|
||||||
| GenDistError(GenericDist_Types.error)
|
| GenDistError(error)
|
||||||
|
|
||||||
|
@genType
|
||||||
let run: (~env: env, GenericDist_Types.Operation.genericFunctionCallInfo) => outputType
|
let run: (~env: env, GenericDist_Types.Operation.genericFunctionCallInfo) => outputType
|
||||||
let runFromDist: (
|
let runFromDist: (
|
||||||
~env: env,
|
~env: env,
|
||||||
~functionCallInfo: GenericDist_Types.Operation.fromDist,
|
~functionCallInfo: GenericDist_Types.Operation.fromDist,
|
||||||
GenericDist_Types.genericDist,
|
genericDist,
|
||||||
) => outputType
|
) => outputType
|
||||||
let runFromFloat: (
|
let runFromFloat: (
|
||||||
~env: env,
|
~env: env,
|
||||||
|
@ -23,12 +28,68 @@ let runFromFloat: (
|
||||||
|
|
||||||
module Output: {
|
module Output: {
|
||||||
type t = outputType
|
type t = outputType
|
||||||
let toDist: t => option<GenericDist_Types.genericDist>
|
let toDist: t => option<genericDist>
|
||||||
let toDistR: t => result<GenericDist_Types.genericDist, GenericDist_Types.error>
|
let toDistR: t => result<genericDist, error>
|
||||||
let toFloat: t => option<float>
|
let toFloat: t => option<float>
|
||||||
let toFloatR: t => result<float, GenericDist_Types.error>
|
let toFloatR: t => result<float, error>
|
||||||
let toString: t => option<string>
|
let toString: t => option<string>
|
||||||
let toStringR: t => result<string, GenericDist_Types.error>
|
let toStringR: t => result<string, error>
|
||||||
let toError: t => option<GenericDist_Types.error>
|
let toError: t => option<error>
|
||||||
let fmap: (~env: env, t, GenericDist_Types.Operation.singleParamaterFunction) => t
|
let fmap: (~env: env, t, GenericDist_Types.Operation.singleParamaterFunction) => t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module Constructors: {
|
||||||
|
@genType
|
||||||
|
let mean: (~env: env, genericDist) => result<float, error>
|
||||||
|
@genType
|
||||||
|
let sample: (~env: env, genericDist) => result<float, error>
|
||||||
|
@genType
|
||||||
|
let cdf: (~env: env, genericDist, float) => result<float, error>
|
||||||
|
@genType
|
||||||
|
let inv: (~env: env, genericDist, float) => result<float, error>
|
||||||
|
@genType
|
||||||
|
let pdf: (~env: env, genericDist, float) => result<float, error>
|
||||||
|
@genType
|
||||||
|
let normalize: (~env: env, genericDist) => result<genericDist, error>
|
||||||
|
@genType
|
||||||
|
let toPointSet: (~env: env, genericDist) => result<genericDist, error>
|
||||||
|
@genType
|
||||||
|
let toSampleSet: (~env: env, genericDist, int) => result<genericDist, error>
|
||||||
|
@genType
|
||||||
|
let truncate: (
|
||||||
|
~env: env,
|
||||||
|
genericDist,
|
||||||
|
option<float>,
|
||||||
|
option<float>,
|
||||||
|
) => result<genericDist, error>
|
||||||
|
@genType
|
||||||
|
let inspect: (~env: env, genericDist) => result<genericDist, error>
|
||||||
|
@genType
|
||||||
|
let toString: (~env: env, genericDist) => result<string, error>
|
||||||
|
@genType
|
||||||
|
let toSparkline: (~env: env, genericDist, int) => result<string, error>
|
||||||
|
@genType
|
||||||
|
let algebraicAdd: (~env: env, genericDist, genericDist) => result<genericDist, error>
|
||||||
|
@genType
|
||||||
|
let algebraicMultiply: (~env: env, genericDist, genericDist) => result<genericDist, error>
|
||||||
|
@genType
|
||||||
|
let algebraicDivide: (~env: env, genericDist, genericDist) => result<genericDist, error>
|
||||||
|
@genType
|
||||||
|
let algebraicSubtract: (~env: env, genericDist, genericDist) => result<genericDist, error>
|
||||||
|
@genType
|
||||||
|
let algebraicLogarithm: (~env: env, genericDist, genericDist) => result<genericDist, error>
|
||||||
|
@genType
|
||||||
|
let algebraicPower: (~env: env, genericDist, genericDist) => result<genericDist, error>
|
||||||
|
@genType
|
||||||
|
let pointwiseAdd: (~env: env, genericDist, genericDist) => result<genericDist, error>
|
||||||
|
@genType
|
||||||
|
let pointwiseMultiply: (~env: env, genericDist, genericDist) => result<genericDist, error>
|
||||||
|
@genType
|
||||||
|
let pointwiseDivide: (~env: env, genericDist, genericDist) => result<genericDist, error>
|
||||||
|
@genType
|
||||||
|
let pointwiseSubtract: (~env: env, genericDist, genericDist) => result<genericDist, error>
|
||||||
|
@genType
|
||||||
|
let pointwiseLogarithm: (~env: env, genericDist, genericDist) => result<genericDist, error>
|
||||||
|
@genType
|
||||||
|
let pointwisePower: (~env: env, genericDist, genericDist) => result<genericDist, error>
|
||||||
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ module Operation = {
|
||||||
| #Multiply
|
| #Multiply
|
||||||
| #Subtract
|
| #Subtract
|
||||||
| #Divide
|
| #Divide
|
||||||
| #Exponentiate
|
| #Power
|
||||||
| #Logarithm
|
| #Logarithm
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ module Operation = {
|
||||||
| #Add => \"+."
|
| #Add => \"+."
|
||||||
| #Multiply => \"*."
|
| #Multiply => \"*."
|
||||||
| #Subtract => \"-."
|
| #Subtract => \"-."
|
||||||
| #Exponentiate => \"**"
|
| #Power => \"**"
|
||||||
| #Divide => \"/."
|
| #Divide => \"/."
|
||||||
| #Logarithm => (a, b) => log(a) /. log(b)
|
| #Logarithm => (a, b) => log(a) /. log(b)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,17 +2,20 @@
|
||||||
type t = GenericDist_Types.genericDist
|
type t = GenericDist_Types.genericDist
|
||||||
type error = GenericDist_Types.error
|
type error = GenericDist_Types.error
|
||||||
type toPointSetFn = t => result<PointSetTypes.pointSetDist, error>
|
type toPointSetFn = t => result<PointSetTypes.pointSetDist, error>
|
||||||
type toSampleSetFn = t => result<array<float>, error>
|
type toSampleSetFn = t => result<SampleSetDist.t, error>
|
||||||
type scaleMultiplyFn = (t, float) => result<t, error>
|
type scaleMultiplyFn = (t, float) => result<t, error>
|
||||||
type pointwiseAddFn = (t, t) => result<t, error>
|
type pointwiseAddFn = (t, t) => result<t, error>
|
||||||
|
|
||||||
let sampleN = (t: t, n) =>
|
let sampleN = (t: t, n) =>
|
||||||
switch t {
|
switch t {
|
||||||
| PointSet(r) => Ok(PointSetDist.sampleNRendered(n, r))
|
| PointSet(r) => PointSetDist.sampleNRendered(n, r)
|
||||||
| Symbolic(r) => Ok(SymbolicDist.T.sampleN(n, r))
|
| Symbolic(r) => SymbolicDist.T.sampleN(n, r)
|
||||||
| SampleSet(_) => Error(GenericDist_Types.NotYetImplemented)
|
| SampleSet(r) => SampleSetDist.sampleN(r, n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let toSampleSetDist = (t: t, n) =>
|
||||||
|
SampleSetDist.make(sampleN(t, n))->GenericDist_Types.Error.resultStringToResultError
|
||||||
|
|
||||||
let fromFloat = (f: float): t => Symbolic(SymbolicDist.Float.make(f))
|
let fromFloat = (f: float): t => Symbolic(SymbolicDist.Float.make(f))
|
||||||
|
|
||||||
let toString = (t: t) =>
|
let toString = (t: t) =>
|
||||||
|
@ -49,15 +52,21 @@ let toFloatOperation = (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Todo: If it's a pointSet, but the xyPointLenght is different from what it has, it should change.
|
//Todo: If it's a pointSet, but the xyPointLength is different from what it has, it should change.
|
||||||
// This is tricky because the case of discrete distributions.
|
// This is tricky because the case of discrete distributions.
|
||||||
// Also, change the outputXYPoints/pointSetDistLength details
|
// Also, change the outputXYPoints/pointSetDistLength details
|
||||||
let toPointSet = (~xyPointLength, ~sampleCount, t): result<PointSetTypes.pointSetDist, error> => {
|
let toPointSet = (
|
||||||
|
t,
|
||||||
|
~xyPointLength,
|
||||||
|
~sampleCount,
|
||||||
|
~xSelection: GenericDist_Types.Operation.pointsetXSelection=#ByWeight,
|
||||||
|
unit,
|
||||||
|
): result<PointSetTypes.pointSetDist, error> => {
|
||||||
switch (t: t) {
|
switch (t: t) {
|
||||||
| PointSet(pointSet) => Ok(pointSet)
|
| PointSet(pointSet) => Ok(pointSet)
|
||||||
| Symbolic(r) => Ok(SymbolicDist.T.toPointSetDist(xyPointLength, r))
|
| Symbolic(r) => Ok(SymbolicDist.T.toPointSetDist(~xSelection, xyPointLength, r))
|
||||||
| SampleSet(r) => {
|
| SampleSet(r) =>
|
||||||
let response = SampleSet.toPointSetDist(
|
SampleSetDist.toPointSetDist(
|
||||||
~samples=r,
|
~samples=r,
|
||||||
~samplingInputs={
|
~samplingInputs={
|
||||||
sampleCount: sampleCount,
|
sampleCount: sampleCount,
|
||||||
|
@ -65,16 +74,23 @@ let toPointSet = (~xyPointLength, ~sampleCount, t): result<PointSetTypes.pointSe
|
||||||
pointSetDistLength: xyPointLength,
|
pointSetDistLength: xyPointLength,
|
||||||
kernelWidth: None,
|
kernelWidth: None,
|
||||||
},
|
},
|
||||||
(),
|
)->GenericDist_Types.Error.resultStringToResultError
|
||||||
).pointSetDist
|
|
||||||
switch response {
|
|
||||||
| Some(r) => Ok(r)
|
|
||||||
| None => Error(Other("Converting sampleSet to pointSet failed"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
PointSetDist.toSparkline calls "downsampleEquallyOverX", which downsamples it to n=bucketCount.
|
||||||
|
It first needs a pointSetDist, so we convert to a pointSetDist. In this process we want the
|
||||||
|
xyPointLength to be a bit longer than the eventual toSparkline downsampling. I chose 3
|
||||||
|
fairly arbitrarily.
|
||||||
|
*/
|
||||||
|
let toSparkline = (t: t, ~sampleCount: int, ~bucketCount: int=20, unit): result<string, error> =>
|
||||||
|
t
|
||||||
|
->toPointSet(~xSelection=#Linear, ~xyPointLength=bucketCount * 3, ~sampleCount, ())
|
||||||
|
->E.R.bind(r =>
|
||||||
|
r->PointSetDist.toSparkline(bucketCount)->GenericDist_Types.Error.resultStringToResultError
|
||||||
|
)
|
||||||
|
|
||||||
module Truncate = {
|
module Truncate = {
|
||||||
let trySymbolicSimplification = (leftCutoff, rightCutoff, t: t): option<t> =>
|
let trySymbolicSimplification = (leftCutoff, rightCutoff, t: t): option<t> =>
|
||||||
switch (leftCutoff, rightCutoff, t) {
|
switch (leftCutoff, rightCutoff, t) {
|
||||||
|
@ -147,10 +163,12 @@ module AlgebraicCombination = {
|
||||||
t1: t,
|
t1: t,
|
||||||
t2: t,
|
t2: t,
|
||||||
) => {
|
) => {
|
||||||
let arithmeticOperation = Operation.Algebraic.toFn(arithmeticOperation)
|
let fn = Operation.Algebraic.toFn(arithmeticOperation)
|
||||||
E.R.merge(toSampleSet(t1), toSampleSet(t2))->E.R2.fmap(((a, b)) => {
|
E.R.merge(toSampleSet(t1), toSampleSet(t2))
|
||||||
Belt.Array.zip(a, b)->E.A2.fmap(((a, b)) => arithmeticOperation(a, b))
|
->E.R.bind(((t1, t2)) => {
|
||||||
|
SampleSetDist.map2(~fn, ~t1, ~t2)->GenericDist_Types.Error.resultStringToResultError
|
||||||
})
|
})
|
||||||
|
->E.R2.fmap(r => GenericDist_Types.SampleSet(r))
|
||||||
}
|
}
|
||||||
|
|
||||||
//I'm (Ozzie) really just guessing here, very little idea what's best
|
//I'm (Ozzie) really just guessing here, very little idea what's best
|
||||||
|
@ -181,13 +199,7 @@ module AlgebraicCombination = {
|
||||||
| Some(Error(e)) => Error(Other(e))
|
| Some(Error(e)) => Error(Other(e))
|
||||||
| None =>
|
| None =>
|
||||||
switch chooseConvolutionOrMonteCarlo(t1, t2) {
|
switch chooseConvolutionOrMonteCarlo(t1, t2) {
|
||||||
| #CalculateWithMonteCarlo =>
|
| #CalculateWithMonteCarlo => runMonteCarlo(toSampleSetFn, arithmeticOperation, t1, t2)
|
||||||
runMonteCarlo(
|
|
||||||
toSampleSetFn,
|
|
||||||
arithmeticOperation,
|
|
||||||
t1,
|
|
||||||
t2,
|
|
||||||
)->E.R2.fmap(r => GenericDist_Types.SampleSet(r))
|
|
||||||
| #CalculateWithConvolution =>
|
| #CalculateWithConvolution =>
|
||||||
runConvolution(
|
runConvolution(
|
||||||
toPointSetFn,
|
toPointSetFn,
|
||||||
|
@ -228,7 +240,7 @@ let pointwiseCombinationFloat = (
|
||||||
): result<t, error> => {
|
): result<t, error> => {
|
||||||
let m = switch arithmeticOperation {
|
let m = switch arithmeticOperation {
|
||||||
| #Add | #Subtract => Error(GenericDist_Types.DistributionVerticalShiftIsInvalid)
|
| #Add | #Subtract => Error(GenericDist_Types.DistributionVerticalShiftIsInvalid)
|
||||||
| (#Multiply | #Divide | #Exponentiate | #Logarithm) as arithmeticOperation =>
|
| (#Multiply | #Divide | #Power | #Logarithm) as arithmeticOperation =>
|
||||||
toPointSetFn(t)->E.R2.fmap(t => {
|
toPointSetFn(t)->E.R2.fmap(t => {
|
||||||
//TODO: Move to PointSet codebase
|
//TODO: Move to PointSet codebase
|
||||||
let fn = (secondary, main) => Operation.Scale.toFn(arithmeticOperation, main, secondary)
|
let fn = (secondary, main) => Operation.Scale.toFn(arithmeticOperation, main, secondary)
|
||||||
|
@ -253,7 +265,7 @@ let mixture = (
|
||||||
~pointwiseAddFn: pointwiseAddFn,
|
~pointwiseAddFn: pointwiseAddFn,
|
||||||
) => {
|
) => {
|
||||||
if E.A.length(values) == 0 {
|
if E.A.length(values) == 0 {
|
||||||
Error(GenericDist_Types.Other("mixture must have at least 1 element"))
|
Error(GenericDist_Types.Other("Mixture error: mixture must have at least 1 element"))
|
||||||
} else {
|
} else {
|
||||||
let totalWeight = values->E.A2.fmap(E.Tuple2.second)->E.A.Floats.sum
|
let totalWeight = values->E.A2.fmap(E.Tuple2.second)->E.A.Floats.sum
|
||||||
let properlyWeightedValues =
|
let properlyWeightedValues =
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
type t = GenericDist_Types.genericDist
|
type t = GenericDist_Types.genericDist
|
||||||
type error = GenericDist_Types.error
|
type error = GenericDist_Types.error
|
||||||
type toPointSetFn = t => result<PointSetTypes.pointSetDist, error>
|
type toPointSetFn = t => result<PointSetTypes.pointSetDist, error>
|
||||||
type toSampleSetFn = t => result<array<float>, error>
|
type toSampleSetFn = t => result<SampleSetDist.t, error>
|
||||||
type scaleMultiplyFn = (t, float) => result<t, error>
|
type scaleMultiplyFn = (t, float) => result<t, error>
|
||||||
type pointwiseAddFn = (t, t) => result<t, error>
|
type pointwiseAddFn = (t, t) => result<t, error>
|
||||||
|
|
||||||
let sampleN: (t, int) => result<array<float>, error>
|
let sampleN: (t, int) => array<float>
|
||||||
|
|
||||||
|
let toSampleSetDist: (t, int) => Belt.Result.t<QuriSquiggleLang.SampleSetDist.t, error>
|
||||||
|
|
||||||
let fromFloat: float => t
|
let fromFloat: float => t
|
||||||
|
|
||||||
|
@ -20,17 +22,20 @@ let toFloatOperation: (
|
||||||
) => result<float, error>
|
) => result<float, error>
|
||||||
|
|
||||||
let toPointSet: (
|
let toPointSet: (
|
||||||
|
t,
|
||||||
~xyPointLength: int,
|
~xyPointLength: int,
|
||||||
~sampleCount: int,
|
~sampleCount: int,
|
||||||
t,
|
~xSelection: GenericDist_Types.Operation.pointsetXSelection=?,
|
||||||
|
unit,
|
||||||
) => result<PointSetTypes.pointSetDist, error>
|
) => result<PointSetTypes.pointSetDist, error>
|
||||||
|
let toSparkline: (t, ~sampleCount: int, ~bucketCount: int=?, unit) => result<string, error>
|
||||||
|
|
||||||
let truncate: (
|
let truncate: (
|
||||||
t,
|
t,
|
||||||
~toPointSetFn: toPointSetFn,
|
~toPointSetFn: toPointSetFn,
|
||||||
~leftCutoff: option<float>=?,
|
~leftCutoff: option<float>=?,
|
||||||
~rightCutoff: option<float>=?,
|
~rightCutoff: option<float>=?,
|
||||||
unit
|
unit,
|
||||||
) => result<t, error>
|
) => result<t, error>
|
||||||
|
|
||||||
let algebraicCombination: (
|
let algebraicCombination: (
|
||||||
|
|
|
@ -1,14 +1,24 @@
|
||||||
type genericDist =
|
type genericDist =
|
||||||
| PointSet(PointSetTypes.pointSetDist)
|
| PointSet(PointSetTypes.pointSetDist)
|
||||||
| SampleSet(array<float>)
|
| SampleSet(SampleSetDist.t)
|
||||||
| Symbolic(SymbolicDistTypes.symbolicDist)
|
| Symbolic(SymbolicDistTypes.symbolicDist)
|
||||||
|
|
||||||
|
@genType
|
||||||
type error =
|
type error =
|
||||||
| NotYetImplemented
|
| NotYetImplemented
|
||||||
| Unreachable
|
| Unreachable
|
||||||
| DistributionVerticalShiftIsInvalid
|
| DistributionVerticalShiftIsInvalid
|
||||||
| Other(string)
|
| Other(string)
|
||||||
|
|
||||||
|
module Error = {
|
||||||
|
type t = error
|
||||||
|
|
||||||
|
let fromString = (s: string): t => Other(s)
|
||||||
|
|
||||||
|
let resultStringToResultError: result<'a, string> => result<'a, error> = n =>
|
||||||
|
n->E.R2.errMap(r => r->fromString->Error)
|
||||||
|
}
|
||||||
|
|
||||||
module Operation = {
|
module Operation = {
|
||||||
type direction =
|
type direction =
|
||||||
| Algebraic
|
| Algebraic
|
||||||
|
@ -19,7 +29,7 @@ module Operation = {
|
||||||
| #Multiply
|
| #Multiply
|
||||||
| #Subtract
|
| #Subtract
|
||||||
| #Divide
|
| #Divide
|
||||||
| #Exponentiate
|
| #Power
|
||||||
| #Logarithm
|
| #Logarithm
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -28,7 +38,7 @@ module Operation = {
|
||||||
| #Add => \"+."
|
| #Add => \"+."
|
||||||
| #Multiply => \"*."
|
| #Multiply => \"*."
|
||||||
| #Subtract => \"-."
|
| #Subtract => \"-."
|
||||||
| #Exponentiate => \"**"
|
| #Power => \"**"
|
||||||
| #Divide => \"/."
|
| #Divide => \"/."
|
||||||
| #Logarithm => (a, b) => log(a) /. log(b)
|
| #Logarithm => (a, b) => log(a) /. log(b)
|
||||||
}
|
}
|
||||||
|
@ -41,6 +51,8 @@ module Operation = {
|
||||||
| #Sample
|
| #Sample
|
||||||
]
|
]
|
||||||
|
|
||||||
|
type pointsetXSelection = [#Linear | #ByWeight]
|
||||||
|
|
||||||
type toDist =
|
type toDist =
|
||||||
| Normalize
|
| Normalize
|
||||||
| ToPointSet
|
| ToPointSet
|
||||||
|
@ -50,16 +62,21 @@ module Operation = {
|
||||||
|
|
||||||
type toFloatArray = Sample(int)
|
type toFloatArray = Sample(int)
|
||||||
|
|
||||||
|
type toString =
|
||||||
|
| ToString
|
||||||
|
| ToSparkline(int)
|
||||||
|
|
||||||
type fromDist =
|
type fromDist =
|
||||||
| ToFloat(toFloat)
|
| ToFloat(toFloat)
|
||||||
| ToDist(toDist)
|
| ToDist(toDist)
|
||||||
| ToDistCombination(direction, arithmeticOperation, [#Dist(genericDist) | #Float(float)])
|
| ToDistCombination(direction, arithmeticOperation, [#Dist(genericDist) | #Float(float)])
|
||||||
| ToString
|
| ToString(toString)
|
||||||
|
|
||||||
type singleParamaterFunction =
|
type singleParamaterFunction =
|
||||||
| FromDist(fromDist)
|
| FromDist(fromDist)
|
||||||
| FromFloat(fromDist)
|
| FromFloat(fromDist)
|
||||||
|
|
||||||
|
@genType
|
||||||
type genericFunctionCallInfo =
|
type genericFunctionCallInfo =
|
||||||
| FromDist(fromDist, genericDist)
|
| FromDist(fromDist, genericDist)
|
||||||
| FromFloat(fromDist, float)
|
| FromFloat(fromDist, float)
|
||||||
|
@ -77,7 +94,8 @@ module Operation = {
|
||||||
| ToDist(ToSampleSet(r)) => `toSampleSet(${E.I.toString(r)})`
|
| ToDist(ToSampleSet(r)) => `toSampleSet(${E.I.toString(r)})`
|
||||||
| ToDist(Truncate(_, _)) => `truncate`
|
| ToDist(Truncate(_, _)) => `truncate`
|
||||||
| ToDist(Inspect) => `inspect`
|
| ToDist(Inspect) => `inspect`
|
||||||
| ToString => `toString`
|
| ToString(ToString) => `toString`
|
||||||
|
| ToString(ToSparkline(n)) => `toSparkline(${E.I.toString(n)})`
|
||||||
| ToDistCombination(Algebraic, _, _) => `algebraic`
|
| ToDistCombination(Algebraic, _, _) => `algebraic`
|
||||||
| ToDistCombination(Pointwise, _, _) => `pointwise`
|
| ToDistCombination(Pointwise, _, _) => `pointwise`
|
||||||
}
|
}
|
||||||
|
@ -88,3 +106,79 @@ module Operation = {
|
||||||
| Mixture(_) => `mixture`
|
| Mixture(_) => `mixture`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
It can be a pain to write out the genericFunctionCallInfo. The constructors help with this.
|
||||||
|
This code only covers some of genericFunctionCallInfo: many arguments could be called with either a
|
||||||
|
float or a distribution. The "UsingDists" module assumes that everything is a distribution.
|
||||||
|
This is a tradeoff of some generality in order to get a bit more simplicity.
|
||||||
|
I could see having a longer interface in the future, but it could be messy.
|
||||||
|
Like, algebraicAddDistFloat vs. algebraicAddDistDist
|
||||||
|
*/
|
||||||
|
module Constructors = {
|
||||||
|
type t = Operation.genericFunctionCallInfo
|
||||||
|
|
||||||
|
module UsingDists = {
|
||||||
|
@genType
|
||||||
|
let mean = (dist): t => FromDist(ToFloat(#Mean), dist)
|
||||||
|
let sample = (dist): t => FromDist(ToFloat(#Sample), dist)
|
||||||
|
let cdf = (dist, x): t => FromDist(ToFloat(#Cdf(x)), dist)
|
||||||
|
let inv = (dist, x): t => FromDist(ToFloat(#Inv(x)), dist)
|
||||||
|
let pdf = (dist, x): t => FromDist(ToFloat(#Pdf(x)), dist)
|
||||||
|
let normalize = (dist): t => FromDist(ToDist(Normalize), dist)
|
||||||
|
let toPointSet = (dist): t => FromDist(ToDist(ToPointSet), dist)
|
||||||
|
let toSampleSet = (dist, r): t => FromDist(ToDist(ToSampleSet(r)), dist)
|
||||||
|
let truncate = (dist, left, right): t => FromDist(ToDist(Truncate(left, right)), dist)
|
||||||
|
let inspect = (dist): t => FromDist(ToDist(Inspect), dist)
|
||||||
|
let toString = (dist): t => FromDist(ToString(ToString), dist)
|
||||||
|
let toSparkline = (dist, n): t => FromDist(ToString(ToSparkline(n)), dist)
|
||||||
|
let algebraicAdd = (dist1, dist2: genericDist): t => FromDist(
|
||||||
|
ToDistCombination(Algebraic, #Add, #Dist(dist2)),
|
||||||
|
dist1,
|
||||||
|
)
|
||||||
|
let algebraicMultiply = (dist1, dist2): t => FromDist(
|
||||||
|
ToDistCombination(Algebraic, #Multiply, #Dist(dist2)),
|
||||||
|
dist1,
|
||||||
|
)
|
||||||
|
let algebraicDivide = (dist1, dist2): t => FromDist(
|
||||||
|
ToDistCombination(Algebraic, #Divide, #Dist(dist2)),
|
||||||
|
dist1,
|
||||||
|
)
|
||||||
|
let algebraicSubtract = (dist1, dist2): t => FromDist(
|
||||||
|
ToDistCombination(Algebraic, #Subtract, #Dist(dist2)),
|
||||||
|
dist1,
|
||||||
|
)
|
||||||
|
let algebraicLogarithm = (dist1, dist2): t => FromDist(
|
||||||
|
ToDistCombination(Algebraic, #Logarithm, #Dist(dist2)),
|
||||||
|
dist1,
|
||||||
|
)
|
||||||
|
let algebraicPower = (dist1, dist2): t => FromDist(
|
||||||
|
ToDistCombination(Algebraic, #Power, #Dist(dist2)),
|
||||||
|
dist1,
|
||||||
|
)
|
||||||
|
let pointwiseAdd = (dist1, dist2): t => FromDist(
|
||||||
|
ToDistCombination(Pointwise, #Add, #Dist(dist2)),
|
||||||
|
dist1,
|
||||||
|
)
|
||||||
|
let pointwiseMultiply = (dist1, dist2): t => FromDist(
|
||||||
|
ToDistCombination(Pointwise, #Multiply, #Dist(dist2)),
|
||||||
|
dist1,
|
||||||
|
)
|
||||||
|
let pointwiseDivide = (dist1, dist2): t => FromDist(
|
||||||
|
ToDistCombination(Pointwise, #Divide, #Dist(dist2)),
|
||||||
|
dist1,
|
||||||
|
)
|
||||||
|
let pointwiseSubtract = (dist1, dist2): t => FromDist(
|
||||||
|
ToDistCombination(Pointwise, #Subtract, #Dist(dist2)),
|
||||||
|
dist1,
|
||||||
|
)
|
||||||
|
let pointwiseLogarithm = (dist1, dist2): t => FromDist(
|
||||||
|
ToDistCombination(Pointwise, #Logarithm, #Dist(dist2)),
|
||||||
|
dist1,
|
||||||
|
)
|
||||||
|
let pointwisePower = (dist1, dist2): t => FromDist(
|
||||||
|
ToDistCombination(Pointwise, #Power, #Dist(dist2)),
|
||||||
|
dist1,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -114,7 +114,7 @@ let combineShapesContinuousContinuous = (
|
||||||
| #Subtract => (m1, m2) => m1 -. m2
|
| #Subtract => (m1, m2) => m1 -. m2
|
||||||
| #Multiply => (m1, m2) => m1 *. m2
|
| #Multiply => (m1, m2) => m1 *. m2
|
||||||
| #Divide => (m1, mInv2) => m1 *. mInv2
|
| #Divide => (m1, mInv2) => m1 *. mInv2
|
||||||
| #Exponentiate => (m1, mInv2) => m1 ** mInv2
|
| #Power => (m1, mInv2) => m1 ** mInv2
|
||||||
| #Logarithm => (m1, m2) => log(m1) /. log(m2)
|
| #Logarithm => (m1, m2) => log(m1) /. log(m2)
|
||||||
} // note: here, mInv2 = mean(1 / t2) ~= 1 / mean(t2)
|
} // note: here, mInv2 = mean(1 / t2) ~= 1 / mean(t2)
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@ let combineShapesContinuousContinuous = (
|
||||||
| #Add => (v1, v2, _, _) => v1 +. v2
|
| #Add => (v1, v2, _, _) => v1 +. v2
|
||||||
| #Subtract => (v1, v2, _, _) => v1 +. v2
|
| #Subtract => (v1, v2, _, _) => v1 +. v2
|
||||||
| #Multiply => (v1, v2, m1, m2) => v1 *. v2 +. v1 *. m2 ** 2. +. v2 *. m1 ** 2.
|
| #Multiply => (v1, v2, m1, m2) => v1 *. v2 +. v1 *. m2 ** 2. +. v2 *. m1 ** 2.
|
||||||
| #Exponentiate => (v1, v2, m1, m2) => v1 *. v2 +. v1 *. m2 ** 2. +. v2 *. m1 ** 2.
|
| #Power => (v1, v2, m1, m2) => v1 *. v2 +. v1 *. m2 ** 2. +. v2 *. m1 ** 2.
|
||||||
| #Logarithm => (v1, v2, m1, m2) => v1 *. v2 +. v1 *. m2 ** 2. +. v2 *. m1 ** 2.
|
| #Logarithm => (v1, v2, m1, m2) => v1 *. v2 +. v1 *. m2 ** 2. +. v2 *. m1 ** 2.
|
||||||
| #Divide => (v1, vInv2, m1, mInv2) => v1 *. vInv2 +. v1 *. mInv2 ** 2. +. vInv2 *. m1 ** 2.
|
| #Divide => (v1, vInv2, m1, mInv2) => v1 *. vInv2 +. v1 *. mInv2 ** 2. +. vInv2 *. m1 ** 2.
|
||||||
}
|
}
|
||||||
|
@ -233,7 +233,7 @@ let combineShapesContinuousDiscrete = (
|
||||||
()
|
()
|
||||||
}
|
}
|
||||||
| #Multiply
|
| #Multiply
|
||||||
| #Exponentiate
|
| #Power
|
||||||
| #Logarithm
|
| #Logarithm
|
||||||
| #Divide =>
|
| #Divide =>
|
||||||
for j in 0 to t2n - 1 {
|
for j in 0 to t2n - 1 {
|
||||||
|
|
|
@ -249,6 +249,9 @@ module T = Dist({
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let downsampleEquallyOverX = (length, t): t =>
|
||||||
|
t |> shapeMap(XYShape.XsConversion.proportionEquallyOverX(length))
|
||||||
|
|
||||||
/* This simply creates multiple copies of the continuous distribution, scaled and shifted according to
|
/* This simply creates multiple copies of the continuous distribution, scaled and shifted according to
|
||||||
each discrete data point, and then adds them all together. */
|
each discrete data point, and then adds them all together. */
|
||||||
let combineAlgebraicallyWithDiscrete = (
|
let combineAlgebraicallyWithDiscrete = (
|
||||||
|
|
|
@ -191,7 +191,6 @@ let isFloat = (t: t) =>
|
||||||
let sampleNRendered = (n, dist) => {
|
let sampleNRendered = (n, dist) => {
|
||||||
let integralCache = T.Integral.get(dist)
|
let integralCache = T.Integral.get(dist)
|
||||||
let distWithUpdatedIntegralCache = T.updateIntegralCache(Some(integralCache), dist)
|
let distWithUpdatedIntegralCache = T.updateIntegralCache(Some(integralCache), dist)
|
||||||
|
|
||||||
doN(n, () => sample(distWithUpdatedIntegralCache))
|
doN(n, () => sample(distWithUpdatedIntegralCache))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,3 +202,9 @@ let operate = (distToFloatOp: Operation.distToFloatOperation, s): float =>
|
||||||
| #Sample => sample(s)
|
| #Sample => sample(s)
|
||||||
| #Mean => T.mean(s)
|
| #Mean => T.mean(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let toSparkline = (t: t, bucketCount) =>
|
||||||
|
T.toContinuous(t)
|
||||||
|
->E.O2.fmap(Continuous.downsampleEquallyOverX(bucketCount))
|
||||||
|
->E.O2.toResult("toContinous Error: Could not convert into continuous distribution")
|
||||||
|
->E.R2.fmap(r => Continuous.getShape(r).ys->Sparklines.create())
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
This is used as a smart constructor. The only way to create a SampleSetDist.t is to call
|
||||||
|
this constructor.
|
||||||
|
https://stackoverflow.com/questions/66909578/how-to-make-a-type-constructor-private-in-rescript-except-in-current-module
|
||||||
|
*/
|
||||||
|
module T: {
|
||||||
|
//This really should be hidden (remove the array<float>). The reason it isn't is to act as an escape hatch in JS__Test.ts.
|
||||||
|
//When we get a good functional library in TS, we could refactor that out.
|
||||||
|
@genType
|
||||||
|
type t = array<float>
|
||||||
|
let make: array<float> => result<t, string>
|
||||||
|
let get: t => array<float>
|
||||||
|
} = {
|
||||||
|
type t = array<float>
|
||||||
|
let make = (a: array<float>) =>
|
||||||
|
if E.A.length(a) > 5 {
|
||||||
|
Ok(a)
|
||||||
|
} else {
|
||||||
|
Error("too small")
|
||||||
|
}
|
||||||
|
let get = (a: t) => a
|
||||||
|
}
|
||||||
|
|
||||||
|
include T
|
||||||
|
|
||||||
|
let length = (t: t) => get(t)->E.A.length
|
||||||
|
|
||||||
|
/*
|
||||||
|
TODO: Refactor to get a more precise estimate. Also, this code is just fairly messy, could use
|
||||||
|
some refactoring.
|
||||||
|
*/
|
||||||
|
let toPointSetDist = (~samples: t, ~samplingInputs: SamplingInputs.samplingInputs): result<
|
||||||
|
PointSetTypes.pointSetDist,
|
||||||
|
string,
|
||||||
|
> =>
|
||||||
|
SampleSetDist_ToPointSet.toPointSetDist(
|
||||||
|
~samples=get(samples),
|
||||||
|
~samplingInputs,
|
||||||
|
(),
|
||||||
|
).pointSetDist->E.O2.toResult("Failed to convert to PointSetDist")
|
||||||
|
|
||||||
|
//Randomly get one sample from the distribution
|
||||||
|
let sample = (t: t): float => {
|
||||||
|
let i = E.Int.random(~min=0, ~max=E.A.length(get(t)) - 1)
|
||||||
|
E.A.unsafe_get(get(t), i)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
If asked for a length of samples shorter or equal the length of the distribution,
|
||||||
|
return this first n samples of this distribution.
|
||||||
|
Else, return n random samples of the distribution.
|
||||||
|
The former helps in cases where multiple distributions are correlated.
|
||||||
|
However, if n > length(t), then there's no clear right answer, so we just randomly
|
||||||
|
sample everything.
|
||||||
|
*/
|
||||||
|
let sampleN = (t: t, n) => {
|
||||||
|
if n <= E.A.length(get(t)) {
|
||||||
|
E.A.slice(get(t), ~offset=0, ~len=n)
|
||||||
|
} else {
|
||||||
|
Belt.Array.makeBy(n, _ => sample(t))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: Figure out what to do if distributions are different lengths. ``zip`` is kind of inelegant for this.
|
||||||
|
let map2 = (~fn: (float, float) => float, ~t1: t, ~t2: t) => {
|
||||||
|
let samples = Belt.Array.zip(get(t1), get(t2))->E.A2.fmap(((a, b)) => fn(a, b))
|
||||||
|
make(samples)
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
//The math here was taken from https://github.com/jasondavies/science.js/blob/master/src/stats/bandwidth.js
|
//The math here was taken from https://github.com/jasondavies/science.js/blob/master/src/stats/SampleSetDist_Bandwidth.js
|
||||||
|
|
||||||
let len = x => E.A.length(x) |> float_of_int
|
let len = x => E.A.length(x) |> float_of_int
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
// TODO: Refactor to raise correct error when not enough samples
|
|
||||||
|
|
||||||
module Internals = {
|
module Internals = {
|
||||||
module Types = {
|
module Types = {
|
||||||
type samplingStats = {
|
type samplingStats = {
|
||||||
|
@ -57,6 +55,7 @@ module Internals = {
|
||||||
: {
|
: {
|
||||||
let _ = Js.Array.push(element, continuous)
|
let _ = Js.Array.push(element, continuous)
|
||||||
}
|
}
|
||||||
|
|
||||||
()
|
()
|
||||||
})
|
})
|
||||||
(continuous, discrete)
|
(continuous, discrete)
|
||||||
|
@ -71,7 +70,7 @@ module Internals = {
|
||||||
let formatUnitWidth = w => Jstat.max([w, 1.0]) |> int_of_float
|
let formatUnitWidth = w => Jstat.max([w, 1.0]) |> int_of_float
|
||||||
|
|
||||||
let suggestedUnitWidth = (samples, outputXYPoints) => {
|
let suggestedUnitWidth = (samples, outputXYPoints) => {
|
||||||
let suggestedXWidth = Bandwidth.nrd0(samples)
|
let suggestedXWidth = SampleSetDist_Bandwidth.nrd0(samples)
|
||||||
xWidthToUnitWidth(samples, outputXYPoints, suggestedXWidth)
|
xWidthToUnitWidth(samples, outputXYPoints, suggestedXWidth)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +97,7 @@ let toPointSetDist = (
|
||||||
let pdf =
|
let pdf =
|
||||||
continuousPart |> E.A.length > 5
|
continuousPart |> E.A.length > 5
|
||||||
? {
|
? {
|
||||||
let _suggestedXWidth = Bandwidth.nrd0(continuousPart)
|
let _suggestedXWidth = SampleSetDist_Bandwidth.nrd0(continuousPart)
|
||||||
// todo: This does some recalculating from the last step.
|
// todo: This does some recalculating from the last step.
|
||||||
let _suggestedUnitWidth = Internals.T.suggestedUnitWidth(
|
let _suggestedUnitWidth = Internals.T.suggestedUnitWidth(
|
||||||
continuousPart,
|
continuousPart,
|
|
@ -55,7 +55,7 @@ module Exponential = {
|
||||||
rate: rate,
|
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 pdf = (x, t: t) => Jstat.Exponential.pdf(x, t.rate)
|
||||||
let cdf = (x, t: t) => Jstat.Exponential.cdf(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)
|
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 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 inv = (p, t: t) => Jstat.Cauchy.inv(p, t.local, t.scale)
|
||||||
let sample = (t: t) => Jstat.Cauchy.sample(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)`
|
let toString = ({local, scale}: t) => j`Cauchy($local, $scale)`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,8 +80,8 @@ module Triangular = {
|
||||||
let make = (low, medium, high): result<symbolicDist, string> =>
|
let make = (low, medium, high): result<symbolicDist, string> =>
|
||||||
low < medium && medium < high
|
low < medium && medium < high
|
||||||
? Ok(#Triangular({low: low, medium: medium, high: high}))
|
? Ok(#Triangular({low: low, medium: medium, high: high}))
|
||||||
: Error("Triangular values must be increasing order")
|
: Error("Triangular values must be increasing order.")
|
||||||
let pdf = (x, t: t) => Jstat.Triangular.pdf(x, t.low, t.high, t.medium)
|
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 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 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)
|
let sample = (t: t) => Jstat.Triangular.sample(t.low, t.high, t.medium)
|
||||||
|
@ -346,11 +346,11 @@ module T = {
|
||||||
| _ => #NoSolution
|
| _ => #NoSolution
|
||||||
}
|
}
|
||||||
|
|
||||||
let toPointSetDist = (sampleCount, d: symbolicDist): PointSetTypes.pointSetDist =>
|
let toPointSetDist = (~xSelection=#ByWeight, sampleCount, d: symbolicDist): PointSetTypes.pointSetDist =>
|
||||||
switch d {
|
switch d {
|
||||||
| #Float(v) => Discrete(Discrete.make(~integralSumCache=Some(1.0), {xs: [v], ys: [1.0]}))
|
| #Float(v) => Discrete(Discrete.make(~integralSumCache=Some(1.0), {xs: [v], ys: [1.0]}))
|
||||||
| _ =>
|
| _ =>
|
||||||
let xs = interpolateXs(~xSelection=#ByWeight, d, sampleCount)
|
let xs = interpolateXs(~xSelection, d, sampleCount)
|
||||||
let ys = xs |> E.A.fmap(x => pdf(x, d))
|
let ys = xs |> E.A.fmap(x => pdf(x, d))
|
||||||
Continuous(Continuous.make(~integralSumCache=Some(1.0), {xs: xs, ys: ys}))
|
Continuous(Continuous.make(~integralSumCache=Some(1.0), {xs: xs, ys: ys}))
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,7 +118,7 @@ module PointwiseCombination = {
|
||||||
switch pointwiseOp {
|
switch pointwiseOp {
|
||||||
| #Add => pointwiseAdd(evaluationParams, t1, t2)
|
| #Add => pointwiseAdd(evaluationParams, t1, t2)
|
||||||
| #Multiply => pointwiseCombine(\"*.", evaluationParams, t1, t2)
|
| #Multiply => pointwiseCombine(\"*.", evaluationParams, t1, t2)
|
||||||
| #Exponentiate => pointwiseCombine(\"**", evaluationParams, t1, t2)
|
| #Power => pointwiseCombine(\"**", evaluationParams, t1, t2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -218,15 +218,14 @@ module SamplingDistribution = {
|
||||||
algebraicOp,
|
algebraicOp,
|
||||||
a,
|
a,
|
||||||
b,
|
b,
|
||||||
)
|
) |> E.O.toResult("Could not get samples")
|
||||||
|
|
||||||
|
let sampleSetDist = samples -> E.R.bind(SampleSetDist.make)
|
||||||
|
|
||||||
let pointSetDist =
|
let pointSetDist =
|
||||||
samples
|
sampleSetDist
|
||||||
|> E.O.fmap(r =>
|
-> E.R.bind(r =>
|
||||||
SampleSet.toPointSetDist(~samplingInputs=evaluationParams.samplingInputs, ~samples=r, ())
|
SampleSetDist.toPointSetDist(~samplingInputs=evaluationParams.samplingInputs, ~samples=r));
|
||||||
)
|
|
||||||
|> E.O.bind(_, r => r.pointSetDist)
|
|
||||||
|> E.O.toResult("No response")
|
|
||||||
pointSetDist |> E.R.fmap(r => #Normalize(#RenderedDist(r)))
|
pointSetDist |> E.R.fmap(r => #Normalize(#RenderedDist(r)))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -227,7 +227,7 @@ let all = [
|
||||||
},
|
},
|
||||||
(),
|
(),
|
||||||
),
|
),
|
||||||
makeRenderedDistFloat("scaleExp", (dist, float) => verticalScaling(#Exponentiate, dist, float)),
|
makeRenderedDistFloat("scaleExp", (dist, float) => verticalScaling(#Power, dist, float)),
|
||||||
makeRenderedDistFloat("scaleMultiply", (dist, float) => verticalScaling(#Multiply, dist, float)),
|
makeRenderedDistFloat("scaleMultiply", (dist, float) => verticalScaling(#Multiply, dist, float)),
|
||||||
makeRenderedDistFloat("scaleLog", (dist, float) => verticalScaling(#Logarithm, dist, float)),
|
makeRenderedDistFloat("scaleLog", (dist, float) => verticalScaling(#Logarithm, dist, float)),
|
||||||
Multimodal._function,
|
Multimodal._function,
|
||||||
|
|
|
@ -144,11 +144,11 @@ module MathAdtToDistDst = {
|
||||||
| ("subtract", _) => Error("Subtraction needs two operands")
|
| ("subtract", _) => Error("Subtraction needs two operands")
|
||||||
| ("multiply", [l, r]) => toOkAlgebraic((#Multiply, l, r))
|
| ("multiply", [l, r]) => toOkAlgebraic((#Multiply, l, r))
|
||||||
| ("multiply", _) => Error("Multiplication needs two operands")
|
| ("multiply", _) => Error("Multiplication needs two operands")
|
||||||
| ("pow", [l, r]) => toOkAlgebraic((#Exponentiate, l, r))
|
| ("pow", [l, r]) => toOkAlgebraic((#Power, l, r))
|
||||||
| ("pow", _) => Error("Exponentiation needs two operands")
|
| ("pow", _) => Error("Exponentiation needs two operands")
|
||||||
| ("dotMultiply", [l, r]) => toOkPointwise((#Multiply, l, r))
|
| ("dotMultiply", [l, r]) => toOkPointwise((#Multiply, l, r))
|
||||||
| ("dotMultiply", _) => Error("Dotwise multiplication needs two operands")
|
| ("dotMultiply", _) => Error("Dotwise multiplication needs two operands")
|
||||||
| ("dotPow", [l, r]) => toOkPointwise((#Exponentiate, l, r))
|
| ("dotPow", [l, r]) => toOkPointwise((#Power, l, r))
|
||||||
| ("dotPow", _) => Error("Dotwise exponentiation needs two operands")
|
| ("dotPow", _) => Error("Dotwise exponentiation needs two operands")
|
||||||
| ("rightLogShift", [l, r]) => toOkPointwise((#Add, l, r))
|
| ("rightLogShift", [l, r]) => toOkPointwise((#Add, l, r))
|
||||||
| ("rightLogShift", _) => Error("Dotwise addition needs two operands")
|
| ("rightLogShift", _) => Error("Dotwise addition needs two operands")
|
||||||
|
|
|
@ -18,8 +18,8 @@ module Helpers = {
|
||||||
| "divide" => #Divide
|
| "divide" => #Divide
|
||||||
| "log" => #Logarithm
|
| "log" => #Logarithm
|
||||||
| "dotDivide" => #Divide
|
| "dotDivide" => #Divide
|
||||||
| "pow" => #Exponentiate
|
| "pow" => #Power
|
||||||
| "dotPow" => #Exponentiate
|
| "dotPow" => #Power
|
||||||
| "multiply" => #Multiply
|
| "multiply" => #Multiply
|
||||||
| "dotMultiply" => #Multiply
|
| "dotMultiply" => #Multiply
|
||||||
| "dotLog" => #Logarithm
|
| "dotLog" => #Logarithm
|
||||||
|
@ -45,6 +45,13 @@ module Helpers = {
|
||||||
FromDist(GenericDist_Types.Operation.ToFloat(fnCall), dist)->runGenericOperation->Some
|
FromDist(GenericDist_Types.Operation.ToFloat(fnCall), dist)->runGenericOperation->Some
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let toStringFn = (
|
||||||
|
fnCall: GenericDist_Types.Operation.toString,
|
||||||
|
dist: GenericDist_Types.genericDist,
|
||||||
|
) => {
|
||||||
|
FromDist(GenericDist_Types.Operation.ToString(fnCall), dist)->runGenericOperation->Some
|
||||||
|
}
|
||||||
|
|
||||||
let toDistFn = (fnCall: GenericDist_Types.Operation.toDist, dist) => {
|
let toDistFn = (fnCall: GenericDist_Types.Operation.toDist, dist) => {
|
||||||
FromDist(GenericDist_Types.Operation.ToDist(fnCall), dist)->runGenericOperation->Some
|
FromDist(GenericDist_Types.Operation.ToDist(fnCall), dist)->runGenericOperation->Some
|
||||||
}
|
}
|
||||||
|
@ -119,6 +126,9 @@ let dispatchToGenericOutput = (call: ExpressionValue.functionCall): option<
|
||||||
->SymbolicConstructors.symbolicResultToOutput
|
->SymbolicConstructors.symbolicResultToOutput
|
||||||
| ("sample", [EvDistribution(dist)]) => Helpers.toFloatFn(#Sample, dist)
|
| ("sample", [EvDistribution(dist)]) => Helpers.toFloatFn(#Sample, dist)
|
||||||
| ("mean", [EvDistribution(dist)]) => Helpers.toFloatFn(#Mean, dist)
|
| ("mean", [EvDistribution(dist)]) => Helpers.toFloatFn(#Mean, dist)
|
||||||
|
| ("toString", [EvDistribution(dist)]) => Helpers.toStringFn(ToString, dist)
|
||||||
|
| ("toSparkline", [EvDistribution(dist)]) => Helpers.toStringFn(ToSparkline(20), dist)
|
||||||
|
| ("toSparkline", [EvDistribution(dist), EvNumber(n)]) => Helpers.toStringFn(ToSparkline(Belt.Float.toInt(n)), dist)
|
||||||
| ("exp", [EvDistribution(a)]) =>
|
| ("exp", [EvDistribution(a)]) =>
|
||||||
// https://mathjs.org/docs/reference/functions/exp.html
|
// https://mathjs.org/docs/reference/functions/exp.html
|
||||||
Helpers.twoDiststoDistFn(Algebraic, "pow", GenericDist.fromFloat(Math.e), a)->Some
|
Helpers.twoDiststoDistFn(Algebraic, "pow", GenericDist.fromFloat(Math.e), a)->Some
|
||||||
|
|
27
packages/squiggle-lang/src/rescript/TypescriptInterface.res
Normal file
27
packages/squiggle-lang/src/rescript/TypescriptInterface.res
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
This is meant as a file to contain @genType declarations as needed for Typescript.
|
||||||
|
I would ultimately want to have all @genType declarations here, vs. other files, but
|
||||||
|
@genType doesn't play as nicely with renaming Modules and functions as
|
||||||
|
would be preferable.
|
||||||
|
|
||||||
|
The below few seem to work fine. In the future there's definitely more work to do here.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@genType
|
||||||
|
type env = DistributionOperation.env
|
||||||
|
|
||||||
|
@genType
|
||||||
|
type genericDist = GenericDist_Types.genericDist
|
||||||
|
|
||||||
|
@genType
|
||||||
|
type error = GenericDist_Types.error
|
||||||
|
|
||||||
|
@genType
|
||||||
|
type resultDist = result<genericDist, error>
|
||||||
|
@genType
|
||||||
|
type resultFloat = result<float, error>
|
||||||
|
@genType
|
||||||
|
type resultString = result<string, error>
|
||||||
|
|
||||||
|
@genType
|
||||||
|
let makeSampleSetDist = SampleSetDist.make
|
|
@ -24,6 +24,7 @@ module FloatFloatMap = {
|
||||||
|
|
||||||
module Int = {
|
module Int = {
|
||||||
let max = (i1: int, i2: int) => i1 > i2 ? i1 : i2
|
let max = (i1: int, i2: int) => i1 > i2 ? i1 : i2
|
||||||
|
let random = (~min, ~max) => Js.Math.random_int(min, max)
|
||||||
}
|
}
|
||||||
/* Utils */
|
/* Utils */
|
||||||
module U = {
|
module U = {
|
||||||
|
@ -101,6 +102,7 @@ module O2 = {
|
||||||
let default = (a, b) => O.default(b, a)
|
let default = (a, b) => O.default(b, a)
|
||||||
let toExn = (a, b) => O.toExn(b, a)
|
let toExn = (a, b) => O.toExn(b, a)
|
||||||
let fmap = (a, b) => O.fmap(b, a)
|
let fmap = (a, b) => O.fmap(b, a)
|
||||||
|
let toResult = (a, b) => O.toResult(b, a)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Functions */
|
/* Functions */
|
||||||
|
@ -178,6 +180,13 @@ module R = {
|
||||||
|
|
||||||
module R2 = {
|
module R2 = {
|
||||||
let fmap = (a,b) => R.fmap(b,a)
|
let fmap = (a,b) => R.fmap(b,a)
|
||||||
|
let bind = (a, b) => R.bind(b, a)
|
||||||
|
|
||||||
|
//Converts result type to change error type only
|
||||||
|
let errMap = (a, map) => switch(a){
|
||||||
|
| Ok(r) => Ok(r)
|
||||||
|
| Error(e) => map(e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let safe_fn_of_string = (fn, s: string): option<'a> =>
|
let safe_fn_of_string = (fn, s: string): option<'a> =>
|
||||||
|
@ -269,6 +278,7 @@ module A = {
|
||||||
let fold_right = Array.fold_right
|
let fold_right = Array.fold_right
|
||||||
let concatMany = Belt.Array.concatMany
|
let concatMany = Belt.Array.concatMany
|
||||||
let keepMap = Belt.Array.keepMap
|
let keepMap = Belt.Array.keepMap
|
||||||
|
let slice = Belt.Array.slice
|
||||||
let init = Array.init
|
let init = Array.init
|
||||||
let reduce = Belt.Array.reduce
|
let reduce = Belt.Array.reduce
|
||||||
let reducei = Belt.Array.reduceWithIndex
|
let reducei = Belt.Array.reduceWithIndex
|
||||||
|
@ -289,8 +299,7 @@ module A = {
|
||||||
))
|
))
|
||||||
|> Rationale.Result.return
|
|> Rationale.Result.return
|
||||||
}
|
}
|
||||||
let rangeFloat = (~step=1, start, stop) =>
|
|
||||||
Belt.Array.rangeBy(start, stop, ~step) |> fmap(Belt.Int.toFloat)
|
|
||||||
|
|
||||||
// This zips while taking the longest elements of each array.
|
// This zips while taking the longest elements of each array.
|
||||||
let zipMaxLength = (array1, array2) => {
|
let zipMaxLength = (array1, array2) => {
|
||||||
|
@ -442,6 +451,12 @@ module A = {
|
||||||
let mean = a => sum(a) /. (Array.length(a) |> float_of_int)
|
let mean = a => sum(a) /. (Array.length(a) |> float_of_int)
|
||||||
let random = Js.Math.random_int
|
let random = Js.Math.random_int
|
||||||
|
|
||||||
|
// Gives an array with all the differences between values
|
||||||
|
// diff([1,5,3,7]) = [4,-2,4]
|
||||||
|
let diff = (arr: array<float>): array<float> =>
|
||||||
|
Belt.Array.zipBy(arr, Belt.Array.sliceToEnd(arr, 1), (left, right) => right -. left)
|
||||||
|
|
||||||
|
|
||||||
exception RangeError(string)
|
exception RangeError(string)
|
||||||
let range = (min: float, max: float, n: int): array<float> =>
|
let range = (min: float, max: float, n: int): array<float> =>
|
||||||
switch n {
|
switch n {
|
||||||
|
|
|
@ -6,12 +6,12 @@ type algebraicOperation = [
|
||||||
| #Multiply
|
| #Multiply
|
||||||
| #Subtract
|
| #Subtract
|
||||||
| #Divide
|
| #Divide
|
||||||
| #Exponentiate
|
| #Power
|
||||||
| #Logarithm
|
| #Logarithm
|
||||||
]
|
]
|
||||||
@genType
|
@genType
|
||||||
type pointwiseOperation = [#Add | #Multiply | #Exponentiate]
|
type pointwiseOperation = [#Add | #Multiply | #Power]
|
||||||
type scaleOperation = [#Multiply | #Exponentiate | #Logarithm | #Divide]
|
type scaleOperation = [#Multiply | #Power | #Logarithm | #Divide]
|
||||||
type distToFloatOperation = [
|
type distToFloatOperation = [
|
||||||
| #Pdf(float)
|
| #Pdf(float)
|
||||||
| #Cdf(float)
|
| #Cdf(float)
|
||||||
|
@ -27,7 +27,7 @@ module Algebraic = {
|
||||||
| #Add => \"+."
|
| #Add => \"+."
|
||||||
| #Subtract => \"-."
|
| #Subtract => \"-."
|
||||||
| #Multiply => \"*."
|
| #Multiply => \"*."
|
||||||
| #Exponentiate => \"**"
|
| #Power => \"**"
|
||||||
| #Divide => \"/."
|
| #Divide => \"/."
|
||||||
| #Logarithm => (a, b) => log(a) /. log(b)
|
| #Logarithm => (a, b) => log(a) /. log(b)
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ module Algebraic = {
|
||||||
| #Add => "+"
|
| #Add => "+"
|
||||||
| #Subtract => "-"
|
| #Subtract => "-"
|
||||||
| #Multiply => "*"
|
| #Multiply => "*"
|
||||||
| #Exponentiate => "**"
|
| #Power => "**"
|
||||||
| #Divide => "/"
|
| #Divide => "/"
|
||||||
| #Logarithm => "log"
|
| #Logarithm => "log"
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ module Pointwise = {
|
||||||
let toString = x =>
|
let toString = x =>
|
||||||
switch x {
|
switch x {
|
||||||
| #Add => "+"
|
| #Add => "+"
|
||||||
| #Exponentiate => "^"
|
| #Power => "**"
|
||||||
| #Multiply => "*"
|
| #Multiply => "*"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ module Scale = {
|
||||||
switch x {
|
switch x {
|
||||||
| #Multiply => \"*."
|
| #Multiply => \"*."
|
||||||
| #Divide => \"/."
|
| #Divide => \"/."
|
||||||
| #Exponentiate => \"**"
|
| #Power => \"**"
|
||||||
| #Logarithm => (a, b) => log(a) /. log(b)
|
| #Logarithm => (a, b) => log(a) /. log(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ module Scale = {
|
||||||
switch operation {
|
switch operation {
|
||||||
| #Multiply => j`verticalMultiply($value, $scaleBy) `
|
| #Multiply => j`verticalMultiply($value, $scaleBy) `
|
||||||
| #Divide => j`verticalDivide($value, $scaleBy) `
|
| #Divide => j`verticalDivide($value, $scaleBy) `
|
||||||
| #Exponentiate => j`verticalExponentiate($value, $scaleBy) `
|
| #Power => j`verticalPower($value, $scaleBy) `
|
||||||
| #Logarithm => j`verticalLog($value, $scaleBy) `
|
| #Logarithm => j`verticalLog($value, $scaleBy) `
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ module Scale = {
|
||||||
switch x {
|
switch x {
|
||||||
| #Multiply => (a, b) => Some(a *. b)
|
| #Multiply => (a, b) => Some(a *. b)
|
||||||
| #Divide => (a, b) => Some(a /. b)
|
| #Divide => (a, b) => Some(a /. b)
|
||||||
| #Exponentiate => (_, _) => None
|
| #Power => (_, _) => None
|
||||||
| #Logarithm => (_, _) => None
|
| #Logarithm => (_, _) => None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ module Scale = {
|
||||||
switch x {
|
switch x {
|
||||||
| #Multiply => (_, _) => None // TODO: this could probably just be multiplied out (using Continuous.scaleBy)
|
| #Multiply => (_, _) => None // TODO: this could probably just be multiplied out (using Continuous.scaleBy)
|
||||||
| #Divide => (_, _) => None
|
| #Divide => (_, _) => None
|
||||||
| #Exponentiate => (_, _) => None
|
| #Power => (_, _) => None
|
||||||
| #Logarithm => (_, _) => None
|
| #Logarithm => (_, _) => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user