Light cleanup of ReducerInterface_GenericDistribution

This commit is contained in:
Ozzie Gooen 2022-04-01 15:41:11 -04:00
parent 18d742b63c
commit e7c2a7db01
8 changed files with 138 additions and 122 deletions

View File

@ -6,6 +6,9 @@ let makeTest = (str, result) => test(str, () => expectEvalToBe(str, result))
describe("eval", () => { describe("eval", () => {
Only.describe("expressions", () => { Only.describe("expressions", () => {
makeTest("normal(5,2)", "Ok(Normal(5,2))") makeTest("normal(5,2)", "Ok(Normal(5,2))")
makeTest("5 to 2", "Error(TODO: Low value must be less than high value.)")
makeTest("to(2,5)", "Ok(Lognormal(1.1512925464970227,0.278507821238345))")
makeTest("to(-2,2)", "Ok(Normal(0,1.215913388057542))")
makeTest("lognormal(5,2)", "Ok(Lognormal(5,2))") makeTest("lognormal(5,2)", "Ok(Lognormal(5,2))")
makeTest("mean(normal(5,2))", "Ok(5)") makeTest("mean(normal(5,2))", "Ok(5)")
makeTest("mean(lognormal(1,2))", "Ok(20.085536923187668)") makeTest("mean(lognormal(1,2))", "Ok(20.085536923187668)")
@ -21,6 +24,5 @@ describe("eval", () => {
makeTest("3+normal(5,2)", "Ok(Point Set Distribution)") makeTest("3+normal(5,2)", "Ok(Point Set Distribution)")
makeTest("add(3, 3)", "Ok(6)") makeTest("add(3, 3)", "Ok(6)")
makeTest("truncateLeft(normal(5,2), 3)", "Ok(Point Set Distribution)") makeTest("truncateLeft(normal(5,2), 3)", "Ok(Point Set Distribution)")
makeTest("mean(add(3, normal(5,2)))", "Ok(8.004619792609384)")
}) })
}) })

View File

@ -228,7 +228,7 @@ let pointwiseCombinationFloat = (
): result<t, error> => { ): result<t, error> => {
let m = switch arithmeticOperation { let m = switch arithmeticOperation {
| #Add | #Subtract => Error(GenericDist_Types.DistributionVerticalShiftIsInvalid) | #Add | #Subtract => Error(GenericDist_Types.DistributionVerticalShiftIsInvalid)
| (#Multiply | #Divide | #Exponentiate | #Log) as arithmeticOperation => | (#Multiply | #Divide | #Exponentiate | #Logarithm) as arithmeticOperation =>
toPointSetFn(t)->E.R2.fmap(t => { toPointSetFn(t)->E.R2.fmap(t => {
//TODO: Move to PointSet codebase //TODO: Move to PointSet codebase
let fn = (secondary, main) => Operation.Scale.toFn(arithmeticOperation, main, secondary) let fn = (secondary, main) => Operation.Scale.toFn(arithmeticOperation, main, secondary)

View File

@ -20,7 +20,7 @@ module Operation = {
| #Subtract | #Subtract
| #Divide | #Divide
| #Exponentiate | #Exponentiate
| #Log | #Logarithm
] ]
let arithmeticToFn = (arithmetic: arithmeticOperation) => let arithmeticToFn = (arithmetic: arithmeticOperation) =>
@ -30,7 +30,7 @@ module Operation = {
| #Subtract => \"-." | #Subtract => \"-."
| #Exponentiate => \"**" | #Exponentiate => \"**"
| #Divide => \"/." | #Divide => \"/."
| #Log => (a, b) => log(a) /. log(b) | #Logarithm => (a, b) => log(a) /. log(b)
} }
type toFloat = [ type toFloat = [

View File

@ -1,13 +1,14 @@
module ExpressionValue = ReducerInterface_ExpressionValue module ExpressionValue = ReducerInterface_ExpressionValue
type expressionValue = ReducerInterface_ExpressionValue.expressionValue type expressionValue = ReducerInterface_ExpressionValue.expressionValue
let env: GenericDist_GenericOperation.env = { let runGenericOperation = GenericDist_GenericOperation.run(
~env={
sampleCount: 1000, sampleCount: 1000,
xyPointLength: 1000, xyPointLength: 1000,
} },
)
let runGenericOperation = GenericDist_GenericOperation.run(~env)
module Helpers = {
let arithmeticMap = r => let arithmeticMap = r =>
switch r { switch r {
| "add" => #Add | "add" => #Add
@ -15,13 +16,13 @@ let arithmeticMap = r =>
| "subtract" => #Subtract | "subtract" => #Subtract
| "dotSubtract" => #Subtract | "dotSubtract" => #Subtract
| "divide" => #Divide | "divide" => #Divide
| "logarithm" => #Divide | "logarithm" => #Logarithm
| "dotDivide" => #Divide | "dotDivide" => #Divide
| "exponentiate" => #Exponentiate | "exponentiate" => #Exponentiate
| "dotExponentiate" => #Exponentiate | "dotExponentiate" => #Exponentiate
| "multiply" => #Multiply | "multiply" => #Multiply
| "dotMultiply" => #Multiply | "dotMultiply" => #Multiply
| "dotLogarithm" => #Divide | "dotLogarithm" => #Logarithm
| _ => #Multiply | _ => #Multiply
} }
@ -58,23 +59,9 @@ let twoDiststoDistFn = (direction, arithmetic, dist1, dist2) => {
dist1, dist1,
)->runGenericOperation )->runGenericOperation
} }
let genericOutputToReducerValue = (o: GenericDist_GenericOperation.outputType): result<
expressionValue,
Reducer_ErrorValue.errorValue,
> =>
switch o {
| Dist(d) => Ok(ReducerInterface_ExpressionValue.EvDistribution(d))
| Float(d) => Ok(EvNumber(d))
| String(d) => Ok(EvString(d))
| GenDistError(NotYetImplemented) => Error(RETodo("Function not yet implemented"))
| GenDistError(Unreachable) => Error(RETodo("Unreachable"))
| GenDistError(DistributionVerticalShiftIsInvalid) =>
Error(RETodo("Distribution Vertical Shift is Invalid"))
| GenDistError(Other(s)) => Error(RETodo(s))
} }
module SymbolicConstructor = { module SymbolicConstructors = {
let oneFloat = name => let oneFloat = name =>
switch name { switch name {
| "exponential" => Ok(SymbolicDist.Exponential.make) | "exponential" => Ok(SymbolicDist.Exponential.make)
@ -87,6 +74,7 @@ module SymbolicConstructor = {
| "uniform" => Ok(SymbolicDist.Uniform.make) | "uniform" => Ok(SymbolicDist.Uniform.make)
| "beta" => Ok(SymbolicDist.Beta.make) | "beta" => Ok(SymbolicDist.Beta.make)
| "lognormal" => Ok(SymbolicDist.Lognormal.make) | "lognormal" => Ok(SymbolicDist.Lognormal.make)
| "to" => Ok(SymbolicDist.From90thPercentile.make)
| _ => Error("impossible path") | _ => Error("impossible path")
} }
@ -111,47 +99,65 @@ let dispatchToGenericOutput = (call: ExpressionValue.functionCall): option<
let (fnName, args) = call let (fnName, args) = call
switch (fnName, args) { switch (fnName, args) {
| ("exponential" as fnName, [EvNumber(f1)]) => | ("exponential" as fnName, [EvNumber(f1)]) =>
SymbolicConstructor.oneFloat(fnName) SymbolicConstructors.oneFloat(fnName)
->E.R.bind(r => r(f1)) ->E.R.bind(r => r(f1))
->SymbolicConstructor.symbolicResultToOutput ->SymbolicConstructors.symbolicResultToOutput
| (("normal" | "uniform" | "beta" | "lognormal") as fnName, [EvNumber(f1), EvNumber(f2)]) => | (
SymbolicConstructor.twoFloat(fnName) ("normal" | "uniform" | "beta" | "lognormal" | "to") as fnName,
[EvNumber(f1), EvNumber(f2)],
) =>
SymbolicConstructors.twoFloat(fnName)
->E.R.bind(r => r(f1, f2)) ->E.R.bind(r => r(f1, f2))
->SymbolicConstructor.symbolicResultToOutput ->SymbolicConstructors.symbolicResultToOutput
| ("triangular" as fnName, [EvNumber(f1), EvNumber(f2), EvNumber(f3)]) => | ("triangular" as fnName, [EvNumber(f1), EvNumber(f2), EvNumber(f3)]) =>
SymbolicConstructor.threeFloat(fnName) SymbolicConstructors.threeFloat(fnName)
->E.R.bind(r => r(f1, f2, f3)) ->E.R.bind(r => r(f1, f2, f3))
->SymbolicConstructor.symbolicResultToOutput ->SymbolicConstructors.symbolicResultToOutput
| ("sample", [EvDistribution(dist)]) => toFloatFn(#Sample, dist) | ("sample", [EvDistribution(dist)]) => Helpers.toFloatFn(#Sample, dist)
| ("mean", [EvDistribution(dist)]) => toFloatFn(#Mean, dist) | ("mean", [EvDistribution(dist)]) => Helpers.toFloatFn(#Mean, dist)
| ("normalize", [EvDistribution(dist)]) => toDistFn(Normalize, dist) | ("normalize", [EvDistribution(dist)]) => Helpers.toDistFn(Normalize, dist)
| ("toPointSet", [EvDistribution(dist)]) => toDistFn(ToPointSet, dist) | ("toPointSet", [EvDistribution(dist)]) => Helpers.toDistFn(ToPointSet, dist)
| ("cdf", [EvDistribution(dist), EvNumber(float)]) => toFloatFn(#Cdf(float), dist) | ("cdf", [EvDistribution(dist), EvNumber(float)]) => Helpers.toFloatFn(#Cdf(float), dist)
| ("pdf", [EvDistribution(dist), EvNumber(float)]) => toFloatFn(#Pdf(float), dist) | ("pdf", [EvDistribution(dist), EvNumber(float)]) => Helpers.toFloatFn(#Pdf(float), dist)
| ("inv", [EvDistribution(dist), EvNumber(float)]) => toFloatFn(#Inv(float), dist) | ("inv", [EvDistribution(dist), EvNumber(float)]) => Helpers.toFloatFn(#Inv(float), dist)
| ("toSampleSet", [EvDistribution(dist), EvNumber(float)]) => | ("toSampleSet", [EvDistribution(dist), EvNumber(float)]) =>
toDistFn(ToSampleSet(Belt.Int.fromFloat(float)), dist) Helpers.toDistFn(ToSampleSet(Belt.Int.fromFloat(float)), dist)
| ("truncateLeft", [EvDistribution(dist), EvNumber(float)]) => | ("truncateLeft", [EvDistribution(dist), EvNumber(float)]) =>
toDistFn(Truncate(Some(float), None), dist) Helpers.toDistFn(Truncate(Some(float), None), dist)
| ("truncateRight", [EvDistribution(dist), EvNumber(float)]) => | ("truncateRight", [EvDistribution(dist), EvNumber(float)]) =>
toDistFn(Truncate(None, Some(float)), dist) Helpers.toDistFn(Truncate(None, Some(float)), dist)
| ("truncate", [EvDistribution(dist), EvNumber(float1), EvNumber(float2)]) => | ("truncate", [EvDistribution(dist), EvNumber(float1), EvNumber(float2)]) =>
toDistFn(Truncate(Some(float1), Some(float2)), dist) Helpers.toDistFn(Truncate(Some(float1), Some(float2)), dist)
| (("add" | "multiply" | "subtract" | "divide" | "exponentiate") as arithmetic, [a, b] as args) => | (("add" | "multiply" | "subtract" | "divide" | "exponentiate" | "log") as arithmetic, [a, b] as args) =>
catchAndConvertTwoArgsToDists(args)->E.O2.fmap(((fst, snd)) => Helpers.catchAndConvertTwoArgsToDists(args)->E.O2.fmap(((fst, snd)) =>
twoDiststoDistFn(Algebraic, arithmetic, fst, snd) Helpers.twoDiststoDistFn(Algebraic, arithmetic, fst, snd)
) )
| ( | (
("dotAdd" | "dotMultiply" | "dotSubtract" | "dotDivide" | "dotExponentiate") as arithmetic, ("dotAdd" | "dotMultiply" | "dotSubtract" | "dotDivide" | "dotExponentiate" | "dotLogarithm") as arithmetic,
[a, b] as args, [a, b] as args,
) => ) =>
catchAndConvertTwoArgsToDists(args)->E.O2.fmap(((fst, snd)) => Helpers.catchAndConvertTwoArgsToDists(args)->E.O2.fmap(((fst, snd)) =>
twoDiststoDistFn(Pointwise, arithmetic, fst, snd) Helpers.twoDiststoDistFn(Pointwise, arithmetic, fst, snd)
) )
| _ => None | _ => None
} }
} }
let genericOutputToReducerValue = (o: GenericDist_GenericOperation.outputType): result<
expressionValue,
Reducer_ErrorValue.errorValue,
> =>
switch o {
| Dist(d) => Ok(ReducerInterface_ExpressionValue.EvDistribution(d))
| Float(d) => Ok(EvNumber(d))
| String(d) => Ok(EvString(d))
| GenDistError(NotYetImplemented) => Error(RETodo("Function not yet implemented"))
| GenDistError(Unreachable) => Error(RETodo("Unreachable"))
| GenDistError(DistributionVerticalShiftIsInvalid) =>
Error(RETodo("Distribution Vertical Shift is Invalid"))
| GenDistError(Other(s)) => Error(RETodo(s))
}
let dispatch = call => { let dispatch = call => {
dispatchToGenericOutput(call)->E.O2.fmap(genericOutputToReducerValue) dispatchToGenericOutput(call)->E.O2.fmap(genericOutputToReducerValue)
} }

View File

@ -229,6 +229,6 @@ let all = [
), ),
makeRenderedDistFloat("scaleExp", (dist, float) => verticalScaling(#Exponentiate, dist, float)), makeRenderedDistFloat("scaleExp", (dist, float) => verticalScaling(#Exponentiate, dist, float)),
makeRenderedDistFloat("scaleMultiply", (dist, float) => verticalScaling(#Multiply, dist, float)), makeRenderedDistFloat("scaleMultiply", (dist, float) => verticalScaling(#Multiply, dist, float)),
makeRenderedDistFloat("scaleLog", (dist, float) => verticalScaling(#Log, dist, float)), makeRenderedDistFloat("scaleLog", (dist, float) => verticalScaling(#Logarithm, dist, float)),
Multimodal._function, Multimodal._function,
] ]

View File

@ -115,7 +115,7 @@ let combineShapesContinuousContinuous = (
| #Multiply => (m1, m2) => m1 *. m2 | #Multiply => (m1, m2) => m1 *. m2
| #Divide => (m1, mInv2) => m1 *. mInv2 | #Divide => (m1, mInv2) => m1 *. mInv2
| #Exponentiate => (m1, mInv2) => m1 ** mInv2 | #Exponentiate => (m1, mInv2) => m1 ** mInv2
| #Log => (m1, m2) => log(m1) /. log(m2) | #Logarithm => (m1, m2) => log(m1) /. log(m2)
} // note: here, mInv2 = mean(1 / t2) ~= 1 / mean(t2) } // note: here, mInv2 = mean(1 / t2) ~= 1 / mean(t2)
// TODO: I don't know what the variances are for exponentatiation // TODO: I don't know what the variances are for exponentatiation
@ -233,7 +233,7 @@ let combineShapesContinuousDiscrete = (
} }
| #Multiply | #Multiply
| #Exponentiate | #Exponentiate
| #Log | #Logarithm
| #Divide => | #Divide =>
for j in 0 to t2n - 1 { for j in 0 to t2n - 1 {
// creates a new continuous shape for each one of the discrete points, and collects them in outXYShapes. // creates a new continuous shape for each one of the discrete points, and collects them in outXYShapes.

View File

@ -50,9 +50,11 @@ module Exponential = {
type t = exponential type t = exponential
let make = (rate: float): result<symbolicDist, string> => let make = (rate: float): result<symbolicDist, string> =>
rate > 0.0 rate > 0.0
? Ok(#Exponential({ ? Ok(
#Exponential({
rate: rate, rate: rate,
})) }),
)
: Error("Exponential distributions mean must be larger than 0") : Error("Exponential distributions mean must be larger than 0")
let pdf = (x, t: t) => Jstat.Exponential.pdf(x, t.rate) let pdf = (x, t: t) => Jstat.Exponential.pdf(x, t.rate)
let cdf = (x, t: t) => Jstat.Exponential.cdf(x, t.rate) let cdf = (x, t: t) => Jstat.Exponential.cdf(x, t.rate)
@ -127,8 +129,7 @@ module Lognormal = {
let mu = Js.Math.log(mean) -. 0.5 *. Js.Math.log(variance /. meanSquared +. 1.0) let mu = Js.Math.log(mean) -. 0.5 *. Js.Math.log(variance /. meanSquared +. 1.0)
let sigma = Js.Math.pow_float(~base=Js.Math.log(variance /. meanSquared +. 1.0), ~exp=0.5) let sigma = Js.Math.pow_float(~base=Js.Math.log(variance /. meanSquared +. 1.0), ~exp=0.5)
Ok(#Lognormal({mu: mu, sigma: sigma})) Ok(#Lognormal({mu: mu, sigma: sigma}))
} } else {
else {
Error("Lognormal standard deviation must be larger than 0") Error("Lognormal standard deviation must be larger than 0")
} }
} }
@ -154,9 +155,7 @@ module Lognormal = {
module Uniform = { module Uniform = {
type t = uniform type t = uniform
let make = (low, high) => let make = (low, high) =>
high > low high > low ? Ok(#Uniform({low: low, high: high})) : Error("High must be larger than low")
? Ok(#Uniform({low: low, high: high}))
: Error("High must be larger than low")
let pdf = (x, t: t) => Jstat.Uniform.pdf(x, t.low, t.high) let pdf = (x, t: t) => Jstat.Uniform.pdf(x, t.low, t.high)
let cdf = (x, t: t) => Jstat.Uniform.cdf(x, t.low, t.high) let cdf = (x, t: t) => Jstat.Uniform.cdf(x, t.low, t.high)
@ -183,6 +182,15 @@ module Float = {
let toString = Js.Float.toString let toString = Js.Float.toString
} }
module From90thPercentile = {
let make = (low, high) =>
switch (low, high) {
| (low, high) if low <= 0.0 && low < high => Ok(Normal.from90PercentCI(low, high))
| (low, high) if low < high => Ok(Lognormal.from90PercentCI(low, high))
| (_, _) => Error("Low value must be less than high value.")
}
}
module T = { module T = {
let minCdfValue = 0.0001 let minCdfValue = 0.0001
let maxCdfValue = 0.9999 let maxCdfValue = 0.9999

View File

@ -7,11 +7,11 @@ type algebraicOperation = [
| #Subtract | #Subtract
| #Divide | #Divide
| #Exponentiate | #Exponentiate
| #Log | #Logarithm
] ]
@genType @genType
type pointwiseOperation = [#Add | #Multiply | #Exponentiate] type pointwiseOperation = [#Add | #Multiply | #Exponentiate]
type scaleOperation = [#Multiply | #Exponentiate | #Log | #Divide] type scaleOperation = [#Multiply | #Exponentiate | #Logarithm | #Divide]
type distToFloatOperation = [ type distToFloatOperation = [
| #Pdf(float) | #Pdf(float)
| #Cdf(float) | #Cdf(float)
@ -29,7 +29,7 @@ module Algebraic = {
| #Multiply => \"*." | #Multiply => \"*."
| #Exponentiate => \"**" | #Exponentiate => \"**"
| #Divide => \"/." | #Divide => \"/."
| #Log => (a, b) => log(a) /. log(b) | #Logarithm => (a, b) => log(a) /. log(b)
} }
let applyFn = (t, f1, f2) => let applyFn = (t, f1, f2) =>
@ -45,7 +45,7 @@ module Algebraic = {
| #Multiply => "*" | #Multiply => "*"
| #Exponentiate => "**" | #Exponentiate => "**"
| #Divide => "/" | #Divide => "/"
| #Log => "log" | #Logarithm => "log"
} }
let format = (a, b, c) => b ++ (" " ++ (toString(a) ++ (" " ++ c))) let format = (a, b, c) => b ++ (" " ++ (toString(a) ++ (" " ++ c)))
@ -84,7 +84,7 @@ module Scale = {
| #Multiply => \"*." | #Multiply => \"*."
| #Divide => \"/." | #Divide => \"/."
| #Exponentiate => \"**" | #Exponentiate => \"**"
| #Log => (a, b) => log(a) /. log(b) | #Logarithm => (a, b) => log(a) /. log(b)
} }
let format = (operation: t, value, scaleBy) => let format = (operation: t, value, scaleBy) =>
@ -92,7 +92,7 @@ module Scale = {
| #Multiply => j`verticalMultiply($value, $scaleBy) ` | #Multiply => j`verticalMultiply($value, $scaleBy) `
| #Divide => j`verticalDivide($value, $scaleBy) ` | #Divide => j`verticalDivide($value, $scaleBy) `
| #Exponentiate => j`verticalExponentiate($value, $scaleBy) ` | #Exponentiate => j`verticalExponentiate($value, $scaleBy) `
| #Log => j`verticalLog($value, $scaleBy) ` | #Logarithm => j`verticalLog($value, $scaleBy) `
} }
let toIntegralSumCacheFn = x => let toIntegralSumCacheFn = x =>
@ -100,7 +100,7 @@ module Scale = {
| #Multiply => (a, b) => Some(a *. b) | #Multiply => (a, b) => Some(a *. b)
| #Divide => (a, b) => Some(a /. b) | #Divide => (a, b) => Some(a /. b)
| #Exponentiate => (_, _) => None | #Exponentiate => (_, _) => None
| #Log => (_, _) => None | #Logarithm => (_, _) => None
} }
let toIntegralCacheFn = x => let toIntegralCacheFn = x =>
@ -108,7 +108,7 @@ module Scale = {
| #Multiply => (_, _) => None // TODO: this could probably just be multiplied out (using Continuous.scaleBy) | #Multiply => (_, _) => None // TODO: this could probably just be multiplied out (using Continuous.scaleBy)
| #Divide => (_, _) => None | #Divide => (_, _) => None
| #Exponentiate => (_, _) => None | #Exponentiate => (_, _) => None
| #Log => (_, _) => None | #Logarithm => (_, _) => None
} }
} }