Merge pull request #345 from quantified-uncertainty/invalid-ops
Change NaN operations to results
This commit is contained in:
commit
dfd2f83c9d
|
@ -30,7 +30,7 @@ let toExt: option<'a> => 'a = E.O.toExt(
|
||||||
describe("sparkline", () => {
|
describe("sparkline", () => {
|
||||||
let runTest = (
|
let runTest = (
|
||||||
name: string,
|
name: string,
|
||||||
dist: GenericDist_Types.genericDist,
|
dist: DistributionTypes.genericDist,
|
||||||
expected: DistributionOperation.outputType,
|
expected: DistributionOperation.outputType,
|
||||||
) => {
|
) => {
|
||||||
test(name, () => {
|
test(name, () => {
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
let normalDist5: GenericDist_Types.genericDist = Symbolic(#Normal({mean: 5.0, stdev: 2.0}))
|
let normalDist5: DistributionTypes.genericDist = Symbolic(#Normal({mean: 5.0, stdev: 2.0}))
|
||||||
let normalDist10: GenericDist_Types.genericDist = Symbolic(#Normal({mean: 10.0, stdev: 2.0}))
|
let normalDist10: DistributionTypes.genericDist = Symbolic(#Normal({mean: 10.0, stdev: 2.0}))
|
||||||
let normalDist20: GenericDist_Types.genericDist = Symbolic(#Normal({mean: 20.0, stdev: 2.0}))
|
let normalDist20: DistributionTypes.genericDist = Symbolic(#Normal({mean: 20.0, stdev: 2.0}))
|
||||||
let normalDist: GenericDist_Types.genericDist = normalDist5
|
let normalDist: DistributionTypes.genericDist = normalDist5
|
||||||
|
|
||||||
let betaDist: GenericDist_Types.genericDist = Symbolic(#Beta({alpha: 2.0, beta: 5.0}))
|
let betaDist: DistributionTypes.genericDist = Symbolic(#Beta({alpha: 2.0, beta: 5.0}))
|
||||||
let lognormalDist: GenericDist_Types.genericDist = Symbolic(#Lognormal({mu: 0.0, sigma: 1.0}))
|
let lognormalDist: DistributionTypes.genericDist = Symbolic(#Lognormal({mu: 0.0, sigma: 1.0}))
|
||||||
let cauchyDist: GenericDist_Types.genericDist = Symbolic(#Cauchy({local: 1.0, scale: 1.0}))
|
let cauchyDist: DistributionTypes.genericDist = Symbolic(#Cauchy({local: 1.0, scale: 1.0}))
|
||||||
let triangularDist: GenericDist_Types.genericDist = Symbolic(
|
let triangularDist: DistributionTypes.genericDist = Symbolic(
|
||||||
#Triangular({low: 1.0, medium: 2.0, high: 3.0}),
|
#Triangular({low: 1.0, medium: 2.0, high: 3.0}),
|
||||||
)
|
)
|
||||||
let exponentialDist: GenericDist_Types.genericDist = Symbolic(#Exponential({rate: 2.0}))
|
let exponentialDist: DistributionTypes.genericDist = Symbolic(#Exponential({rate: 2.0}))
|
||||||
let uniformDist: GenericDist_Types.genericDist = Symbolic(#Uniform({low: 9.0, high: 10.0}))
|
let uniformDist: DistributionTypes.genericDist = Symbolic(#Uniform({low: 9.0, high: 10.0}))
|
||||||
let floatDist: GenericDist_Types.genericDist = Symbolic(#Float(1e1))
|
let floatDist: DistributionTypes.genericDist = Symbolic(#Float(1e1))
|
||||||
|
|
|
@ -43,10 +43,10 @@ describe("(Algebraic) addition of distributions", () => {
|
||||||
test("normal(mean=5) + normal(mean=20)", () => {
|
test("normal(mean=5) + normal(mean=20)", () => {
|
||||||
normalDist5
|
normalDist5
|
||||||
->algebraicAdd(normalDist20)
|
->algebraicAdd(normalDist20)
|
||||||
->E.R2.fmap(GenericDist_Types.Constructors.UsingDists.mean)
|
->E.R2.fmap(DistributionTypes.Constructors.UsingDists.mean)
|
||||||
->E.R2.fmap(run)
|
->E.R2.fmap(run)
|
||||||
->E.R2.fmap(toFloat)
|
->E.R2.fmap(toFloat)
|
||||||
->E.R.toExn
|
->E.R.toExn("Expected float", _)
|
||||||
->expect
|
->expect
|
||||||
->toBe(Some(2.5e1))
|
->toBe(Some(2.5e1))
|
||||||
})
|
})
|
||||||
|
@ -57,10 +57,10 @@ describe("(Algebraic) addition of distributions", () => {
|
||||||
let received =
|
let received =
|
||||||
uniformDist
|
uniformDist
|
||||||
->algebraicAdd(betaDist)
|
->algebraicAdd(betaDist)
|
||||||
->E.R2.fmap(GenericDist_Types.Constructors.UsingDists.mean)
|
->E.R2.fmap(DistributionTypes.Constructors.UsingDists.mean)
|
||||||
->E.R2.fmap(run)
|
->E.R2.fmap(run)
|
||||||
->E.R2.fmap(toFloat)
|
->E.R2.fmap(toFloat)
|
||||||
->E.R.toExn
|
->E.R.toExn("Expected float", _)
|
||||||
switch received {
|
switch received {
|
||||||
| None => "algebraicAdd has"->expect->toBe("failed")
|
| None => "algebraicAdd has"->expect->toBe("failed")
|
||||||
// This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad.
|
// This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad.
|
||||||
|
@ -74,10 +74,10 @@ describe("(Algebraic) addition of distributions", () => {
|
||||||
let received =
|
let received =
|
||||||
betaDist
|
betaDist
|
||||||
->algebraicAdd(uniformDist)
|
->algebraicAdd(uniformDist)
|
||||||
->E.R2.fmap(GenericDist_Types.Constructors.UsingDists.mean)
|
->E.R2.fmap(DistributionTypes.Constructors.UsingDists.mean)
|
||||||
->E.R2.fmap(run)
|
->E.R2.fmap(run)
|
||||||
->E.R2.fmap(toFloat)
|
->E.R2.fmap(toFloat)
|
||||||
->E.R.toExn
|
->E.R.toExn("Expected float", _)
|
||||||
switch received {
|
switch received {
|
||||||
| None => "algebraicAdd has"->expect->toBe("failed")
|
| None => "algebraicAdd has"->expect->toBe("failed")
|
||||||
// This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad.
|
// This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad.
|
||||||
|
@ -95,7 +95,7 @@ describe("(Algebraic) addition of distributions", () => {
|
||||||
let received =
|
let received =
|
||||||
normalDist10 // this should be normal(10, sqrt(8))
|
normalDist10 // this should be normal(10, sqrt(8))
|
||||||
->Ok
|
->Ok
|
||||||
->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.pdf(d, x))
|
->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.pdf(d, x))
|
||||||
->E.R2.fmap(run)
|
->E.R2.fmap(run)
|
||||||
->E.R2.fmap(toFloat)
|
->E.R2.fmap(toFloat)
|
||||||
->E.R.toOption
|
->E.R.toOption
|
||||||
|
@ -103,7 +103,7 @@ describe("(Algebraic) addition of distributions", () => {
|
||||||
let calculated =
|
let calculated =
|
||||||
normalDist5
|
normalDist5
|
||||||
->algebraicAdd(normalDist5)
|
->algebraicAdd(normalDist5)
|
||||||
->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.pdf(d, x))
|
->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.pdf(d, x))
|
||||||
->E.R2.fmap(run)
|
->E.R2.fmap(run)
|
||||||
->E.R2.fmap(toFloat)
|
->E.R2.fmap(toFloat)
|
||||||
->E.R.toOption
|
->E.R.toOption
|
||||||
|
@ -126,7 +126,7 @@ describe("(Algebraic) addition of distributions", () => {
|
||||||
let received =
|
let received =
|
||||||
normalDist20
|
normalDist20
|
||||||
->Ok
|
->Ok
|
||||||
->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.pdf(d, 1.9e1))
|
->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.pdf(d, 1.9e1))
|
||||||
->E.R2.fmap(run)
|
->E.R2.fmap(run)
|
||||||
->E.R2.fmap(toFloat)
|
->E.R2.fmap(toFloat)
|
||||||
->E.R.toOption
|
->E.R.toOption
|
||||||
|
@ -134,7 +134,7 @@ describe("(Algebraic) addition of distributions", () => {
|
||||||
let calculated =
|
let calculated =
|
||||||
normalDist10
|
normalDist10
|
||||||
->algebraicAdd(normalDist10)
|
->algebraicAdd(normalDist10)
|
||||||
->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.pdf(d, 1.9e1))
|
->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.pdf(d, 1.9e1))
|
||||||
->E.R2.fmap(run)
|
->E.R2.fmap(run)
|
||||||
->E.R2.fmap(toFloat)
|
->E.R2.fmap(toFloat)
|
||||||
->E.R.toOption
|
->E.R.toOption
|
||||||
|
@ -155,10 +155,10 @@ describe("(Algebraic) addition of distributions", () => {
|
||||||
let received =
|
let received =
|
||||||
uniformDist
|
uniformDist
|
||||||
->algebraicAdd(betaDist)
|
->algebraicAdd(betaDist)
|
||||||
->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.pdf(d, 1e1))
|
->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.pdf(d, 1e1))
|
||||||
->E.R2.fmap(run)
|
->E.R2.fmap(run)
|
||||||
->E.R2.fmap(toFloat)
|
->E.R2.fmap(toFloat)
|
||||||
->E.R.toExn
|
->E.R.toExn("Expected float", _)
|
||||||
switch received {
|
switch received {
|
||||||
| None => "algebraicAdd has"->expect->toBe("failed")
|
| None => "algebraicAdd has"->expect->toBe("failed")
|
||||||
// This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad.
|
// This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad.
|
||||||
|
@ -170,10 +170,10 @@ describe("(Algebraic) addition of distributions", () => {
|
||||||
let received =
|
let received =
|
||||||
betaDist
|
betaDist
|
||||||
->algebraicAdd(uniformDist)
|
->algebraicAdd(uniformDist)
|
||||||
->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.pdf(d, 1e1))
|
->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.pdf(d, 1e1))
|
||||||
->E.R2.fmap(run)
|
->E.R2.fmap(run)
|
||||||
->E.R2.fmap(toFloat)
|
->E.R2.fmap(toFloat)
|
||||||
->E.R.toExn
|
->E.R.toExn("Expected float", _)
|
||||||
switch received {
|
switch received {
|
||||||
| None => "algebraicAdd has"->expect->toBe("failed")
|
| None => "algebraicAdd has"->expect->toBe("failed")
|
||||||
// This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad.
|
// This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad.
|
||||||
|
@ -187,7 +187,7 @@ describe("(Algebraic) addition of distributions", () => {
|
||||||
let received =
|
let received =
|
||||||
normalDist10
|
normalDist10
|
||||||
->Ok
|
->Ok
|
||||||
->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.cdf(d, x))
|
->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.cdf(d, x))
|
||||||
->E.R2.fmap(run)
|
->E.R2.fmap(run)
|
||||||
->E.R2.fmap(toFloat)
|
->E.R2.fmap(toFloat)
|
||||||
->E.R.toOption
|
->E.R.toOption
|
||||||
|
@ -195,7 +195,7 @@ describe("(Algebraic) addition of distributions", () => {
|
||||||
let calculated =
|
let calculated =
|
||||||
normalDist5
|
normalDist5
|
||||||
->algebraicAdd(normalDist5)
|
->algebraicAdd(normalDist5)
|
||||||
->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.cdf(d, x))
|
->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.cdf(d, x))
|
||||||
->E.R2.fmap(run)
|
->E.R2.fmap(run)
|
||||||
->E.R2.fmap(toFloat)
|
->E.R2.fmap(toFloat)
|
||||||
->E.R.toOption
|
->E.R.toOption
|
||||||
|
@ -217,7 +217,7 @@ describe("(Algebraic) addition of distributions", () => {
|
||||||
let received =
|
let received =
|
||||||
normalDist20
|
normalDist20
|
||||||
->Ok
|
->Ok
|
||||||
->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.cdf(d, 1.25e1))
|
->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.cdf(d, 1.25e1))
|
||||||
->E.R2.fmap(run)
|
->E.R2.fmap(run)
|
||||||
->E.R2.fmap(toFloat)
|
->E.R2.fmap(toFloat)
|
||||||
->E.R.toOption
|
->E.R.toOption
|
||||||
|
@ -225,7 +225,7 @@ describe("(Algebraic) addition of distributions", () => {
|
||||||
let calculated =
|
let calculated =
|
||||||
normalDist10
|
normalDist10
|
||||||
->algebraicAdd(normalDist10)
|
->algebraicAdd(normalDist10)
|
||||||
->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.cdf(d, 1.25e1))
|
->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.cdf(d, 1.25e1))
|
||||||
->E.R2.fmap(run)
|
->E.R2.fmap(run)
|
||||||
->E.R2.fmap(toFloat)
|
->E.R2.fmap(toFloat)
|
||||||
->E.R.toOption
|
->E.R.toOption
|
||||||
|
@ -246,10 +246,10 @@ describe("(Algebraic) addition of distributions", () => {
|
||||||
let received =
|
let received =
|
||||||
uniformDist
|
uniformDist
|
||||||
->algebraicAdd(betaDist)
|
->algebraicAdd(betaDist)
|
||||||
->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.cdf(d, 1e1))
|
->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.cdf(d, 1e1))
|
||||||
->E.R2.fmap(run)
|
->E.R2.fmap(run)
|
||||||
->E.R2.fmap(toFloat)
|
->E.R2.fmap(toFloat)
|
||||||
->E.R.toExn
|
->E.R.toExn("Expected float", _)
|
||||||
switch received {
|
switch received {
|
||||||
| None => "algebraicAdd has"->expect->toBe("failed")
|
| None => "algebraicAdd has"->expect->toBe("failed")
|
||||||
// This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad.
|
// This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad.
|
||||||
|
@ -261,10 +261,10 @@ describe("(Algebraic) addition of distributions", () => {
|
||||||
let received =
|
let received =
|
||||||
betaDist
|
betaDist
|
||||||
->algebraicAdd(uniformDist)
|
->algebraicAdd(uniformDist)
|
||||||
->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.cdf(d, 1e1))
|
->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.cdf(d, 1e1))
|
||||||
->E.R2.fmap(run)
|
->E.R2.fmap(run)
|
||||||
->E.R2.fmap(toFloat)
|
->E.R2.fmap(toFloat)
|
||||||
->E.R.toExn
|
->E.R.toExn("Expected float", _)
|
||||||
switch received {
|
switch received {
|
||||||
| None => "algebraicAdd has"->expect->toBe("failed")
|
| None => "algebraicAdd has"->expect->toBe("failed")
|
||||||
// This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad.
|
// This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad.
|
||||||
|
@ -279,7 +279,7 @@ describe("(Algebraic) addition of distributions", () => {
|
||||||
let received =
|
let received =
|
||||||
normalDist10
|
normalDist10
|
||||||
->Ok
|
->Ok
|
||||||
->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.inv(d, x))
|
->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.inv(d, x))
|
||||||
->E.R2.fmap(run)
|
->E.R2.fmap(run)
|
||||||
->E.R2.fmap(toFloat)
|
->E.R2.fmap(toFloat)
|
||||||
->E.R.toOption
|
->E.R.toOption
|
||||||
|
@ -287,7 +287,7 @@ describe("(Algebraic) addition of distributions", () => {
|
||||||
let calculated =
|
let calculated =
|
||||||
normalDist5
|
normalDist5
|
||||||
->algebraicAdd(normalDist5)
|
->algebraicAdd(normalDist5)
|
||||||
->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.inv(d, x))
|
->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.inv(d, x))
|
||||||
->E.R2.fmap(run)
|
->E.R2.fmap(run)
|
||||||
->E.R2.fmap(toFloat)
|
->E.R2.fmap(toFloat)
|
||||||
->E.R.toOption
|
->E.R.toOption
|
||||||
|
@ -309,7 +309,7 @@ describe("(Algebraic) addition of distributions", () => {
|
||||||
let received =
|
let received =
|
||||||
normalDist20
|
normalDist20
|
||||||
->Ok
|
->Ok
|
||||||
->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.inv(d, 1e-1))
|
->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.inv(d, 1e-1))
|
||||||
->E.R2.fmap(run)
|
->E.R2.fmap(run)
|
||||||
->E.R2.fmap(toFloat)
|
->E.R2.fmap(toFloat)
|
||||||
->E.R.toOption
|
->E.R.toOption
|
||||||
|
@ -317,7 +317,7 @@ describe("(Algebraic) addition of distributions", () => {
|
||||||
let calculated =
|
let calculated =
|
||||||
normalDist10
|
normalDist10
|
||||||
->algebraicAdd(normalDist10)
|
->algebraicAdd(normalDist10)
|
||||||
->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.inv(d, 1e-1))
|
->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.inv(d, 1e-1))
|
||||||
->E.R2.fmap(run)
|
->E.R2.fmap(run)
|
||||||
->E.R2.fmap(toFloat)
|
->E.R2.fmap(toFloat)
|
||||||
->E.R.toOption
|
->E.R.toOption
|
||||||
|
@ -338,10 +338,10 @@ describe("(Algebraic) addition of distributions", () => {
|
||||||
let received =
|
let received =
|
||||||
uniformDist
|
uniformDist
|
||||||
->algebraicAdd(betaDist)
|
->algebraicAdd(betaDist)
|
||||||
->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.inv(d, 2e-2))
|
->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.inv(d, 2e-2))
|
||||||
->E.R2.fmap(run)
|
->E.R2.fmap(run)
|
||||||
->E.R2.fmap(toFloat)
|
->E.R2.fmap(toFloat)
|
||||||
->E.R.toExn
|
->E.R.toExn("Expected float", _)
|
||||||
switch received {
|
switch received {
|
||||||
| None => "algebraicAdd has"->expect->toBe("failed")
|
| None => "algebraicAdd has"->expect->toBe("failed")
|
||||||
// This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad.
|
// This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad.
|
||||||
|
@ -353,10 +353,10 @@ describe("(Algebraic) addition of distributions", () => {
|
||||||
let received =
|
let received =
|
||||||
betaDist
|
betaDist
|
||||||
->algebraicAdd(uniformDist)
|
->algebraicAdd(uniformDist)
|
||||||
->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.inv(d, 2e-2))
|
->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.inv(d, 2e-2))
|
||||||
->E.R2.fmap(run)
|
->E.R2.fmap(run)
|
||||||
->E.R2.fmap(toFloat)
|
->E.R2.fmap(toFloat)
|
||||||
->E.R.toExn
|
->E.R.toExn("Expected float", _)
|
||||||
switch received {
|
switch received {
|
||||||
| None => "algebraicAdd has"->expect->toBe("failed")
|
| None => "algebraicAdd has"->expect->toBe("failed")
|
||||||
// This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad.
|
// This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad.
|
||||||
|
|
|
@ -15,7 +15,7 @@ open TestHelpers
|
||||||
module Internals = {
|
module Internals = {
|
||||||
let epsilon = 5e1
|
let epsilon = 5e1
|
||||||
|
|
||||||
let mean = GenericDist_Types.Constructors.UsingDists.mean
|
let mean = DistributionTypes.Constructors.UsingDists.mean
|
||||||
|
|
||||||
let expectImpossiblePath: string => assertion = algebraicOp =>
|
let expectImpossiblePath: string => assertion = algebraicOp =>
|
||||||
`${algebraicOp} has`->expect->toEqual("failed")
|
`${algebraicOp} has`->expect->toEqual("failed")
|
||||||
|
@ -50,7 +50,11 @@ module Internals = {
|
||||||
let dist1 = dist1'->DistributionTypes.Symbolic
|
let dist1 = dist1'->DistributionTypes.Symbolic
|
||||||
let dist2 = dist2'->DistributionTypes.Symbolic
|
let dist2 = dist2'->DistributionTypes.Symbolic
|
||||||
let received =
|
let received =
|
||||||
distOp(dist1, dist2)->E.R2.fmap(mean)->E.R2.fmap(run)->E.R2.fmap(toFloat)->E.R.toExn
|
distOp(dist1, dist2)
|
||||||
|
->E.R2.fmap(mean)
|
||||||
|
->E.R2.fmap(run)
|
||||||
|
->E.R2.fmap(toFloat)
|
||||||
|
->E.R.toExn("Expected float", _)
|
||||||
let expected = floatOp(runMean(dist1), runMean(dist2))
|
let expected = floatOp(runMean(dist1), runMean(dist2))
|
||||||
switch received {
|
switch received {
|
||||||
| None => expectImpossiblePath(description)
|
| None => expectImpossiblePath(description)
|
||||||
|
@ -80,14 +84,16 @@ let {testOperationMean, distributions, pairsOfDifferentDistributions, epsilon} =
|
||||||
describe("Means are invariant", () => {
|
describe("Means are invariant", () => {
|
||||||
describe("for addition", () => {
|
describe("for addition", () => {
|
||||||
let testAdditionMean = testOperationMean(algebraicAdd, "algebraicAdd", \"+.", ~epsilon)
|
let testAdditionMean = testOperationMean(algebraicAdd, "algebraicAdd", \"+.", ~epsilon)
|
||||||
|
let testAddInvariant = (t1, t2) =>
|
||||||
|
E.R.liftM2(testAdditionMean, t1, t2)->E.R.toExn("Means were not invariant", _)
|
||||||
|
|
||||||
testAll("with two of the same distribution", distributions, dist => {
|
testAll("with two of the same distribution", distributions, dist => {
|
||||||
E.R.liftM2(testAdditionMean, dist, dist)->E.R.toExn
|
testAddInvariant(dist, dist)
|
||||||
})
|
})
|
||||||
|
|
||||||
testAll("with two different distributions", pairsOfDifferentDistributions, dists => {
|
testAll("with two different distributions", pairsOfDifferentDistributions, dists => {
|
||||||
let (dist1, dist2) = dists
|
let (dist1, dist2) = dists
|
||||||
E.R.liftM2(testAdditionMean, dist1, dist2)->E.R.toExn
|
testAddInvariant(dist1, dist2)
|
||||||
})
|
})
|
||||||
|
|
||||||
testAll(
|
testAll(
|
||||||
|
@ -95,7 +101,7 @@ describe("Means are invariant", () => {
|
||||||
pairsOfDifferentDistributions,
|
pairsOfDifferentDistributions,
|
||||||
dists => {
|
dists => {
|
||||||
let (dist1, dist2) = dists
|
let (dist1, dist2) = dists
|
||||||
E.R.liftM2(testAdditionMean, dist2, dist1)->E.R.toExn
|
testAddInvariant(dist1, dist2)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -107,14 +113,16 @@ describe("Means are invariant", () => {
|
||||||
\"-.",
|
\"-.",
|
||||||
~epsilon,
|
~epsilon,
|
||||||
)
|
)
|
||||||
|
let testSubtractInvariant = (t1, t2) =>
|
||||||
|
E.R.liftM2(testSubtractionMean, t1, t2)->E.R.toExn("Means were not invariant", _)
|
||||||
|
|
||||||
testAll("with two of the same distribution", distributions, dist => {
|
testAll("with two of the same distribution", distributions, dist => {
|
||||||
E.R.liftM2(testSubtractionMean, dist, dist)->E.R.toExn
|
testSubtractInvariant(dist, dist)
|
||||||
})
|
})
|
||||||
|
|
||||||
testAll("with two different distributions", pairsOfDifferentDistributions, dists => {
|
testAll("with two different distributions", pairsOfDifferentDistributions, dists => {
|
||||||
let (dist1, dist2) = dists
|
let (dist1, dist2) = dists
|
||||||
E.R.liftM2(testSubtractionMean, dist1, dist2)->E.R.toExn
|
testSubtractInvariant(dist1, dist2)
|
||||||
})
|
})
|
||||||
|
|
||||||
testAll(
|
testAll(
|
||||||
|
@ -122,7 +130,7 @@ describe("Means are invariant", () => {
|
||||||
pairsOfDifferentDistributions,
|
pairsOfDifferentDistributions,
|
||||||
dists => {
|
dists => {
|
||||||
let (dist1, dist2) = dists
|
let (dist1, dist2) = dists
|
||||||
E.R.liftM2(testSubtractionMean, dist2, dist1)->E.R.toExn
|
testSubtractInvariant(dist1, dist2)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -134,14 +142,16 @@ describe("Means are invariant", () => {
|
||||||
\"*.",
|
\"*.",
|
||||||
~epsilon,
|
~epsilon,
|
||||||
)
|
)
|
||||||
|
let testMultiplicationInvariant = (t1, t2) =>
|
||||||
|
E.R.liftM2(testMultiplicationMean, t1, t2)->E.R.toExn("Means were not invariant", _)
|
||||||
|
|
||||||
testAll("with two of the same distribution", distributions, dist => {
|
testAll("with two of the same distribution", distributions, dist => {
|
||||||
E.R.liftM2(testMultiplicationMean, dist, dist)->E.R.toExn
|
testMultiplicationInvariant(dist, dist)
|
||||||
})
|
})
|
||||||
|
|
||||||
testAll("with two different distributions", pairsOfDifferentDistributions, dists => {
|
testAll("with two different distributions", pairsOfDifferentDistributions, dists => {
|
||||||
let (dist1, dist2) = dists
|
let (dist1, dist2) = dists
|
||||||
E.R.liftM2(testMultiplicationMean, dist1, dist2)->E.R.toExn
|
testMultiplicationInvariant(dist1, dist2)
|
||||||
})
|
})
|
||||||
|
|
||||||
testAll(
|
testAll(
|
||||||
|
@ -149,7 +159,7 @@ describe("Means are invariant", () => {
|
||||||
pairsOfDifferentDistributions,
|
pairsOfDifferentDistributions,
|
||||||
dists => {
|
dists => {
|
||||||
let (dist1, dist2) = dists
|
let (dist1, dist2) = dists
|
||||||
E.R.liftM2(testMultiplicationMean, dist2, dist1)->E.R.toExn
|
testMultiplicationInvariant(dist1, dist2)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
@ -22,7 +22,7 @@ describe("eval on distribution functions", () => {
|
||||||
testEval("mean(-normal(5,2))", "Ok(-5)")
|
testEval("mean(-normal(5,2))", "Ok(-5)")
|
||||||
})
|
})
|
||||||
describe("to", () => {
|
describe("to", () => {
|
||||||
testEval("5 to 2", "Error(TODO: Low value must be less than high value.)")
|
testEval("5 to 2", "Error(Math Error: Low value must be less than high value.)")
|
||||||
testEval("to(2,5)", "Ok(Lognormal(1.1512925464970227,0.27853260523016377))")
|
testEval("to(2,5)", "Ok(Lognormal(1.1512925464970227,0.27853260523016377))")
|
||||||
testEval("to(-2,2)", "Ok(Normal(0,1.2159136638235384))")
|
testEval("to(-2,2)", "Ok(Normal(0,1.2159136638235384))")
|
||||||
})
|
})
|
||||||
|
@ -88,18 +88,15 @@ describe("eval on distribution functions", () => {
|
||||||
|
|
||||||
describe("log", () => {
|
describe("log", () => {
|
||||||
testEval("log(2, uniform(5,8))", "Ok(Sample Set Distribution)")
|
testEval("log(2, uniform(5,8))", "Ok(Sample Set Distribution)")
|
||||||
testEval("log(normal(5,2), 3)", "Ok(Sample Set Distribution)")
|
testEval("log(normal(5,2), 3)", "Error(Math Error: Operation returned complex result)")
|
||||||
testEval("log(normal(5,2), normal(10,1))", "Ok(Sample Set Distribution)")
|
testEval(
|
||||||
|
"log(normal(5,2), normal(10,1))",
|
||||||
|
"Error(Math Error: Operation returned complex result)",
|
||||||
|
)
|
||||||
testEval("log(uniform(5,8))", "Ok(Sample Set Distribution)")
|
testEval("log(uniform(5,8))", "Ok(Sample Set Distribution)")
|
||||||
testEval("log10(uniform(5,8))", "Ok(Sample Set Distribution)")
|
testEval("log10(uniform(5,8))", "Ok(Sample Set Distribution)")
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("dotLog", () => {
|
|
||||||
testEval("dotLog(normal(5,2), 3)", "Ok(Point Set Distribution)")
|
|
||||||
testEval("dotLog(normal(5,2), 3)", "Ok(Point Set Distribution)")
|
|
||||||
testEval("dotLog(normal(5,2), normal(10,1))", "Ok(Point Set Distribution)")
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("dotAdd", () => {
|
describe("dotAdd", () => {
|
||||||
testEval("dotAdd(normal(5,2), lognormal(10,2))", "Ok(Point Set Distribution)")
|
testEval("dotAdd(normal(5,2), lognormal(10,2))", "Ok(Point Set Distribution)")
|
||||||
testEval("dotAdd(normal(5,2), 3)", "Ok(Point Set Distribution)")
|
testEval("dotAdd(normal(5,2), 3)", "Ok(Point Set Distribution)")
|
||||||
|
|
|
@ -12,7 +12,7 @@ describe("Symbolic mean", () => {
|
||||||
expect(squiggleResult.value).toBeCloseTo((x + y + z) / 3);
|
expect(squiggleResult.value).toBeCloseTo((x + y + z) / 3);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect((err as Error).message).toEqual(
|
expect((err as Error).message).toEqual(
|
||||||
"Expected squiggle expression to evaluate but got error: TODO: Triangular values must be increasing order."
|
"Expected squiggle expression to evaluate but got error: Math Error: Triangular values must be increasing order."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
preset: "ts-jest",
|
preset: "ts-jest",
|
||||||
testEnvironment: "node",
|
testEnvironment: "node",
|
||||||
|
bail: true,
|
||||||
setupFilesAfterEnv: [
|
setupFilesAfterEnv: [
|
||||||
"<rootdir>/../../node_modules/bisect_ppx/src/runtime/js/jest.bs.js",
|
"<rootdir>/../../node_modules/bisect_ppx/src/runtime/js/jest.bs.js",
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
type functionCallInfo = GenericDist_Types.Operation.genericFunctionCallInfo
|
type functionCallInfo = DistributionTypes.DistributionOperation.genericFunctionCallInfo
|
||||||
type genericDist = DistributionTypes.genericDist
|
type genericDist = DistributionTypes.genericDist
|
||||||
type error = DistributionTypes.error
|
type error = DistributionTypes.error
|
||||||
|
|
||||||
|
@ -120,7 +120,10 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => {
|
||||||
(),
|
(),
|
||||||
)->OutputLocal.toDistR
|
)->OutputLocal.toDistR
|
||||||
|
|
||||||
let fromDistFn = (subFnName: GenericDist_Types.Operation.fromDist, dist: genericDist) => {
|
let fromDistFn = (
|
||||||
|
subFnName: DistributionTypes.DistributionOperation.fromDist,
|
||||||
|
dist: genericDist,
|
||||||
|
) => {
|
||||||
let response = switch subFnName {
|
let response = switch subFnName {
|
||||||
| ToFloat(distToFloatOperation) =>
|
| ToFloat(distToFloatOperation) =>
|
||||||
GenericDist.toFloatOperation(dist, ~toPointSetFn, ~distToFloatOperation)
|
GenericDist.toFloatOperation(dist, ~toPointSetFn, ~distToFloatOperation)
|
||||||
|
@ -157,14 +160,14 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => {
|
||||||
->GenericDist.algebraicCombination(~toPointSetFn, ~toSampleSetFn, ~arithmeticOperation, ~t2)
|
->GenericDist.algebraicCombination(~toPointSetFn, ~toSampleSetFn, ~arithmeticOperation, ~t2)
|
||||||
->E.R2.fmap(r => Dist(r))
|
->E.R2.fmap(r => Dist(r))
|
||||||
->OutputLocal.fromResult
|
->OutputLocal.fromResult
|
||||||
| ToDistCombination(Pointwise, arithmeticOperation, #Dist(t2)) =>
|
| ToDistCombination(Pointwise, algebraicCombination, #Dist(t2)) =>
|
||||||
dist
|
dist
|
||||||
->GenericDist.pointwiseCombination(~toPointSetFn, ~arithmeticOperation, ~t2)
|
->GenericDist.pointwiseCombination(~toPointSetFn, ~algebraicCombination, ~t2)
|
||||||
->E.R2.fmap(r => Dist(r))
|
->E.R2.fmap(r => Dist(r))
|
||||||
->OutputLocal.fromResult
|
->OutputLocal.fromResult
|
||||||
| ToDistCombination(Pointwise, arithmeticOperation, #Float(float)) =>
|
| ToDistCombination(Pointwise, algebraicCombination, #Float(f)) =>
|
||||||
dist
|
dist
|
||||||
->GenericDist.pointwiseCombinationFloat(~toPointSetFn, ~arithmeticOperation, ~float)
|
->GenericDist.pointwiseCombinationFloat(~toPointSetFn, ~algebraicCombination, ~f)
|
||||||
->E.R2.fmap(r => Dist(r))
|
->E.R2.fmap(r => Dist(r))
|
||||||
->OutputLocal.fromResult
|
->OutputLocal.fromResult
|
||||||
}
|
}
|
||||||
|
@ -192,24 +195,24 @@ module Output = {
|
||||||
let fmap = (
|
let fmap = (
|
||||||
~env,
|
~env,
|
||||||
input: outputType,
|
input: outputType,
|
||||||
functionCallInfo: GenericDist_Types.Operation.singleParamaterFunction,
|
functionCallInfo: DistributionTypes.DistributionOperation.singleParamaterFunction,
|
||||||
): outputType => {
|
): outputType => {
|
||||||
let newFnCall: result<functionCallInfo, error> = switch (functionCallInfo, input) {
|
let newFnCall: result<functionCallInfo, error> = switch (functionCallInfo, input) {
|
||||||
| (FromDist(fromDist), Dist(o)) => Ok(FromDist(fromDist, o))
|
| (FromDist(fromDist), Dist(o)) => Ok(FromDist(fromDist, o))
|
||||||
| (FromFloat(fromDist), Float(o)) => Ok(FromFloat(fromDist, o))
|
| (FromFloat(fromDist), Float(o)) => Ok(FromFloat(fromDist, o))
|
||||||
| (_, GenDistError(r)) => Error(r)
|
| (_, GenDistError(r)) => Error(r)
|
||||||
| (FromDist(_), _) => Error(Other("Expected dist, got something else"))
|
| (FromDist(_), _) => Error(OtherError("Expected dist, got something else"))
|
||||||
| (FromFloat(_), _) => Error(Other("Expected float, got something else"))
|
| (FromFloat(_), _) => Error(OtherError("Expected float, got something else"))
|
||||||
}
|
}
|
||||||
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.
|
// See comment above DistributionTypes.Constructors to explain the purpose of this module.
|
||||||
// I tried having another internal module called UsingDists, similar to how its done in
|
// 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.
|
// DistributionTypes.Constructors. However, this broke GenType for me, so beware.
|
||||||
module Constructors = {
|
module Constructors = {
|
||||||
module C = GenericDist_Types.Constructors.UsingDists
|
module C = DistributionTypes.Constructors.UsingDists
|
||||||
open OutputLocal
|
open OutputLocal
|
||||||
let mean = (~env, dist) => C.mean(dist)->run(~env)->toFloatR
|
let mean = (~env, dist) => C.mean(dist)->run(~env)->toFloatR
|
||||||
let sample = (~env, dist) => C.sample(dist)->run(~env)->toFloatR
|
let sample = (~env, dist) => C.sample(dist)->run(~env)->toFloatR
|
||||||
|
|
|
@ -4,7 +4,7 @@ type env = {
|
||||||
xyPointLength: int,
|
xyPointLength: int,
|
||||||
}
|
}
|
||||||
|
|
||||||
open GenericDist_Types
|
open DistributionTypes
|
||||||
|
|
||||||
@genType
|
@genType
|
||||||
type outputType =
|
type outputType =
|
||||||
|
@ -15,15 +15,15 @@ type outputType =
|
||||||
| GenDistError(error)
|
| GenDistError(error)
|
||||||
|
|
||||||
@genType
|
@genType
|
||||||
let run: (~env: env, GenericDist_Types.Operation.genericFunctionCallInfo) => outputType
|
let run: (~env: env, DistributionTypes.DistributionOperation.genericFunctionCallInfo) => outputType
|
||||||
let runFromDist: (
|
let runFromDist: (
|
||||||
~env: env,
|
~env: env,
|
||||||
~functionCallInfo: GenericDist_Types.Operation.fromDist,
|
~functionCallInfo: DistributionTypes.DistributionOperation.fromDist,
|
||||||
genericDist,
|
genericDist,
|
||||||
) => outputType
|
) => outputType
|
||||||
let runFromFloat: (
|
let runFromFloat: (
|
||||||
~env: env,
|
~env: env,
|
||||||
~functionCallInfo: GenericDist_Types.Operation.fromDist,
|
~functionCallInfo: DistributionTypes.DistributionOperation.fromDist,
|
||||||
float,
|
float,
|
||||||
) => outputType
|
) => outputType
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ module Output: {
|
||||||
let toBool: t => option<bool>
|
let toBool: t => option<bool>
|
||||||
let toBoolR: t => result<bool, error>
|
let toBoolR: t => result<bool, error>
|
||||||
let toError: t => option<error>
|
let toError: t => option<error>
|
||||||
let fmap: (~env: env, t, GenericDist_Types.Operation.singleParamaterFunction) => t
|
let fmap: (~env: env, t, DistributionTypes.DistributionOperation.singleParamaterFunction) => t
|
||||||
}
|
}
|
||||||
|
|
||||||
module Constructors: {
|
module Constructors: {
|
||||||
|
|
|
@ -9,33 +9,51 @@ type error =
|
||||||
| NotYetImplemented
|
| NotYetImplemented
|
||||||
| Unreachable
|
| Unreachable
|
||||||
| DistributionVerticalShiftIsInvalid
|
| DistributionVerticalShiftIsInvalid
|
||||||
|
| TooFewSamples
|
||||||
| ArgumentError(string)
|
| ArgumentError(string)
|
||||||
| Other(string)
|
| OperationError(Operation.Error.t)
|
||||||
|
| PointSetConversionError(SampleSetDist.pointsetConversionError)
|
||||||
|
| SparklineError(PointSetTypes.sparklineError) // This type of error is for when we find a sparkline of a discrete distribution. This should probably at some point be actually implemented
|
||||||
|
| OtherError(string)
|
||||||
|
|
||||||
|
@genType
|
||||||
|
module Error = {
|
||||||
|
type t = error
|
||||||
|
|
||||||
|
let fromString = (s: string): t => OtherError(s)
|
||||||
|
|
||||||
|
@genType
|
||||||
|
let toString = (err: error): string =>
|
||||||
|
switch err {
|
||||||
|
| NotYetImplemented => "Function Not Yet Implemented"
|
||||||
|
| Unreachable => "Unreachable"
|
||||||
|
| DistributionVerticalShiftIsInvalid => "Distribution Vertical Shift is Invalid"
|
||||||
|
| ArgumentError(s) => `Argument Error ${s}`
|
||||||
|
| TooFewSamples => "Too Few Samples"
|
||||||
|
| OperationError(err) => Operation.Error.toString(err)
|
||||||
|
| PointSetConversionError(err) => SampleSetDist.pointsetConversionErrorToString(err)
|
||||||
|
| SparklineError(err) => PointSetTypes.sparklineErrorToString(err)
|
||||||
|
| OtherError(s) => s
|
||||||
|
}
|
||||||
|
|
||||||
|
let resultStringToResultError: result<'a, string> => result<'a, error> = n =>
|
||||||
|
n->E.R2.errMap(r => r->fromString)
|
||||||
|
|
||||||
|
let sampleErrorToDistErr = (err: SampleSetDist.sampleSetError): error =>
|
||||||
|
switch err {
|
||||||
|
| TooFewSamples => TooFewSamples
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@genType
|
||||||
|
module DistributionOperation = {
|
||||||
|
@genType
|
||||||
|
type pointsetXSelection = [#Linear | #ByWeight]
|
||||||
|
|
||||||
module Operation = {
|
|
||||||
type direction =
|
type direction =
|
||||||
| Algebraic
|
| Algebraic
|
||||||
| Pointwise
|
| Pointwise
|
||||||
|
|
||||||
type arithmeticOperation = [
|
|
||||||
| #Add
|
|
||||||
| #Multiply
|
|
||||||
| #Subtract
|
|
||||||
| #Divide
|
|
||||||
| #Power
|
|
||||||
| #Logarithm
|
|
||||||
]
|
|
||||||
|
|
||||||
let arithmeticToFn = (arithmetic: arithmeticOperation) =>
|
|
||||||
switch arithmetic {
|
|
||||||
| #Add => \"+."
|
|
||||||
| #Multiply => \"*."
|
|
||||||
| #Subtract => \"-."
|
|
||||||
| #Power => \"**"
|
|
||||||
| #Divide => \"/."
|
|
||||||
| #Logarithm => (a, b) => log(a) /. log(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
type toFloat = [
|
type toFloat = [
|
||||||
| #Cdf(float)
|
| #Cdf(float)
|
||||||
| #Inv(float)
|
| #Inv(float)
|
||||||
|
@ -43,9 +61,7 @@ module Operation = {
|
||||||
| #Mean
|
| #Mean
|
||||||
| #Sample
|
| #Sample
|
||||||
]
|
]
|
||||||
}
|
|
||||||
|
|
||||||
module DistributionOperation = {
|
|
||||||
type toDist =
|
type toDist =
|
||||||
| Normalize
|
| Normalize
|
||||||
| ToPointSet
|
| ToPointSet
|
||||||
|
@ -55,15 +71,18 @@ module DistributionOperation = {
|
||||||
|
|
||||||
type toFloatArray = Sample(int)
|
type toFloatArray = Sample(int)
|
||||||
|
|
||||||
type fromDist =
|
type toBool = IsNormalized
|
||||||
| ToFloat(Operation.toFloat)
|
|
||||||
| ToDist(toDist)
|
type toString =
|
||||||
| ToDistCombination(
|
|
||||||
Operation.direction,
|
|
||||||
Operation.arithmeticOperation,
|
|
||||||
[#Dist(genericDist) | #Float(float)],
|
|
||||||
)
|
|
||||||
| ToString
|
| ToString
|
||||||
|
| ToSparkline(int)
|
||||||
|
|
||||||
|
type fromDist =
|
||||||
|
| ToFloat(toFloat)
|
||||||
|
| ToDist(toDist)
|
||||||
|
| ToDistCombination(direction, Operation.Algebraic.t, [#Dist(genericDist) | #Float(float)])
|
||||||
|
| ToString(toString)
|
||||||
|
| ToBool(toBool)
|
||||||
|
|
||||||
type singleParamaterFunction =
|
type singleParamaterFunction =
|
||||||
| FromDist(fromDist)
|
| FromDist(fromDist)
|
||||||
|
@ -86,7 +105,9 @@ module DistributionOperation = {
|
||||||
| 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)})`
|
||||||
|
| ToBool(IsNormalized) => `isNormalized`
|
||||||
| ToDistCombination(Algebraic, _, _) => `algebraic`
|
| ToDistCombination(Algebraic, _, _) => `algebraic`
|
||||||
| ToDistCombination(Pointwise, _, _) => `pointwise`
|
| ToDistCombination(Pointwise, _, _) => `pointwise`
|
||||||
}
|
}
|
||||||
|
@ -97,3 +118,71 @@ module DistributionOperation = {
|
||||||
| Mixture(_) => `mixture`
|
| Mixture(_) => `mixture`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
module Constructors = {
|
||||||
|
type t = DistributionOperation.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 isNormalized = (dist): t => FromDist(ToBool(IsNormalized), 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,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ let sampleN = (t: t, n) =>
|
||||||
}
|
}
|
||||||
|
|
||||||
let toSampleSetDist = (t: t, n) =>
|
let toSampleSetDist = (t: t, n) =>
|
||||||
SampleSetDist.make(sampleN(t, n))->GenericDist_Types.Error.resultStringToResultError
|
SampleSetDist.make(sampleN(t, n))->E.R2.errMap(DistributionTypes.Error.sampleErrorToDistErr)
|
||||||
|
|
||||||
let fromFloat = (f: float): t => Symbolic(SymbolicDist.Float.make(f))
|
let fromFloat = (f: float): t => Symbolic(SymbolicDist.Float.make(f))
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ let toPointSet = (
|
||||||
t,
|
t,
|
||||||
~xyPointLength,
|
~xyPointLength,
|
||||||
~sampleCount,
|
~sampleCount,
|
||||||
~xSelection: GenericDist_Types.Operation.pointsetXSelection=#ByWeight,
|
~xSelection: DistributionTypes.DistributionOperation.pointsetXSelection=#ByWeight,
|
||||||
(),
|
(),
|
||||||
): result<PointSetTypes.pointSetDist, error> => {
|
): result<PointSetTypes.pointSetDist, error> => {
|
||||||
switch (t: t) {
|
switch (t: t) {
|
||||||
|
@ -83,7 +83,7 @@ let toPointSet = (
|
||||||
pointSetDistLength: xyPointLength,
|
pointSetDistLength: xyPointLength,
|
||||||
kernelWidth: None,
|
kernelWidth: None,
|
||||||
},
|
},
|
||||||
)->GenericDist_Types.Error.resultStringToResultError
|
)->E.R2.errMap(x => DistributionTypes.PointSetConversionError(x))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,7 +97,7 @@ let toSparkline = (t: t, ~sampleCount: int, ~bucketCount: int=20, ()): result<st
|
||||||
t
|
t
|
||||||
->toPointSet(~xSelection=#Linear, ~xyPointLength=bucketCount * 3, ~sampleCount, ())
|
->toPointSet(~xSelection=#Linear, ~xyPointLength=bucketCount * 3, ~sampleCount, ())
|
||||||
->E.R.bind(r =>
|
->E.R.bind(r =>
|
||||||
r->PointSetDist.toSparkline(bucketCount)->GenericDist_Types.Error.resultStringToResultError
|
r->PointSetDist.toSparkline(bucketCount)->E.R2.errMap(x => DistributionTypes.SparklineError(x))
|
||||||
)
|
)
|
||||||
|
|
||||||
module Truncate = {
|
module Truncate = {
|
||||||
|
@ -148,10 +148,10 @@ let truncate = Truncate.run
|
||||||
*/
|
*/
|
||||||
module AlgebraicCombination = {
|
module AlgebraicCombination = {
|
||||||
let tryAnalyticalSimplification = (
|
let tryAnalyticalSimplification = (
|
||||||
arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation,
|
arithmeticOperation: Operation.algebraicOperation,
|
||||||
t1: t,
|
t1: t,
|
||||||
t2: t,
|
t2: t,
|
||||||
): option<result<SymbolicDistTypes.symbolicDist, string>> =>
|
): option<result<SymbolicDistTypes.symbolicDist, Operation.Error.t>> =>
|
||||||
switch (arithmeticOperation, t1, t2) {
|
switch (arithmeticOperation, t1, t2) {
|
||||||
| (arithmeticOperation, Symbolic(d1), Symbolic(d2)) =>
|
| (arithmeticOperation, Symbolic(d1), Symbolic(d2)) =>
|
||||||
switch SymbolicDist.T.tryAnalyticalSimplification(d1, d2, arithmeticOperation) {
|
switch SymbolicDist.T.tryAnalyticalSimplification(d1, d2, arithmeticOperation) {
|
||||||
|
@ -174,14 +174,14 @@ module AlgebraicCombination = {
|
||||||
|
|
||||||
let runMonteCarlo = (
|
let runMonteCarlo = (
|
||||||
toSampleSet: toSampleSetFn,
|
toSampleSet: toSampleSetFn,
|
||||||
arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation,
|
arithmeticOperation: Operation.algebraicOperation,
|
||||||
t1: t,
|
t1: t,
|
||||||
t2: t,
|
t2: t,
|
||||||
) => {
|
): result<t, error> => {
|
||||||
let fn = Operation.Algebraic.toFn(arithmeticOperation)
|
let fn = Operation.Algebraic.toFn(arithmeticOperation)
|
||||||
E.R.merge(toSampleSet(t1), toSampleSet(t2))
|
E.R.merge(toSampleSet(t1), toSampleSet(t2))
|
||||||
->E.R.bind(((t1, t2)) => {
|
->E.R.bind(((t1, t2)) => {
|
||||||
SampleSetDist.map2(~fn, ~t1, ~t2)->GenericDist_Types.Error.resultStringToResultError
|
SampleSetDist.map2(~fn, ~t1, ~t2)->E.R2.errMap(x => DistributionTypes.OperationError(x))
|
||||||
})
|
})
|
||||||
->E.R2.fmap(r => DistributionTypes.SampleSet(r))
|
->E.R2.fmap(r => DistributionTypes.SampleSet(r))
|
||||||
}
|
}
|
||||||
|
@ -224,7 +224,7 @@ module AlgebraicCombination = {
|
||||||
): result<t, error> => {
|
): result<t, error> => {
|
||||||
switch tryAnalyticalSimplification(arithmeticOperation, t1, t2) {
|
switch tryAnalyticalSimplification(arithmeticOperation, t1, t2) {
|
||||||
| Some(Ok(symbolicDist)) => Ok(Symbolic(symbolicDist))
|
| Some(Ok(symbolicDist)) => Ok(Symbolic(symbolicDist))
|
||||||
| Some(Error(e)) => Error(Other(e))
|
| Some(Error(e)) => Error(OperationError(e))
|
||||||
| None =>
|
| None =>
|
||||||
switch chooseConvolutionOrMonteCarlo(arithmeticOperation, t1, t2) {
|
switch chooseConvolutionOrMonteCarlo(arithmeticOperation, t1, t2) {
|
||||||
| MonteCarlo => runMonteCarlo(toSampleSetFn, arithmeticOperation, t1, t2)
|
| MonteCarlo => runMonteCarlo(toSampleSetFn, arithmeticOperation, t1, t2)
|
||||||
|
@ -241,40 +241,36 @@ let algebraicCombination = AlgebraicCombination.run
|
||||||
let pointwiseCombination = (
|
let pointwiseCombination = (
|
||||||
t1: t,
|
t1: t,
|
||||||
~toPointSetFn: toPointSetFn,
|
~toPointSetFn: toPointSetFn,
|
||||||
~arithmeticOperation,
|
~algebraicCombination: Operation.algebraicOperation,
|
||||||
~t2: t,
|
~t2: t,
|
||||||
): result<t, error> => {
|
): result<t, error> => {
|
||||||
E.R.merge(toPointSetFn(t1), toPointSetFn(t2))
|
E.R.merge(toPointSetFn(t1), toPointSetFn(t2))->E.R.bind(((t1, t2)) =>
|
||||||
->E.R2.fmap(((t1, t2)) =>
|
PointSetDist.combinePointwise(Operation.Algebraic.toFn(algebraicCombination), t1, t2)
|
||||||
PointSetDist.combinePointwise(
|
|
||||||
GenericDist_Types.Operation.arithmeticToFn(arithmeticOperation),
|
|
||||||
t1,
|
|
||||||
t2,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
->E.R2.fmap(r => DistributionTypes.PointSet(r))
|
->E.R2.fmap(r => DistributionTypes.PointSet(r))
|
||||||
|
->E.R2.errMap(err => DistributionTypes.OperationError(err))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
let pointwiseCombinationFloat = (
|
let pointwiseCombinationFloat = (
|
||||||
t: t,
|
t: t,
|
||||||
~toPointSetFn: toPointSetFn,
|
~toPointSetFn: toPointSetFn,
|
||||||
~arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation,
|
~algebraicCombination: Operation.algebraicOperation,
|
||||||
~float: float,
|
~f: float,
|
||||||
): result<t, error> => {
|
): result<t, error> => {
|
||||||
let m = switch arithmeticOperation {
|
let m = switch algebraicCombination {
|
||||||
| #Add | #Subtract => Error(DistributionTypes.DistributionVerticalShiftIsInvalid)
|
| #Add | #Subtract => Error(DistributionTypes.DistributionVerticalShiftIsInvalid)
|
||||||
| (#Multiply | #Divide | #Power | #Logarithm) as arithmeticOperation =>
|
| (#Multiply | #Divide | #Power | #Logarithm) as arithmeticOperation =>
|
||||||
toPointSetFn(t)->E.R2.fmap(t => {
|
toPointSetFn(t)->E.R.bind(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)
|
||||||
let integralSumCacheFn = Operation.Scale.toIntegralSumCacheFn(arithmeticOperation)
|
let integralSumCacheFn = Operation.Scale.toIntegralSumCacheFn(arithmeticOperation)
|
||||||
let integralCacheFn = Operation.Scale.toIntegralCacheFn(arithmeticOperation)
|
let integralCacheFn = Operation.Scale.toIntegralCacheFn(arithmeticOperation)
|
||||||
PointSetDist.T.mapY(
|
PointSetDist.T.mapYResult(
|
||||||
~integralSumCacheFn=integralSumCacheFn(float),
|
~integralSumCacheFn=integralSumCacheFn(f),
|
||||||
~integralCacheFn=integralCacheFn(float),
|
~integralCacheFn=integralCacheFn(f),
|
||||||
~fn=fn(float),
|
~fn=fn(f),
|
||||||
t,
|
t,
|
||||||
)
|
)->E.R2.errMap(x => DistributionTypes.OperationError(x))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
m->E.R2.fmap(r => DistributionTypes.PointSet(r))
|
m->E.R2.fmap(r => DistributionTypes.PointSet(r))
|
||||||
|
@ -288,7 +284,7 @@ let mixture = (
|
||||||
~pointwiseAddFn: pointwiseAddFn,
|
~pointwiseAddFn: pointwiseAddFn,
|
||||||
) => {
|
) => {
|
||||||
if E.A.length(values) == 0 {
|
if E.A.length(values) == 0 {
|
||||||
Error(DistributionTypes.Other("Mixture error: mixture must have at least 1 element"))
|
Error(DistributionTypes.OtherError("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,5 +1,5 @@
|
||||||
type t = GenericDist_Types.genericDist
|
type t = DistributionTypes.genericDist
|
||||||
type error = GenericDist_Types.error
|
type error = DistributionTypes.error
|
||||||
type toPointSetFn = t => result<PointSetTypes.pointSetDist, error>
|
type toPointSetFn = t => result<PointSetTypes.pointSetDist, error>
|
||||||
type toSampleSetFn = t => result<SampleSetDist.t, error>
|
type toSampleSetFn = t => result<SampleSetDist.t, error>
|
||||||
type scaleMultiplyFn = (t, float) => result<t, error>
|
type scaleMultiplyFn = (t, float) => result<t, error>
|
||||||
|
@ -28,7 +28,7 @@ let toPointSet: (
|
||||||
t,
|
t,
|
||||||
~xyPointLength: int,
|
~xyPointLength: int,
|
||||||
~sampleCount: int,
|
~sampleCount: int,
|
||||||
~xSelection: GenericDist_Types.Operation.pointsetXSelection=?,
|
~xSelection: DistributionTypes.DistributionOperation.pointsetXSelection=?,
|
||||||
unit,
|
unit,
|
||||||
) => result<PointSetTypes.pointSetDist, error>
|
) => result<PointSetTypes.pointSetDist, error>
|
||||||
let toSparkline: (t, ~sampleCount: int, ~bucketCount: int=?, unit) => result<string, error>
|
let toSparkline: (t, ~sampleCount: int, ~bucketCount: int=?, unit) => result<string, error>
|
||||||
|
@ -45,22 +45,22 @@ let algebraicCombination: (
|
||||||
t,
|
t,
|
||||||
~toPointSetFn: toPointSetFn,
|
~toPointSetFn: toPointSetFn,
|
||||||
~toSampleSetFn: toSampleSetFn,
|
~toSampleSetFn: toSampleSetFn,
|
||||||
~arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation,
|
~arithmeticOperation: Operation.algebraicOperation,
|
||||||
~t2: t,
|
~t2: t,
|
||||||
) => result<t, error>
|
) => result<t, error>
|
||||||
|
|
||||||
let pointwiseCombination: (
|
let pointwiseCombination: (
|
||||||
t,
|
t,
|
||||||
~toPointSetFn: toPointSetFn,
|
~toPointSetFn: toPointSetFn,
|
||||||
~arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation,
|
~algebraicCombination: Operation.algebraicOperation,
|
||||||
~t2: t,
|
~t2: t,
|
||||||
) => result<t, error>
|
) => result<t, error>
|
||||||
|
|
||||||
let pointwiseCombinationFloat: (
|
let pointwiseCombinationFloat: (
|
||||||
t,
|
t,
|
||||||
~toPointSetFn: toPointSetFn,
|
~toPointSetFn: toPointSetFn,
|
||||||
~arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation,
|
~algebraicCombination: Operation.algebraicOperation,
|
||||||
~float: float,
|
~f: float,
|
||||||
) => result<t, error>
|
) => result<t, error>
|
||||||
|
|
||||||
let mixture: (
|
let mixture: (
|
||||||
|
|
|
@ -1,194 +0,0 @@
|
||||||
type genericDist = DistributionTypes.genericDist
|
|
||||||
@genType
|
|
||||||
type error = DistributionTypes.error
|
|
||||||
|
|
||||||
@genType
|
|
||||||
module Error = {
|
|
||||||
type t = error
|
|
||||||
|
|
||||||
let fromString = (s: string): t => Other(s)
|
|
||||||
|
|
||||||
@genType
|
|
||||||
let toString = (x: t) => {
|
|
||||||
switch x {
|
|
||||||
| NotYetImplemented => "Not Yet Implemented"
|
|
||||||
| Unreachable => "Unreachable"
|
|
||||||
| DistributionVerticalShiftIsInvalid => "Distribution Vertical Shift Is Invalid"
|
|
||||||
| ArgumentError(x) => `Argument Error: ${x}`
|
|
||||||
| Other(s) => s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let resultStringToResultError: result<'a, string> => result<'a, error> = n =>
|
|
||||||
n->E.R2.errMap(r => r->fromString->Error)
|
|
||||||
}
|
|
||||||
|
|
||||||
module Operation = {
|
|
||||||
type direction =
|
|
||||||
| Algebraic
|
|
||||||
| Pointwise
|
|
||||||
|
|
||||||
type arithmeticOperation = [
|
|
||||||
| #Add
|
|
||||||
| #Multiply
|
|
||||||
| #Subtract
|
|
||||||
| #Divide
|
|
||||||
| #Power
|
|
||||||
| #Logarithm
|
|
||||||
]
|
|
||||||
|
|
||||||
let arithmeticToFn = (arithmetic: arithmeticOperation) =>
|
|
||||||
switch arithmetic {
|
|
||||||
| #Add => \"+."
|
|
||||||
| #Multiply => \"*."
|
|
||||||
| #Subtract => \"-."
|
|
||||||
| #Power => \"**"
|
|
||||||
| #Divide => \"/."
|
|
||||||
| #Logarithm => (a, b) => log(a) /. log(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
type toFloat = [
|
|
||||||
| #Cdf(float)
|
|
||||||
| #Inv(float)
|
|
||||||
| #Mean
|
|
||||||
| #Pdf(float)
|
|
||||||
| #Sample
|
|
||||||
]
|
|
||||||
|
|
||||||
@genType
|
|
||||||
type pointsetXSelection = [#Linear | #ByWeight]
|
|
||||||
|
|
||||||
type toDist =
|
|
||||||
| Normalize
|
|
||||||
| ToPointSet
|
|
||||||
| ToSampleSet(int)
|
|
||||||
| Truncate(option<float>, option<float>)
|
|
||||||
| Inspect
|
|
||||||
|
|
||||||
type toFloatArray = Sample(int)
|
|
||||||
|
|
||||||
type toString =
|
|
||||||
| ToString
|
|
||||||
| ToSparkline(int)
|
|
||||||
|
|
||||||
type toBool = IsNormalized
|
|
||||||
|
|
||||||
type fromDist =
|
|
||||||
| ToFloat(toFloat)
|
|
||||||
| ToDist(toDist)
|
|
||||||
| ToDistCombination(direction, arithmeticOperation, [#Dist(genericDist) | #Float(float)])
|
|
||||||
| ToString(toString)
|
|
||||||
| ToBool(toBool)
|
|
||||||
|
|
||||||
type singleParamaterFunction =
|
|
||||||
| FromDist(fromDist)
|
|
||||||
| FromFloat(fromDist)
|
|
||||||
|
|
||||||
@genType
|
|
||||||
type genericFunctionCallInfo =
|
|
||||||
| FromDist(fromDist, genericDist)
|
|
||||||
| FromFloat(fromDist, float)
|
|
||||||
| Mixture(array<(genericDist, float)>)
|
|
||||||
|
|
||||||
let distCallToString = (distFunction: fromDist): string =>
|
|
||||||
switch distFunction {
|
|
||||||
| ToFloat(#Cdf(r)) => `cdf(${E.Float.toFixed(r)})`
|
|
||||||
| ToFloat(#Inv(r)) => `inv(${E.Float.toFixed(r)})`
|
|
||||||
| ToFloat(#Mean) => `mean`
|
|
||||||
| ToFloat(#Pdf(r)) => `pdf(${E.Float.toFixed(r)})`
|
|
||||||
| ToFloat(#Sample) => `sample`
|
|
||||||
| ToDist(Normalize) => `normalize`
|
|
||||||
| ToDist(ToPointSet) => `toPointSet`
|
|
||||||
| ToDist(ToSampleSet(r)) => `toSampleSet(${E.I.toString(r)})`
|
|
||||||
| ToDist(Truncate(_, _)) => `truncate`
|
|
||||||
| ToDist(Inspect) => `inspect`
|
|
||||||
| ToString(ToString) => `toString`
|
|
||||||
| ToString(ToSparkline(n)) => `toSparkline(${E.I.toString(n)})`
|
|
||||||
| ToBool(IsNormalized) => `isNormalized`
|
|
||||||
| ToDistCombination(Algebraic, _, _) => `algebraic`
|
|
||||||
| ToDistCombination(Pointwise, _, _) => `pointwise`
|
|
||||||
}
|
|
||||||
|
|
||||||
let toString = (d: genericFunctionCallInfo): string =>
|
|
||||||
switch d {
|
|
||||||
| FromDist(f, _) | FromFloat(f, _) => distCallToString(f)
|
|
||||||
| 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 isNormalized = (dist): t => FromDist(ToBool(IsNormalized), 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,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -243,10 +243,13 @@ let combineShapesContinuousDiscrete = (
|
||||||
outXYShapes
|
outXYShapes
|
||||||
|> E.A.fmap(XYShape.T.fromZippedArray)
|
|> E.A.fmap(XYShape.T.fromZippedArray)
|
||||||
|> E.A.fold_left(
|
|> E.A.fold_left(
|
||||||
|
(acc, x) =>
|
||||||
XYShape.PointwiseCombination.combine(
|
XYShape.PointwiseCombination.combine(
|
||||||
\"+.",
|
(a, b) => Ok(a +. b),
|
||||||
XYShape.XtoY.continuousInterpolator(#Linear, #UseZero),
|
XYShape.XtoY.continuousInterpolator(#Linear, #UseZero),
|
||||||
),
|
acc,
|
||||||
|
x,
|
||||||
|
)->E.R.toExn("Error, unexpected failure", _),
|
||||||
XYShape.T.empty,
|
XYShape.T.empty,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,10 +88,10 @@ let stepwiseToLinear = (t: t): t =>
|
||||||
let combinePointwise = (
|
let combinePointwise = (
|
||||||
~integralSumCachesFn=(_, _) => None,
|
~integralSumCachesFn=(_, _) => None,
|
||||||
~distributionType: PointSetTypes.distributionType=#PDF,
|
~distributionType: PointSetTypes.distributionType=#PDF,
|
||||||
fn: (float, float) => float,
|
fn: (float, float) => result<float, Operation.Error.t>,
|
||||||
t1: PointSetTypes.continuousShape,
|
t1: PointSetTypes.continuousShape,
|
||||||
t2: PointSetTypes.continuousShape,
|
t2: PointSetTypes.continuousShape,
|
||||||
): PointSetTypes.continuousShape => {
|
): result<PointSetTypes.continuousShape, 'e> => {
|
||||||
// If we're adding the distributions, and we know the total of each, then we
|
// If we're adding the distributions, and we know the total of each, then we
|
||||||
// can just sum them up. Otherwise, all bets are off.
|
// can just sum them up. Otherwise, all bets are off.
|
||||||
let combinedIntegralSum = Common.combineIntegralSums(
|
let combinedIntegralSum = Common.combineIntegralSums(
|
||||||
|
@ -119,9 +119,8 @@ let combinePointwise = (
|
||||||
|
|
||||||
let interpolator = XYShape.XtoY.continuousInterpolator(t1.interpolation, extrapolation)
|
let interpolator = XYShape.XtoY.continuousInterpolator(t1.interpolation, extrapolation)
|
||||||
|
|
||||||
make(
|
XYShape.PointwiseCombination.combine(fn, interpolator, t1.xyShape, t2.xyShape)->E.R2.fmap(x =>
|
||||||
~integralSumCache=combinedIntegralSum,
|
make(~integralSumCache=combinedIntegralSum, x)
|
||||||
XYShape.PointwiseCombination.combine(fn, interpolator, t1.xyShape, t2.xyShape),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,13 +139,47 @@ let updateIntegralSumCache = (integralSumCache, t: t): t => {
|
||||||
|
|
||||||
let updateIntegralCache = (integralCache, t: t): t => {...t, integralCache: integralCache}
|
let updateIntegralCache = (integralCache, t: t): t => {...t, integralCache: integralCache}
|
||||||
|
|
||||||
|
let sum = (
|
||||||
|
~integralSumCachesFn: (float, float) => option<float>=(_, _) => None,
|
||||||
|
continuousShapes,
|
||||||
|
): t =>
|
||||||
|
continuousShapes |> E.A.fold_left(
|
||||||
|
(x, y) =>
|
||||||
|
combinePointwise(~integralSumCachesFn, (a, b) => Ok(a +. b), x, y)->E.R.toExn(
|
||||||
|
"Addition should never fail",
|
||||||
|
_,
|
||||||
|
),
|
||||||
|
empty,
|
||||||
|
)
|
||||||
|
|
||||||
let reduce = (
|
let reduce = (
|
||||||
~integralSumCachesFn: (float, float) => option<float>=(_, _) => None,
|
~integralSumCachesFn: (float, float) => option<float>=(_, _) => None,
|
||||||
fn,
|
fn: (float, float) => result<float, 'e>,
|
||||||
continuousShapes,
|
continuousShapes,
|
||||||
) => continuousShapes |> E.A.fold_left(combinePointwise(~integralSumCachesFn, fn), empty)
|
): result<t, 'e> =>
|
||||||
|
continuousShapes |> E.A.R.foldM(combinePointwise(~integralSumCachesFn, fn), empty)
|
||||||
|
|
||||||
let mapY = (~integralSumCacheFn=_ => None, ~integralCacheFn=_ => None, ~fn, t: t) =>
|
let mapYResult = (
|
||||||
|
~integralSumCacheFn=_ => None,
|
||||||
|
~integralCacheFn=_ => None,
|
||||||
|
~fn: float => result<float, 'e>,
|
||||||
|
t: t,
|
||||||
|
): result<t, 'e> =>
|
||||||
|
XYShape.T.mapYResult(fn, getShape(t))->E.R2.fmap(x =>
|
||||||
|
make(
|
||||||
|
~interpolation=t.interpolation,
|
||||||
|
~integralSumCache=t.integralSumCache |> E.O.bind(_, integralSumCacheFn),
|
||||||
|
~integralCache=t.integralCache |> E.O.bind(_, integralCacheFn),
|
||||||
|
x,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
let mapY = (
|
||||||
|
~integralSumCacheFn=_ => None,
|
||||||
|
~integralCacheFn=_ => None,
|
||||||
|
~fn: float => float,
|
||||||
|
t: t,
|
||||||
|
): t =>
|
||||||
make(
|
make(
|
||||||
~interpolation=t.interpolation,
|
~interpolation=t.interpolation,
|
||||||
~integralSumCache=t.integralSumCache |> E.O.bind(_, integralSumCacheFn),
|
~integralSumCache=t.integralSumCache |> E.O.bind(_, integralSumCacheFn),
|
||||||
|
@ -170,6 +203,7 @@ module T = Dist({
|
||||||
let minX = shapeFn(XYShape.T.minX)
|
let minX = shapeFn(XYShape.T.minX)
|
||||||
let maxX = shapeFn(XYShape.T.maxX)
|
let maxX = shapeFn(XYShape.T.maxX)
|
||||||
let mapY = mapY
|
let mapY = mapY
|
||||||
|
let mapYResult = mapYResult
|
||||||
let updateIntegralCache = updateIntegralCache
|
let updateIntegralCache = updateIntegralCache
|
||||||
let toDiscreteProbabilityMassFraction = _ => 0.0
|
let toDiscreteProbabilityMassFraction = _ => 0.0
|
||||||
let toPointSetDist = (t: t): PointSetTypes.pointSetDist => Continuous(t)
|
let toPointSetDist = (t: t): PointSetTypes.pointSetDist => Continuous(t)
|
||||||
|
|
|
@ -49,11 +49,11 @@ let combinePointwise = (
|
||||||
make(
|
make(
|
||||||
~integralSumCache=combinedIntegralSum,
|
~integralSumCache=combinedIntegralSum,
|
||||||
XYShape.PointwiseCombination.combine(
|
XYShape.PointwiseCombination.combine(
|
||||||
\"+.",
|
(a, b) => Ok(a +. b),
|
||||||
XYShape.XtoY.discreteInterpolator,
|
XYShape.XtoY.discreteInterpolator,
|
||||||
t1.xyShape,
|
t1.xyShape,
|
||||||
t2.xyShape,
|
t2.xyShape,
|
||||||
),
|
)->E.R.toExn("Addition operation should never fail", _),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,7 +103,26 @@ let combineAlgebraically = (op: Operation.convolutionOperation, t1: t, t2: t): t
|
||||||
make(~integralSumCache=combinedIntegralSum, combinedShape)
|
make(~integralSumCache=combinedIntegralSum, combinedShape)
|
||||||
}
|
}
|
||||||
|
|
||||||
let mapY = (~integralSumCacheFn=_ => None, ~integralCacheFn=_ => None, ~fn, t: t) =>
|
let mapYResult = (
|
||||||
|
~integralSumCacheFn=_ => None,
|
||||||
|
~integralCacheFn=_ => None,
|
||||||
|
~fn: float => result<float, 'e>,
|
||||||
|
t: t,
|
||||||
|
): result<t, 'e> =>
|
||||||
|
XYShape.T.mapYResult(fn, getShape(t))->E.R2.fmap(x =>
|
||||||
|
make(
|
||||||
|
~integralSumCache=t.integralSumCache |> E.O.bind(_, integralSumCacheFn),
|
||||||
|
~integralCache=t.integralCache |> E.O.bind(_, integralCacheFn),
|
||||||
|
x,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
let mapY = (
|
||||||
|
~integralSumCacheFn=_ => None,
|
||||||
|
~integralCacheFn=_ => None,
|
||||||
|
~fn: float => float,
|
||||||
|
t: t,
|
||||||
|
): t =>
|
||||||
make(
|
make(
|
||||||
~integralSumCache=t.integralSumCache |> E.O.bind(_, integralSumCacheFn),
|
~integralSumCache=t.integralSumCache |> E.O.bind(_, integralSumCacheFn),
|
||||||
~integralCache=t.integralCache |> E.O.bind(_, integralCacheFn),
|
~integralCache=t.integralCache |> E.O.bind(_, integralCacheFn),
|
||||||
|
@ -143,6 +162,7 @@ module T = Dist({
|
||||||
let maxX = shapeFn(XYShape.T.maxX)
|
let maxX = shapeFn(XYShape.T.maxX)
|
||||||
let toDiscreteProbabilityMassFraction = _ => 1.0
|
let toDiscreteProbabilityMassFraction = _ => 1.0
|
||||||
let mapY = mapY
|
let mapY = mapY
|
||||||
|
let mapYResult = mapYResult
|
||||||
let updateIntegralCache = updateIntegralCache
|
let updateIntegralCache = updateIntegralCache
|
||||||
let toPointSetDist = (t: t): PointSetTypes.pointSetDist => Discrete(t)
|
let toPointSetDist = (t: t): PointSetTypes.pointSetDist => Discrete(t)
|
||||||
let toContinuous = _ => None
|
let toContinuous = _ => None
|
||||||
|
|
|
@ -9,6 +9,12 @@ module type dist = {
|
||||||
~fn: float => float,
|
~fn: float => float,
|
||||||
t,
|
t,
|
||||||
) => t
|
) => t
|
||||||
|
let mapYResult: (
|
||||||
|
~integralSumCacheFn: float => option<float>=?,
|
||||||
|
~integralCacheFn: PointSetTypes.continuousShape => option<PointSetTypes.continuousShape>=?,
|
||||||
|
~fn: float => result<float, 'e>,
|
||||||
|
t,
|
||||||
|
) => result<t, 'e>
|
||||||
let xToY: (float, t) => PointSetTypes.mixedPoint
|
let xToY: (float, t) => PointSetTypes.mixedPoint
|
||||||
let toPointSetDist: t => PointSetTypes.pointSetDist
|
let toPointSetDist: t => PointSetTypes.pointSetDist
|
||||||
let toContinuous: t => option<PointSetTypes.continuousShape>
|
let toContinuous: t => option<PointSetTypes.continuousShape>
|
||||||
|
@ -37,6 +43,7 @@ module Dist = (T: dist) => {
|
||||||
let integral = T.integral
|
let integral = T.integral
|
||||||
let xTotalRange = (t: t) => maxX(t) -. minX(t)
|
let xTotalRange = (t: t) => maxX(t) -. minX(t)
|
||||||
let mapY = T.mapY
|
let mapY = T.mapY
|
||||||
|
let mapYResult = T.mapYResult
|
||||||
let xToY = T.xToY
|
let xToY = T.xToY
|
||||||
let downsample = T.downsample
|
let downsample = T.downsample
|
||||||
let toPointSetDist = T.toPointSetDist
|
let toPointSetDist = T.toPointSetDist
|
||||||
|
|
|
@ -146,8 +146,7 @@ module T = Dist({
|
||||||
let discreteIntegral = Continuous.stepwiseToLinear(Discrete.T.Integral.get(t.discrete))
|
let discreteIntegral = Continuous.stepwiseToLinear(Discrete.T.Integral.get(t.discrete))
|
||||||
|
|
||||||
Continuous.make(
|
Continuous.make(
|
||||||
XYShape.PointwiseCombination.combine(
|
XYShape.PointwiseCombination.addCombine(
|
||||||
\"+.",
|
|
||||||
XYShape.XtoY.continuousInterpolator(#Linear, #UseOutermostPoints),
|
XYShape.XtoY.continuousInterpolator(#Linear, #UseOutermostPoints),
|
||||||
Continuous.getShape(continuousIntegral),
|
Continuous.getShape(continuousIntegral),
|
||||||
Continuous.getShape(discreteIntegral),
|
Continuous.getShape(discreteIntegral),
|
||||||
|
@ -161,19 +160,20 @@ module T = Dist({
|
||||||
|
|
||||||
let integralYtoX = (f, t) => t |> integral |> Continuous.getShape |> XYShape.YtoX.linear(f)
|
let integralYtoX = (f, t) => t |> integral |> Continuous.getShape |> XYShape.YtoX.linear(f)
|
||||||
|
|
||||||
// This pipes all ys (continuous and discrete) through fn.
|
let createMixedFromContinuousDiscrete = (
|
||||||
// If mapY is a linear operation, we might be able to update the integralSumCaches as well;
|
~integralSumCacheFn=_ => None,
|
||||||
// if not, they'll be set to None.
|
~integralCacheFn=_ => None,
|
||||||
let mapY = (~integralSumCacheFn=_ => None, ~integralCacheFn=_ => None, ~fn, t: t): t => {
|
t: t,
|
||||||
|
discrete: PointSetTypes.discreteShape,
|
||||||
|
continuous: PointSetTypes.continuousShape,
|
||||||
|
): t => {
|
||||||
let yMappedDiscrete: PointSetTypes.discreteShape =
|
let yMappedDiscrete: PointSetTypes.discreteShape =
|
||||||
t.discrete
|
discrete
|
||||||
|> Discrete.T.mapY(~fn)
|
|
||||||
|> Discrete.updateIntegralSumCache(E.O.bind(t.discrete.integralSumCache, integralSumCacheFn))
|
|> Discrete.updateIntegralSumCache(E.O.bind(t.discrete.integralSumCache, integralSumCacheFn))
|
||||||
|> Discrete.updateIntegralCache(E.O.bind(t.discrete.integralCache, integralCacheFn))
|
|> Discrete.updateIntegralCache(E.O.bind(t.discrete.integralCache, integralCacheFn))
|
||||||
|
|
||||||
let yMappedContinuous: PointSetTypes.continuousShape =
|
let yMappedContinuous: PointSetTypes.continuousShape =
|
||||||
t.continuous
|
continuous
|
||||||
|> Continuous.T.mapY(~fn)
|
|
||||||
|> Continuous.updateIntegralSumCache(
|
|> Continuous.updateIntegralSumCache(
|
||||||
E.O.bind(t.continuous.integralSumCache, integralSumCacheFn),
|
E.O.bind(t.continuous.integralSumCache, integralSumCacheFn),
|
||||||
)
|
)
|
||||||
|
@ -187,6 +187,46 @@ module T = Dist({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This pipes all ys (continuous and discrete) through fn.
|
||||||
|
// If mapY is a linear operation, we might be able to update the integralSumCaches as well;
|
||||||
|
// if not, they'll be set to None.
|
||||||
|
let mapY = (
|
||||||
|
~integralSumCacheFn=_ => None,
|
||||||
|
~integralCacheFn=_ => None,
|
||||||
|
~fn: float => float,
|
||||||
|
t: t,
|
||||||
|
): t => {
|
||||||
|
let discrete = t.discrete |> Discrete.T.mapY(~fn)
|
||||||
|
let continuous = t.continuous |> Continuous.T.mapY(~fn)
|
||||||
|
createMixedFromContinuousDiscrete(
|
||||||
|
~integralCacheFn,
|
||||||
|
~integralSumCacheFn,
|
||||||
|
t,
|
||||||
|
discrete,
|
||||||
|
continuous,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let mapYResult = (
|
||||||
|
~integralSumCacheFn=_ => None,
|
||||||
|
~integralCacheFn=_ => None,
|
||||||
|
~fn: float => result<float, 'e>,
|
||||||
|
t: t,
|
||||||
|
): result<t, 'e> => {
|
||||||
|
E.R.merge(
|
||||||
|
Discrete.T.mapYResult(~fn, t.discrete),
|
||||||
|
Continuous.T.mapYResult(~fn, t.continuous),
|
||||||
|
)->E.R2.fmap(((discreteMapped, continuousMapped)) => {
|
||||||
|
createMixedFromContinuousDiscrete(
|
||||||
|
~integralCacheFn,
|
||||||
|
~integralSumCacheFn,
|
||||||
|
t,
|
||||||
|
discreteMapped,
|
||||||
|
continuousMapped,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
let mean = ({discrete, continuous}: t): float => {
|
let mean = ({discrete, continuous}: t): float => {
|
||||||
let discreteMean = Discrete.T.mean(discrete)
|
let discreteMean = Discrete.T.mean(discrete)
|
||||||
let continuousMean = Continuous.T.mean(continuous)
|
let continuousMean = Continuous.T.mean(continuous)
|
||||||
|
@ -239,7 +279,7 @@ let combineAlgebraically = (op: Operation.convolutionOperation, t1: t, t2: t): t
|
||||||
let ccConvResult = Continuous.combineAlgebraically(op, t1.continuous, t2.continuous)
|
let ccConvResult = Continuous.combineAlgebraically(op, t1.continuous, t2.continuous)
|
||||||
let dcConvResult = Continuous.combineAlgebraicallyWithDiscrete(op, t2.continuous, t1.discrete)
|
let dcConvResult = Continuous.combineAlgebraicallyWithDiscrete(op, t2.continuous, t1.discrete)
|
||||||
let cdConvResult = Continuous.combineAlgebraicallyWithDiscrete(op, t1.continuous, t2.discrete)
|
let cdConvResult = Continuous.combineAlgebraicallyWithDiscrete(op, t1.continuous, t2.discrete)
|
||||||
let continuousConvResult = Continuous.reduce(\"+.", [ccConvResult, dcConvResult, cdConvResult])
|
let continuousConvResult = Continuous.sum([ccConvResult, dcConvResult, cdConvResult])
|
||||||
|
|
||||||
// ... finally, discrete (*) discrete => discrete, obviously:
|
// ... finally, discrete (*) discrete => discrete, obviously:
|
||||||
let discreteConvResult = Discrete.combineAlgebraically(op, t1.discrete, t2.discrete)
|
let discreteConvResult = Discrete.combineAlgebraically(op, t1.discrete, t2.discrete)
|
||||||
|
@ -261,10 +301,10 @@ let combineAlgebraically = (op: Operation.convolutionOperation, t1: t, t2: t): t
|
||||||
let combinePointwise = (
|
let combinePointwise = (
|
||||||
~integralSumCachesFn=(_, _) => None,
|
~integralSumCachesFn=(_, _) => None,
|
||||||
~integralCachesFn=(_, _) => None,
|
~integralCachesFn=(_, _) => None,
|
||||||
fn,
|
fn: (float, float) => result<float, 'e>,
|
||||||
t1: t,
|
t1: t,
|
||||||
t2: t,
|
t2: t,
|
||||||
): t => {
|
): result<t, 'e> => {
|
||||||
let reducedDiscrete =
|
let reducedDiscrete =
|
||||||
[t1, t2] |> E.A.fmap(toDiscrete) |> E.A.O.concatSomes |> Discrete.reduce(~integralSumCachesFn)
|
[t1, t2] |> E.A.fmap(toDiscrete) |> E.A.O.concatSomes |> Discrete.reduce(~integralSumCachesFn)
|
||||||
|
|
||||||
|
@ -285,11 +325,12 @@ let combinePointwise = (
|
||||||
t1.integralCache,
|
t1.integralCache,
|
||||||
t2.integralCache,
|
t2.integralCache,
|
||||||
)
|
)
|
||||||
|
reducedContinuous->E.R2.fmap(continuous =>
|
||||||
make(
|
make(
|
||||||
~integralSumCache=combinedIntegralSum,
|
~integralSumCache=combinedIntegralSum,
|
||||||
~integralCache=combinedIntegral,
|
~integralCache=combinedIntegral,
|
||||||
~discrete=reducedDiscrete,
|
~discrete=reducedDiscrete,
|
||||||
~continuous=reducedContinuous,
|
~continuous,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,13 @@ let fmap = ((fn1, fn2, fn3), t: t): t =>
|
||||||
| Continuous(m) => Continuous(fn3(m))
|
| Continuous(m) => Continuous(fn3(m))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let fmapResult = ((fn1, fn2, fn3), t: t): result<t, 'e> =>
|
||||||
|
switch t {
|
||||||
|
| Mixed(m) => fn1(m)->E.R2.fmap(x => PointSetTypes.Mixed(x))
|
||||||
|
| Discrete(m) => fn2(m)->E.R2.fmap(x => PointSetTypes.Discrete(x))
|
||||||
|
| Continuous(m) => fn3(m)->E.R2.fmap(x => PointSetTypes.Continuous(x))
|
||||||
|
}
|
||||||
|
|
||||||
let toMixed = mapToAll((
|
let toMixed = mapToAll((
|
||||||
m => m,
|
m => m,
|
||||||
d =>
|
d =>
|
||||||
|
@ -53,19 +60,28 @@ let combinePointwise = (
|
||||||
PointSetTypes.continuousShape,
|
PointSetTypes.continuousShape,
|
||||||
PointSetTypes.continuousShape,
|
PointSetTypes.continuousShape,
|
||||||
) => option<PointSetTypes.continuousShape>=(_, _) => None,
|
) => option<PointSetTypes.continuousShape>=(_, _) => None,
|
||||||
fn,
|
fn: (float, float) => result<float, Operation.Error.t>,
|
||||||
t1: t,
|
t1: t,
|
||||||
t2: t,
|
t2: t,
|
||||||
) =>
|
): result<PointSetTypes.pointSetDist, Operation.Error.t> =>
|
||||||
switch (t1, t2) {
|
switch (t1, t2) {
|
||||||
| (Continuous(m1), Continuous(m2)) =>
|
| (Continuous(m1), Continuous(m2)) =>
|
||||||
PointSetTypes.Continuous(Continuous.combinePointwise(~integralSumCachesFn, fn, m1, m2))
|
Continuous.combinePointwise(
|
||||||
|
~integralSumCachesFn,
|
||||||
|
fn,
|
||||||
|
m1,
|
||||||
|
m2,
|
||||||
|
)->E.R2.fmap(x => PointSetTypes.Continuous(x))
|
||||||
| (Discrete(m1), Discrete(m2)) =>
|
| (Discrete(m1), Discrete(m2)) =>
|
||||||
PointSetTypes.Discrete(Discrete.combinePointwise(~integralSumCachesFn, m1, m2))
|
Ok(PointSetTypes.Discrete(Discrete.combinePointwise(~integralSumCachesFn, m1, m2)))
|
||||||
| (m1, m2) =>
|
| (m1, m2) =>
|
||||||
PointSetTypes.Mixed(
|
Mixed.combinePointwise(
|
||||||
Mixed.combinePointwise(~integralSumCachesFn, ~integralCachesFn, fn, toMixed(m1), toMixed(m2)),
|
~integralSumCachesFn,
|
||||||
)
|
~integralCachesFn,
|
||||||
|
fn,
|
||||||
|
toMixed(m1),
|
||||||
|
toMixed(m2),
|
||||||
|
)->E.R2.fmap(x => PointSetTypes.Mixed(x))
|
||||||
}
|
}
|
||||||
|
|
||||||
module T = Dist({
|
module T = Dist({
|
||||||
|
@ -130,13 +146,26 @@ module T = Dist({
|
||||||
let integralYtoX = f =>
|
let integralYtoX = f =>
|
||||||
mapToAll((Mixed.T.Integral.yToX(f), Discrete.T.Integral.yToX(f), Continuous.T.Integral.yToX(f)))
|
mapToAll((Mixed.T.Integral.yToX(f), Discrete.T.Integral.yToX(f), Continuous.T.Integral.yToX(f)))
|
||||||
let maxX = mapToAll((Mixed.T.maxX, Discrete.T.maxX, Continuous.T.maxX))
|
let maxX = mapToAll((Mixed.T.maxX, Discrete.T.maxX, Continuous.T.maxX))
|
||||||
let mapY = (~integralSumCacheFn=_ => None, ~integralCacheFn=_ => None, ~fn) =>
|
let mapY = (~integralSumCacheFn=_ => None, ~integralCacheFn=_ => None, ~fn: float => float): (
|
||||||
|
t => t
|
||||||
|
) =>
|
||||||
fmap((
|
fmap((
|
||||||
Mixed.T.mapY(~integralSumCacheFn, ~integralCacheFn, ~fn),
|
Mixed.T.mapY(~integralSumCacheFn, ~integralCacheFn, ~fn),
|
||||||
Discrete.T.mapY(~integralSumCacheFn, ~integralCacheFn, ~fn),
|
Discrete.T.mapY(~integralSumCacheFn, ~integralCacheFn, ~fn),
|
||||||
Continuous.T.mapY(~integralSumCacheFn, ~integralCacheFn, ~fn),
|
Continuous.T.mapY(~integralSumCacheFn, ~integralCacheFn, ~fn),
|
||||||
))
|
))
|
||||||
|
|
||||||
|
let mapYResult = (
|
||||||
|
~integralSumCacheFn=_ => None,
|
||||||
|
~integralCacheFn=_ => None,
|
||||||
|
~fn: float => result<float, 'e>,
|
||||||
|
): (t => result<t, 'e>) =>
|
||||||
|
fmapResult((
|
||||||
|
Mixed.T.mapYResult(~integralSumCacheFn, ~integralCacheFn, ~fn),
|
||||||
|
Discrete.T.mapYResult(~integralSumCacheFn, ~integralCacheFn, ~fn),
|
||||||
|
Continuous.T.mapYResult(~integralSumCacheFn, ~integralCacheFn, ~fn),
|
||||||
|
))
|
||||||
|
|
||||||
let mean = (t: t): float =>
|
let mean = (t: t): float =>
|
||||||
switch t {
|
switch t {
|
||||||
| Mixed(m) => Mixed.T.mean(m)
|
| Mixed(m) => Mixed.T.mean(m)
|
||||||
|
@ -195,8 +224,8 @@ let operate = (distToFloatOp: Operation.distToFloatOperation, s): float =>
|
||||||
| #Mean => T.mean(s)
|
| #Mean => T.mean(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
let toSparkline = (t: t, bucketCount) =>
|
let toSparkline = (t: t, bucketCount): result<string, PointSetTypes.sparklineError> =>
|
||||||
T.toContinuous(t)
|
T.toContinuous(t)
|
||||||
->E.O2.fmap(Continuous.downsampleEquallyOverX(bucketCount))
|
->E.O2.fmap(Continuous.downsampleEquallyOverX(bucketCount))
|
||||||
->E.O2.toResult("toContinous Error: Could not convert into continuous distribution")
|
->E.O2.toResult(PointSetTypes.CannotSparklineDiscrete)
|
||||||
->E.R2.fmap(r => Continuous.getShape(r).ys->Sparklines.create())
|
->E.R2.fmap(r => Continuous.getShape(r).ys->Sparklines.create())
|
||||||
|
|
|
@ -94,3 +94,11 @@ module MixedPoint = {
|
||||||
|
|
||||||
let add = combine2((a, b) => a +. b)
|
let add = combine2((a, b) => a +. b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@genType
|
||||||
|
type sparklineError = CannotSparklineDiscrete
|
||||||
|
|
||||||
|
let sparklineErrorToString = (err: sparklineError): string =>
|
||||||
|
switch err {
|
||||||
|
| CannotSparklineDiscrete => "Cannot find the sparkline of a discrete distribution"
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,24 @@
|
||||||
|
@genType
|
||||||
|
module Error = {
|
||||||
|
@genType
|
||||||
|
type sampleSetError = TooFewSamples
|
||||||
|
|
||||||
|
let sampleSetErrorToString = (err: sampleSetError): string =>
|
||||||
|
switch err {
|
||||||
|
| TooFewSamples => "Too few samples when constructing sample set"
|
||||||
|
}
|
||||||
|
|
||||||
|
@genType
|
||||||
|
type pointsetConversionError = TooFewSamplesForConversionToPointSet
|
||||||
|
|
||||||
|
let pointsetConversionErrorToString = (err: pointsetConversionError) =>
|
||||||
|
switch err {
|
||||||
|
| TooFewSamplesForConversionToPointSet => "Too Few Samples to convert to point set"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
include Error
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This is used as a smart constructor. The only way to create a SampleSetDist.t is to call
|
This is used as a smart constructor. The only way to create a SampleSetDist.t is to call
|
||||||
this constructor.
|
this constructor.
|
||||||
|
@ -8,7 +29,7 @@ module T: {
|
||||||
//When we get a good functional library in TS, we could refactor that out.
|
//When we get a good functional library in TS, we could refactor that out.
|
||||||
@genType
|
@genType
|
||||||
type t = array<float>
|
type t = array<float>
|
||||||
let make: array<float> => result<t, string>
|
let make: array<float> => result<t, sampleSetError>
|
||||||
let get: t => array<float>
|
let get: t => array<float>
|
||||||
} = {
|
} = {
|
||||||
type t = array<float>
|
type t = array<float>
|
||||||
|
@ -16,7 +37,7 @@ module T: {
|
||||||
if E.A.length(a) > 5 {
|
if E.A.length(a) > 5 {
|
||||||
Ok(a)
|
Ok(a)
|
||||||
} else {
|
} else {
|
||||||
Error("too small")
|
Error(TooFewSamples)
|
||||||
}
|
}
|
||||||
let get = (a: t) => a
|
let get = (a: t) => a
|
||||||
}
|
}
|
||||||
|
@ -31,13 +52,13 @@ some refactoring.
|
||||||
*/
|
*/
|
||||||
let toPointSetDist = (~samples: t, ~samplingInputs: SamplingInputs.samplingInputs): result<
|
let toPointSetDist = (~samples: t, ~samplingInputs: SamplingInputs.samplingInputs): result<
|
||||||
PointSetTypes.pointSetDist,
|
PointSetTypes.pointSetDist,
|
||||||
string,
|
pointsetConversionError,
|
||||||
> =>
|
> =>
|
||||||
SampleSetDist_ToPointSet.toPointSetDist(
|
SampleSetDist_ToPointSet.toPointSetDist(
|
||||||
~samples=get(samples),
|
~samples=get(samples),
|
||||||
~samplingInputs,
|
~samplingInputs,
|
||||||
(),
|
(),
|
||||||
).pointSetDist->E.O2.toResult("Failed to convert to PointSetDist")
|
).pointSetDist->E.O2.toResult(TooFewSamplesForConversionToPointSet)
|
||||||
|
|
||||||
//Randomly get one sample from the distribution
|
//Randomly get one sample from the distribution
|
||||||
let sample = (t: t): float => {
|
let sample = (t: t): float => {
|
||||||
|
@ -62,7 +83,18 @@ let sampleN = (t: t, n) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Figure out what to do if distributions are different lengths. ``zip`` is kind of inelegant for this.
|
//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 map2 = (~fn: (float, float) => result<float, Operation.Error.t>, ~t1: t, ~t2: t): result<
|
||||||
|
t,
|
||||||
|
Operation.Error.t,
|
||||||
|
> => {
|
||||||
let samples = Belt.Array.zip(get(t1), get(t2))->E.A2.fmap(((a, b)) => fn(a, b))
|
let samples = Belt.Array.zip(get(t1), get(t2))->E.A2.fmap(((a, b)) => fn(a, b))
|
||||||
make(samples)
|
|
||||||
|
// This assertion should never be reached. In order for it to be reached, one
|
||||||
|
// of the input parameters would need to be a sample set distribution with less
|
||||||
|
// than 6 samples. Which should be impossible due to the smart constructor.
|
||||||
|
// I could prove this to the type system (say, creating a {first: float, second: float, ..., fifth: float, rest: array<float>}
|
||||||
|
// But doing so would take too much time, so I'll leave it as an assertion
|
||||||
|
E.A.R.firstErrorOrOpen(samples)->E.R2.fmap(x =>
|
||||||
|
E.R.toExn("Input of samples should be larger than 5", make(x))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,7 +83,7 @@ let toPointSetDist = (
|
||||||
~samples: Internals.T.t,
|
~samples: Internals.T.t,
|
||||||
~samplingInputs: SamplingInputs.samplingInputs,
|
~samplingInputs: SamplingInputs.samplingInputs,
|
||||||
(),
|
(),
|
||||||
) => {
|
): Internals.Types.outputs => {
|
||||||
Array.fast_sort(compare, samples)
|
Array.fast_sort(compare, samples)
|
||||||
let (continuousPart, discretePart) = E.A.Sorted.Floats.split(samples)
|
let (continuousPart, discretePart) = E.A.Sorted.Floats.split(samples)
|
||||||
let length = samples |> E.A.length |> float_of_int
|
let length = samples |> E.A.length |> float_of_int
|
||||||
|
|
|
@ -379,7 +379,7 @@ module T = {
|
||||||
): analyticalSimplificationResult =>
|
): analyticalSimplificationResult =>
|
||||||
switch (d1, d2) {
|
switch (d1, d2) {
|
||||||
| (#Float(v1), #Float(v2)) =>
|
| (#Float(v1), #Float(v2)) =>
|
||||||
switch Operation.Algebraic.applyFn(op, v1, v2) {
|
switch Operation.Algebraic.toFn(op, v1, v2) {
|
||||||
| Ok(r) => #AnalyticalSolution(#Float(r))
|
| Ok(r) => #AnalyticalSolution(#Float(r))
|
||||||
| Error(n) => #Error(n)
|
| Error(n) => #Error(n)
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,6 @@ type symbolicDist = [
|
||||||
|
|
||||||
type analyticalSimplificationResult = [
|
type analyticalSimplificationResult = [
|
||||||
| #AnalyticalSolution(symbolicDist)
|
| #AnalyticalSolution(symbolicDist)
|
||||||
| #Error(string)
|
| #Error(Operation.Error.t)
|
||||||
| #NoSolution
|
| #NoSolution
|
||||||
]
|
]
|
||||||
|
|
|
@ -9,6 +9,7 @@ type errorValue =
|
||||||
| RERecordPropertyNotFound(string, string)
|
| RERecordPropertyNotFound(string, string)
|
||||||
| RESymbolNotFound(string)
|
| RESymbolNotFound(string)
|
||||||
| RESyntaxError(string)
|
| RESyntaxError(string)
|
||||||
|
| REDistributionError(DistributionTypes.error)
|
||||||
| RETodo(string) // To do
|
| RETodo(string) // To do
|
||||||
|
|
||||||
type t = errorValue
|
type t = errorValue
|
||||||
|
@ -20,6 +21,7 @@ let errorToString = err =>
|
||||||
| REAssignmentExpected => "Assignment expected"
|
| REAssignmentExpected => "Assignment expected"
|
||||||
| REExpressionExpected => "Expression expected"
|
| REExpressionExpected => "Expression expected"
|
||||||
| REFunctionExpected(msg) => `Function expected: ${msg}`
|
| REFunctionExpected(msg) => `Function expected: ${msg}`
|
||||||
|
| REDistributionError(err) => `Math Error: ${DistributionTypes.Error.toString(err)}`
|
||||||
| REJavaScriptExn(omsg, oname) => {
|
| REJavaScriptExn(omsg, oname) => {
|
||||||
let answer = "JS Exception:"
|
let answer = "JS Exception:"
|
||||||
let answer = switch oname {
|
let answer = switch oname {
|
||||||
|
|
|
@ -10,7 +10,7 @@ type rec expressionValue =
|
||||||
| EvArray(array<expressionValue>)
|
| EvArray(array<expressionValue>)
|
||||||
| EvBool(bool)
|
| EvBool(bool)
|
||||||
| EvCall(string) // External function call
|
| EvCall(string) // External function call
|
||||||
| EvDistribution(GenericDist_Types.genericDist)
|
| EvDistribution(DistributionTypes.genericDist)
|
||||||
| EvNumber(float)
|
| EvNumber(float)
|
||||||
| EvRecord(Js.Dict.t<expressionValue>)
|
| EvRecord(Js.Dict.t<expressionValue>)
|
||||||
| EvString(string)
|
| EvString(string)
|
||||||
|
|
|
@ -24,13 +24,12 @@ module Helpers = {
|
||||||
| "dotPow" => #Power
|
| "dotPow" => #Power
|
||||||
| "multiply" => #Multiply
|
| "multiply" => #Multiply
|
||||||
| "dotMultiply" => #Multiply
|
| "dotMultiply" => #Multiply
|
||||||
| "dotLog" => #Logarithm
|
|
||||||
| _ => #Multiply
|
| _ => #Multiply
|
||||||
}
|
}
|
||||||
|
|
||||||
let catchAndConvertTwoArgsToDists = (args: array<expressionValue>): option<(
|
let catchAndConvertTwoArgsToDists = (args: array<expressionValue>): option<(
|
||||||
GenericDist_Types.genericDist,
|
DistributionTypes.genericDist,
|
||||||
GenericDist_Types.genericDist,
|
DistributionTypes.genericDist,
|
||||||
)> => {
|
)> => {
|
||||||
switch args {
|
switch args {
|
||||||
| [EvDistribution(a), EvDistribution(b)] => Some((a, b))
|
| [EvDistribution(a), EvDistribution(b)] => Some((a, b))
|
||||||
|
@ -41,33 +40,41 @@ module Helpers = {
|
||||||
}
|
}
|
||||||
|
|
||||||
let toFloatFn = (
|
let toFloatFn = (
|
||||||
fnCall: GenericDist_Types.Operation.toFloat,
|
fnCall: DistributionTypes.DistributionOperation.toFloat,
|
||||||
dist: GenericDist_Types.genericDist,
|
dist: DistributionTypes.genericDist,
|
||||||
) => {
|
) => {
|
||||||
FromDist(GenericDist_Types.Operation.ToFloat(fnCall), dist)->runGenericOperation->Some
|
FromDist(DistributionTypes.DistributionOperation.ToFloat(fnCall), dist)
|
||||||
|
->runGenericOperation
|
||||||
|
->Some
|
||||||
}
|
}
|
||||||
|
|
||||||
let toStringFn = (
|
let toStringFn = (
|
||||||
fnCall: GenericDist_Types.Operation.toString,
|
fnCall: DistributionTypes.DistributionOperation.toString,
|
||||||
dist: GenericDist_Types.genericDist,
|
dist: DistributionTypes.genericDist,
|
||||||
) => {
|
) => {
|
||||||
FromDist(GenericDist_Types.Operation.ToString(fnCall), dist)->runGenericOperation->Some
|
FromDist(DistributionTypes.DistributionOperation.ToString(fnCall), dist)
|
||||||
|
->runGenericOperation
|
||||||
|
->Some
|
||||||
}
|
}
|
||||||
|
|
||||||
let toBoolFn = (
|
let toBoolFn = (
|
||||||
fnCall: GenericDist_Types.Operation.toBool,
|
fnCall: DistributionTypes.DistributionOperation.toBool,
|
||||||
dist: GenericDist_Types.genericDist,
|
dist: DistributionTypes.genericDist,
|
||||||
) => {
|
) => {
|
||||||
FromDist(GenericDist_Types.Operation.ToBool(fnCall), dist)->runGenericOperation->Some
|
FromDist(DistributionTypes.DistributionOperation.ToBool(fnCall), dist)
|
||||||
|
->runGenericOperation
|
||||||
|
->Some
|
||||||
}
|
}
|
||||||
|
|
||||||
let toDistFn = (fnCall: GenericDist_Types.Operation.toDist, dist) => {
|
let toDistFn = (fnCall: DistributionTypes.DistributionOperation.toDist, dist) => {
|
||||||
FromDist(GenericDist_Types.Operation.ToDist(fnCall), dist)->runGenericOperation->Some
|
FromDist(DistributionTypes.DistributionOperation.ToDist(fnCall), dist)
|
||||||
|
->runGenericOperation
|
||||||
|
->Some
|
||||||
}
|
}
|
||||||
|
|
||||||
let twoDiststoDistFn = (direction, arithmetic, dist1, dist2) => {
|
let twoDiststoDistFn = (direction, arithmetic, dist1, dist2) => {
|
||||||
FromDist(
|
FromDist(
|
||||||
GenericDist_Types.Operation.ToDistCombination(
|
DistributionTypes.DistributionOperation.ToDistCombination(
|
||||||
direction,
|
direction,
|
||||||
arithmeticMap(arithmetic),
|
arithmeticMap(arithmetic),
|
||||||
#Dist(dist2),
|
#Dist(dist2),
|
||||||
|
@ -84,7 +91,7 @@ module Helpers = {
|
||||||
let parseNumberArray = (ags: array<expressionValue>): Belt.Result.t<array<float>, string> =>
|
let parseNumberArray = (ags: array<expressionValue>): Belt.Result.t<array<float>, string> =>
|
||||||
E.A.fmap(parseNumber, ags) |> E.A.R.firstErrorOrOpen
|
E.A.fmap(parseNumber, ags) |> E.A.R.firstErrorOrOpen
|
||||||
|
|
||||||
let parseDist = (args: expressionValue): Belt.Result.t<GenericDist_Types.genericDist, string> =>
|
let parseDist = (args: expressionValue): Belt.Result.t<DistributionTypes.genericDist, string> =>
|
||||||
switch args {
|
switch args {
|
||||||
| EvDistribution(x) => Ok(x)
|
| EvDistribution(x) => Ok(x)
|
||||||
| EvNumber(x) => Ok(GenericDist.fromFloat(x))
|
| EvNumber(x) => Ok(GenericDist.fromFloat(x))
|
||||||
|
@ -92,12 +99,12 @@ module Helpers = {
|
||||||
}
|
}
|
||||||
|
|
||||||
let parseDistributionArray = (ags: array<expressionValue>): Belt.Result.t<
|
let parseDistributionArray = (ags: array<expressionValue>): Belt.Result.t<
|
||||||
array<GenericDist_Types.genericDist>,
|
array<DistributionTypes.genericDist>,
|
||||||
string,
|
string,
|
||||||
> => E.A.fmap(parseDist, ags) |> E.A.R.firstErrorOrOpen
|
> => E.A.fmap(parseDist, ags) |> E.A.R.firstErrorOrOpen
|
||||||
|
|
||||||
let mixtureWithGivenWeights = (
|
let mixtureWithGivenWeights = (
|
||||||
distributions: array<GenericDist_Types.genericDist>,
|
distributions: array<DistributionTypes.genericDist>,
|
||||||
weights: array<float>,
|
weights: array<float>,
|
||||||
): DistributionOperation.outputType =>
|
): DistributionOperation.outputType =>
|
||||||
E.A.length(distributions) == E.A.length(weights)
|
E.A.length(distributions) == E.A.length(weights)
|
||||||
|
@ -107,7 +114,7 @@ module Helpers = {
|
||||||
)
|
)
|
||||||
|
|
||||||
let mixtureWithDefaultWeights = (
|
let mixtureWithDefaultWeights = (
|
||||||
distributions: array<GenericDist_Types.genericDist>,
|
distributions: array<DistributionTypes.genericDist>,
|
||||||
): DistributionOperation.outputType => {
|
): DistributionOperation.outputType => {
|
||||||
let length = E.A.length(distributions)
|
let length = E.A.length(distributions)
|
||||||
let weights = Belt.Array.make(length, 1.0 /. Belt.Int.toFloat(length))
|
let weights = Belt.Array.make(length, 1.0 /. Belt.Int.toFloat(length))
|
||||||
|
@ -165,7 +172,7 @@ module SymbolicConstructors = {
|
||||||
): option<DistributionOperation.outputType> =>
|
): option<DistributionOperation.outputType> =>
|
||||||
switch symbolicResult {
|
switch symbolicResult {
|
||||||
| Ok(r) => Some(Dist(Symbolic(r)))
|
| Ok(r) => Some(Dist(Symbolic(r)))
|
||||||
| Error(r) => Some(GenDistError(Other(r)))
|
| Error(r) => Some(GenDistError(OtherError(r)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,15 +242,12 @@ let dispatchToGenericOutput = (call: ExpressionValue.functionCall): option<
|
||||||
| "dotMultiply"
|
| "dotMultiply"
|
||||||
| "dotSubtract"
|
| "dotSubtract"
|
||||||
| "dotDivide"
|
| "dotDivide"
|
||||||
| "dotPow"
|
| "dotPow") as arithmetic,
|
||||||
| "dotLog") as arithmetic,
|
|
||||||
[_, _] as args,
|
[_, _] as args,
|
||||||
) =>
|
) =>
|
||||||
Helpers.catchAndConvertTwoArgsToDists(args)->E.O2.fmap(((fst, snd)) =>
|
Helpers.catchAndConvertTwoArgsToDists(args)->E.O2.fmap(((fst, snd)) =>
|
||||||
Helpers.twoDiststoDistFn(Pointwise, arithmetic, fst, snd)
|
Helpers.twoDiststoDistFn(Pointwise, arithmetic, fst, snd)
|
||||||
)
|
)
|
||||||
| ("dotLog", [EvDistribution(a)]) =>
|
|
||||||
Helpers.twoDiststoDistFn(Pointwise, "dotLog", a, GenericDist.fromFloat(Math.e))->Some
|
|
||||||
| ("dotExp", [EvDistribution(a)]) =>
|
| ("dotExp", [EvDistribution(a)]) =>
|
||||||
Helpers.twoDiststoDistFn(Pointwise, "dotPow", GenericDist.fromFloat(Math.e), a)->Some
|
Helpers.twoDiststoDistFn(Pointwise, "dotPow", GenericDist.fromFloat(Math.e), a)->Some
|
||||||
| _ => None
|
| _ => None
|
||||||
|
@ -259,12 +263,7 @@ let genericOutputToReducerValue = (o: DistributionOperation.outputType): result<
|
||||||
| Float(d) => Ok(EvNumber(d))
|
| Float(d) => Ok(EvNumber(d))
|
||||||
| String(d) => Ok(EvString(d))
|
| String(d) => Ok(EvString(d))
|
||||||
| Bool(d) => Ok(EvBool(d))
|
| Bool(d) => Ok(EvBool(d))
|
||||||
| GenDistError(NotYetImplemented) => Error(RETodo("Function not yet implemented"))
|
| GenDistError(err) => Error(REDistributionError(err))
|
||||||
| GenDistError(Unreachable) => Error(RETodo("Unreachable"))
|
|
||||||
| GenDistError(DistributionVerticalShiftIsInvalid) =>
|
|
||||||
Error(RETodo("Distribution Vertical Shift Is Invalid"))
|
|
||||||
| GenDistError(ArgumentError(err)) => Error(RETodo("Argument Error: " ++ err))
|
|
||||||
| GenDistError(Other(s)) => Error(RETodo(s))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let dispatch = call => {
|
let dispatch = call => {
|
||||||
|
|
|
@ -53,4 +53,4 @@ type continuousShape = PointSetTypes.continuousShape
|
||||||
let errorValueToString = Reducer_ErrorValue.errorToString
|
let errorValueToString = Reducer_ErrorValue.errorToString
|
||||||
|
|
||||||
@genType
|
@genType
|
||||||
let distributionErrorToString = GenericDist_Types.Error.toString
|
let distributionErrorToString = DistributionTypes.Error.toString
|
||||||
|
|
|
@ -152,13 +152,20 @@ module I = {
|
||||||
let toString = Js.Int.toString
|
let toString = Js.Int.toString
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exception Assertion(string)
|
||||||
|
|
||||||
/* R for Result */
|
/* R for Result */
|
||||||
module R = {
|
module R = {
|
||||||
let result = Rationale.Result.result
|
let result = Rationale.Result.result
|
||||||
let id = e => e |> result(U.id, U.id)
|
let id = e => e |> result(U.id, U.id)
|
||||||
let fmap = Rationale.Result.fmap
|
let fmap = Rationale.Result.fmap
|
||||||
let bind = Rationale.Result.bind
|
let bind = Rationale.Result.bind
|
||||||
let toExn = Belt.Result.getExn
|
let toExn = (msg: string, x: result<'a, 'b>): 'a =>
|
||||||
|
switch x {
|
||||||
|
| Ok(r) => r
|
||||||
|
| Error(_) => raise(Assertion(msg))
|
||||||
|
}
|
||||||
|
|
||||||
let default = (default, res: Belt.Result.t<'a, 'b>) =>
|
let default = (default, res: Belt.Result.t<'a, 'b>) =>
|
||||||
switch res {
|
switch res {
|
||||||
| Ok(r) => r
|
| Ok(r) => r
|
||||||
|
@ -185,6 +192,7 @@ module R = {
|
||||||
| Ok(f) => fmap(f, a)
|
| Ok(f) => fmap(f, a)
|
||||||
| Error(err) => Error(err)
|
| Error(err) => Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// (a1 -> a2 -> r) -> m a1 -> m a2 -> m r // not in Rationale
|
// (a1 -> a2 -> r) -> m a1 -> m a2 -> m r // not in Rationale
|
||||||
let liftM2: (('a, 'b) => 'c, result<'a, 'd>, result<'b, 'd>) => result<'c, 'd> = (op, xR, yR) => {
|
let liftM2: (('a, 'b) => 'c, result<'a, 'd>, result<'b, 'd>) => result<'c, 'd> = (op, xR, yR) => {
|
||||||
ap'(fmap(op, xR), yR)
|
ap'(fmap(op, xR), yR)
|
||||||
|
@ -210,10 +218,10 @@ module R2 = {
|
||||||
let bind = (a, b) => R.bind(b, a)
|
let bind = (a, b) => R.bind(b, a)
|
||||||
|
|
||||||
//Converts result type to change error type only
|
//Converts result type to change error type only
|
||||||
let errMap = (a, map) =>
|
let errMap = (a: result<'a, 'b>, map: 'b => 'c): result<'a, 'c> =>
|
||||||
switch a {
|
switch a {
|
||||||
| Ok(r) => Ok(r)
|
| Ok(r) => Ok(r)
|
||||||
| Error(e) => map(e)
|
| Error(e) => Error(map(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
let fmap2 = (xR, f) =>
|
let fmap2 = (xR, f) =>
|
||||||
|
@ -436,6 +444,32 @@ module A = {
|
||||||
r |> Belt.Array.map(_, r => Belt.Result.getExn(r))
|
r |> Belt.Array.map(_, r => Belt.Result.getExn(r))
|
||||||
bringErrorUp |> Belt.Result.map(_, forceOpen)
|
bringErrorUp |> Belt.Result.map(_, forceOpen)
|
||||||
}
|
}
|
||||||
|
let filterOk = (x: array<result<'a, 'b>>): array<'a> => fmap(R.toOption, x)->O.concatSomes
|
||||||
|
|
||||||
|
let forM = (x: array<'a>, fn: 'a => result<'b, 'c>): result<array<'b>, 'c> =>
|
||||||
|
firstErrorOrOpen(fmap(fn, x))
|
||||||
|
|
||||||
|
let foldM = (fn: ('c, 'a) => result<'b, 'e>, init: 'c, x: array<'a>): result<'c, 'e> => {
|
||||||
|
let acc = ref(init)
|
||||||
|
let final = ref(Ok())
|
||||||
|
let break = ref(false)
|
||||||
|
let i = ref(0)
|
||||||
|
|
||||||
|
while break.contents != true && i.contents < length(x) {
|
||||||
|
switch fn(acc.contents, x[i.contents]) {
|
||||||
|
| Ok(r) => acc := r
|
||||||
|
| Error(err) => {
|
||||||
|
final := Error(err)
|
||||||
|
break := true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i := i.contents + 1
|
||||||
|
}
|
||||||
|
switch final.contents {
|
||||||
|
| Ok(_) => Ok(acc.contents)
|
||||||
|
| Error(err) => Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module Sorted = {
|
module Sorted = {
|
||||||
|
|
|
@ -37,22 +37,59 @@ module Convolution = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module Algebraic = {
|
type operationError =
|
||||||
type t = algebraicOperation
|
| DivisionByZeroError
|
||||||
let toFn: (t, float, float) => float = x =>
|
| ComplexNumberError
|
||||||
switch x {
|
|
||||||
| #Add => \"+."
|
@genType
|
||||||
| #Subtract => \"-."
|
module Error = {
|
||||||
| #Multiply => \"*."
|
@genType
|
||||||
| #Power => \"**"
|
type t = operationError
|
||||||
| #Divide => \"/."
|
|
||||||
| #Logarithm => (a, b) => log(a) /. log(b)
|
let toString = (err: t): string =>
|
||||||
|
switch err {
|
||||||
|
| DivisionByZeroError => "Cannot divide by zero"
|
||||||
|
| ComplexNumberError => "Operation returned complex result"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let applyFn = (t, f1, f2) =>
|
let power = (a: float, b: float): result<float, Error.t> =>
|
||||||
switch (t, f1, f2) {
|
if a >= 0.0 {
|
||||||
| (#Divide, _, 0.) => Error("Cannot divide $v1 by zero.")
|
Ok(a ** b)
|
||||||
| _ => Ok(toFn(t, f1, f2))
|
} else {
|
||||||
|
Error(ComplexNumberError)
|
||||||
|
}
|
||||||
|
|
||||||
|
let divide = (a: float, b: float): result<float, Error.t> =>
|
||||||
|
if b != 0.0 {
|
||||||
|
Ok(a /. b)
|
||||||
|
} else {
|
||||||
|
Error(DivisionByZeroError)
|
||||||
|
}
|
||||||
|
|
||||||
|
let logarithm = (a: float, b: float): result<float, Error.t> =>
|
||||||
|
if b == 1. {
|
||||||
|
Error(DivisionByZeroError)
|
||||||
|
} else if b == 0. {
|
||||||
|
Ok(0.)
|
||||||
|
} else if a > 0.0 && b > 0.0 {
|
||||||
|
Ok(log(a) /. log(b))
|
||||||
|
} else {
|
||||||
|
Error(ComplexNumberError)
|
||||||
|
}
|
||||||
|
|
||||||
|
@genType
|
||||||
|
module Algebraic = {
|
||||||
|
@genType
|
||||||
|
type t = algebraicOperation
|
||||||
|
let toFn: (t, float, float) => result<float, Error.t> = (x, a, b) =>
|
||||||
|
switch x {
|
||||||
|
| #Add => Ok(a +. b)
|
||||||
|
| #Subtract => Ok(a -. b)
|
||||||
|
| #Multiply => Ok(a *. b)
|
||||||
|
| #Power => power(a, b)
|
||||||
|
| #Divide => divide(a, b)
|
||||||
|
| #Logarithm => logarithm(a, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
let toString = x =>
|
let toString = x =>
|
||||||
|
@ -96,12 +133,12 @@ module DistToFloat = {
|
||||||
// Note that different logarithms don't really do anything.
|
// Note that different logarithms don't really do anything.
|
||||||
module Scale = {
|
module Scale = {
|
||||||
type t = scaleOperation
|
type t = scaleOperation
|
||||||
let toFn = x =>
|
let toFn = (x: t, a: float, b: float): result<float, Error.t> =>
|
||||||
switch x {
|
switch x {
|
||||||
| #Multiply => \"*."
|
| #Multiply => Ok(a *. b)
|
||||||
| #Divide => \"/."
|
| #Divide => divide(a, b)
|
||||||
| #Power => \"**"
|
| #Power => power(a, b)
|
||||||
| #Logarithm => (a, b) => log(a) /. log(b)
|
| #Logarithm => logarithm(a, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
let format = (operation: t, value, scaleBy) =>
|
let format = (operation: t, value, scaleBy) =>
|
||||||
|
|
|
@ -43,6 +43,10 @@ module T = {
|
||||||
let xTotalRange = (t: t) => maxX(t) -. minX(t)
|
let xTotalRange = (t: t) => maxX(t) -. minX(t)
|
||||||
let mapX = (fn, t: t): t => {xs: E.A.fmap(fn, t.xs), ys: t.ys}
|
let mapX = (fn, t: t): t => {xs: E.A.fmap(fn, t.xs), ys: t.ys}
|
||||||
let mapY = (fn, t: t): t => {xs: t.xs, ys: E.A.fmap(fn, t.ys)}
|
let mapY = (fn, t: t): t => {xs: t.xs, ys: E.A.fmap(fn, t.ys)}
|
||||||
|
let mapYResult = (fn: float => result<float, 'e>, t: t): result<t, 'e> => {
|
||||||
|
let mappedYs = E.A.fmap(fn, t.ys)
|
||||||
|
E.A.R.firstErrorOrOpen(mappedYs)->E.R2.fmap(y => {xs: t.xs, ys: y})
|
||||||
|
}
|
||||||
let square = mapX(x => x ** 2.0)
|
let square = mapX(x => x ** 2.0)
|
||||||
let zip = ({xs, ys}: t) => Belt.Array.zip(xs, ys)
|
let zip = ({xs, ys}: t) => Belt.Array.zip(xs, ys)
|
||||||
let fromArray = ((xs, ys)): t => {xs: xs, ys: ys}
|
let fromArray = ((xs, ys)): t => {xs: xs, ys: ys}
|
||||||
|
@ -229,7 +233,12 @@ module Zipped = {
|
||||||
|
|
||||||
module PointwiseCombination = {
|
module PointwiseCombination = {
|
||||||
// t1Interpolator and t2Interpolator are functions from XYShape.XtoY, e.g. linearBetweenPointsExtrapolateFlat.
|
// t1Interpolator and t2Interpolator are functions from XYShape.XtoY, e.g. linearBetweenPointsExtrapolateFlat.
|
||||||
let combine: ((float, float) => float, interpolator, T.t, T.t) => T.t = %raw(`
|
let combine: (
|
||||||
|
(float, float) => result<float, Operation.Error.t>,
|
||||||
|
interpolator,
|
||||||
|
T.t,
|
||||||
|
T.t,
|
||||||
|
) => result<T.t, Operation.Error.t> = %raw(`
|
||||||
// This function combines two xyShapes by looping through both of them simultaneously.
|
// This function combines two xyShapes by looping through both of them simultaneously.
|
||||||
// It always moves on to the next smallest x, whether that's in the first or second input's xs,
|
// It always moves on to the next smallest x, whether that's in the first or second input's xs,
|
||||||
// and interpolates the value on the other side, thus accumulating xs and ys.
|
// and interpolates the value on the other side, thus accumulating xs and ys.
|
||||||
|
@ -277,13 +286,28 @@ module PointwiseCombination = {
|
||||||
}
|
}
|
||||||
|
|
||||||
outX.push(x);
|
outX.push(x);
|
||||||
outY.push(fn(ya, yb));
|
|
||||||
|
// Here I check whether the operation was a success. If it was
|
||||||
|
// keep going. Otherwise, stop and throw the error back to user
|
||||||
|
let newY = fn(ya, yb);
|
||||||
|
if(newY.TAG === 0){
|
||||||
|
outY.push(newY._0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return newY;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {xs: outX, ys: outY};
|
return {TAG: 0, _0: {xs: outX, ys: outY}, [Symbol.for("name")]: "Ok"};
|
||||||
}
|
}
|
||||||
`)
|
`)
|
||||||
|
|
||||||
|
let addCombine = (interpolator: interpolator, t1: T.t, t2: T.t): T.t =>
|
||||||
|
combine((a, b) => Ok(a +. b), interpolator, t1, t2)->E.R.toExn(
|
||||||
|
"Add operation should never fail",
|
||||||
|
_,
|
||||||
|
)
|
||||||
|
|
||||||
let combineEvenXs = (~fn, ~xToYSelection, sampleCount, t1: T.t, t2: T.t) =>
|
let combineEvenXs = (~fn, ~xToYSelection, sampleCount, t1: T.t, t2: T.t) =>
|
||||||
switch (E.A.length(t1.xs), E.A.length(t2.xs)) {
|
switch (E.A.length(t1.xs), E.A.length(t2.xs)) {
|
||||||
| (0, 0) => T.empty
|
| (0, 0) => T.empty
|
||||||
|
|
|
@ -255,16 +255,6 @@ dist2 = triangular(1,2,3)
|
||||||
dist1 .^ dist2`}
|
dist1 .^ dist2`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
### Pointwise logarithm
|
|
||||||
|
|
||||||
TODO: write about the semantics and the case handling re scalar vs. dist and log base.
|
|
||||||
|
|
||||||
<SquiggleEditor
|
|
||||||
initialSquiggleString={`dist1 = 1 to 10
|
|
||||||
dist2 = triangular(1,2,3)
|
|
||||||
dotLog(dist1, dist2)`}
|
|
||||||
/>
|
|
||||||
|
|
||||||
## Standard functions on distributions
|
## Standard functions on distributions
|
||||||
|
|
||||||
### Probability density function
|
### Probability density function
|
||||||
|
|
Loading…
Reference in New Issue
Block a user