Merge pull request #147 from QURIresearch/issue-100

Combining GenericDistribution library with Reducer library
This commit is contained in:
Ozzie Gooen 2022-04-04 11:18:18 -04:00 committed by GitHub
commit cbd4ad3a79
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 373 additions and 38 deletions

View File

@ -0,0 +1,125 @@
open Jest
let testSkip: (bool, string, unit => assertion) => unit = (skip: bool) =>
if skip {
Skip.test
} else {
test
}
let testEval = (~skip=false, str, result) =>
testSkip(skip)(str, () => Reducer_TestHelpers.expectEvalToBe(str, result))
let testParse = (~skip=false, str, result) =>
testSkip(skip)(str, () => Reducer_TestHelpers.expectParseToBe(str, result))
describe("eval on distribution functions", () => {
describe("normal distribution", () => {
testEval("normal(5,2)", "Ok(Normal(5,2))")
})
describe("lognormal distribution", () => {
testEval("lognormal(5,2)", "Ok(Lognormal(5,2))")
})
describe("unaryMinus", () => {
testEval("mean(-normal(5,2))", "Ok(-5.002887370380851)")
})
describe("to", () => {
testEval("5 to 2", "Error(TODO: Low value must be less than high value.)")
testEval("to(2,5)", "Ok(Lognormal(1.1512925464970227,0.278507821238345))")
testEval("to(-2,2)", "Ok(Normal(0,1.215913388057542))")
})
describe("mean", () => {
testEval("mean(normal(5,2))", "Ok(5)")
testEval("mean(lognormal(1,2))", "Ok(20.085536923187668)")
})
describe("normalize", () => {
testEval("normalize(normal(5,2))", "Ok(Normal(5,2))")
})
describe("toPointSet", () => {
testEval("toPointSet(normal(5,2))", "Ok(Point Set Distribution)")
})
describe("toSampleSet", () => {
testEval("toSampleSet(normal(5,2), 100)", "Ok(Sample Set Distribution)")
})
describe("add", () => {
testEval("add(normal(5,2), normal(10,2))", "Ok(Normal(15,2.8284271247461903))")
testEval("add(normal(5,2), lognormal(10,2))", "Ok(Sample Set Distribution)")
testEval("add(normal(5,2), 3)", "Ok(Point Set Distribution)")
testEval("add(3, normal(5,2))", "Ok(Point Set Distribution)")
testEval("3+normal(5,2)", "Ok(Point Set Distribution)")
testEval("normal(5,2)+3", "Ok(Point Set Distribution)")
})
describe("truncate", () => {
testEval("truncateLeft(normal(5,2), 3)", "Ok(Point Set Distribution)")
testEval("truncateRight(normal(5,2), 3)", "Ok(Point Set Distribution)")
testEval("truncate(normal(5,2), 3, 8)", "Ok(Point Set Distribution)")
})
describe("exp", () => {
testEval("exp(normal(5,2))", "Ok(Point Set Distribution)")
})
describe("pow", () => {
testEval("pow(3, uniform(5,8))", "Ok(Point Set Distribution)")
testEval("pow(uniform(5,8), 3)", "Ok(Point Set Distribution)")
testEval("pow(uniform(5,8), uniform(9, 10))", "Ok(Sample Set Distribution)")
})
describe("log", () => {
testEval("log(2, uniform(5,8))", "Ok(Point Set Distribution)")
testEval("log(normal(5,2), 3)", "Ok(Point Set Distribution)")
testEval("log(normal(5,2), normal(10,1))", "Ok(Sample Set Distribution)")
testEval("log(uniform(5,8))", "Ok(Point Set Distribution)")
testEval("log10(uniform(5,8))", "Ok(Point 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", () => {
testEval("dotAdd(normal(5,2), lognormal(10,2))", "Ok(Point Set Distribution)")
testEval("dotAdd(normal(5,2), 3)", "Ok(Point Set Distribution)")
})
describe("equality", () => {
testEval(~skip=true, "normal(5,2) == normal(5,2)", "Ok(true)")
})
describe("mixture", () => {
testEval(
~skip=true,
"mx(normal(5,2), normal(10,1), normal(15, 1))",
"Ok(Point Set Distribution)",
)
testEval(
~skip=true,
"mixture(normal(5,2), normal(10,1), [.2,, .4])",
"Ok(Point Set Distribution)",
)
})
})
describe("parse on distribution functions", () => {
describe("power", () => {
testParse("normal(5,2) ^ normal(5,1)", "Ok((:pow (:normal 5 2) (:normal 5 1)))")
testParse("3 ^ normal(5,1)", "Ok((:pow 3 (:normal 5 1)))")
testParse("normal(5,2) ^ 3", "Ok((:pow (:normal 5 2) 3))")
})
describe("pointwise arithmetic expressions", () => {
testParse(~skip=true, "normal(5,2) .+ normal(5,1)", "Ok((:dotAdd (:normal 5 2) (:normal 5 1)))")
testParse(~skip=true, "normal(5,2) .- normal(5,1)", "Ok((:dotSubtract (:normal 5 2) (:normal 5 1)))")
testParse("normal(5,2) .* normal(5,1)", "Ok((:dotMultiply (:normal 5 2) (:normal 5 1)))")
testParse("normal(5,2) ./ normal(5,1)", "Ok((:dotDivide (:normal 5 2) (:normal 5 1)))")
testParse("normal(5,2) .^ normal(5,1)", "Ok((:dotPow (:normal 5 2) (:normal 5 1)))")
})
describe("equality", () => {
testParse("5 == normal(5,2)", "Ok((:equal 5 (:normal 5 2)))")
})
describe("pointwise adding two normals", () => {
testParse(~skip=true, "normal(5,2) .+ normal(5,1)", "Ok((:dotAdd (:normal 5 2) (:normal 5 1)))")
})
describe("exponential of one distribution", () => {
testParse(~skip=true, "exp(normal(5,2)", "Ok((:pow (:normal 5 2) 3))")
})
})

View File

@ -41,7 +41,7 @@
}, },
"refmt": 3, "refmt": 3,
"warnings": { "warnings": {
"number": "+A-42-48-9-30-4-102" "number": "+A-42-48-9-30-4-102-20-27-41"
}, },
"ppx-flags": [] "ppx-flags": []
} }

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

@ -48,12 +48,24 @@ module OutputLocal = {
| _ => None | _ => None
} }
let toFloatR = (t: t): result<float, error> =>
switch t {
| Float(r) => Ok(r)
| e => Error(toErrorOrUnreachable(e))
}
let toString = (t: t) => let toString = (t: t) =>
switch t { switch t {
| String(d) => Some(d) | String(d) => Some(d)
| _ => None | _ => None
} }
let toStringR = (t: t): result<string, error> =>
switch t {
| String(r) => Ok(r)
| e => Error(toErrorOrUnreachable(e))
}
//This is used to catch errors in other switch statements. //This is used to catch errors in other switch statements.
let fromResult = (r: result<t, error>): outputType => let fromResult = (r: result<t, error>): outputType =>
switch r { switch r {

View File

@ -26,7 +26,9 @@ module Output: {
let toDist: t => option<GenericDist_Types.genericDist> let toDist: t => option<GenericDist_Types.genericDist>
let toDistR: t => result<GenericDist_Types.genericDist, GenericDist_Types.error> let toDistR: t => result<GenericDist_Types.genericDist, GenericDist_Types.error>
let toFloat: t => option<float> let toFloat: t => option<float>
let toFloatR: t => result<float, GenericDist_Types.error>
let toString: t => option<string> let toString: t => option<string>
let toStringR: t => result<string, GenericDist_Types.error>
let toError: t => option<GenericDist_Types.error> let toError: t => option<GenericDist_Types.error>
let fmap: (~env: env, t, GenericDist_Types.Operation.singleParamaterFunction) => t let fmap: (~env: env, t, GenericDist_Types.Operation.singleParamaterFunction) => t
} }

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

@ -12,6 +12,7 @@ type rec expressionValue =
| EvSymbol(string) | EvSymbol(string)
| EvArray(array<expressionValue>) | EvArray(array<expressionValue>)
| EvRecord(Js.Dict.t<expressionValue>) | EvRecord(Js.Dict.t<expressionValue>)
| EvDistribution(GenericDist_Types.genericDist)
type functionCall = (string, array<expressionValue>) type functionCall = (string, array<expressionValue>)
@ -35,6 +36,7 @@ let rec toString = aValue =>
->Js.String.concatMany("") ->Js.String.concatMany("")
`{${pairs}}` `{${pairs}}`
} }
| EvDistribution(dist) => `${GenericDist.toString(dist)}`
} }
let toStringWithType = aValue => let toStringWithType = aValue =>
@ -45,6 +47,7 @@ let toStringWithType = aValue =>
| EvSymbol(_) => `Symbol::${toString(aValue)}` | EvSymbol(_) => `Symbol::${toString(aValue)}`
| EvArray(_) => `Array::${toString(aValue)}` | EvArray(_) => `Array::${toString(aValue)}`
| EvRecord(_) => `Record::${toString(aValue)}` | EvRecord(_) => `Record::${toString(aValue)}`
| EvDistribution(_) => `Distribution::${toString(aValue)}`
} }
let argsToString = (args: array<expressionValue>): string => { let argsToString = (args: array<expressionValue>): string => {

View File

@ -13,13 +13,10 @@ module Sample = {
/* /*
Map external calls of Reducer Map external calls of Reducer
*/ */
let dispatch = (call: ExpressionValue.functionCall, chain): result<expressionValue, 'e> => let dispatch = (call: ExpressionValue.functionCall, chain): result<expressionValue, 'e> =>
switch call { ReducerInterface_GenericDistribution.dispatch(call) |> E.O.default(chain(call))
| ("add", [EvNumber(a), EvNumber(b)]) => Sample.customAdd(a, b)->EvNumber->Ok /*
| call => chain(call)
/*
If your dispatch is too big you can divide it into smaller dispatches and pass the call so that it gets called finally. If your dispatch is too big you can divide it into smaller dispatches and pass the call so that it gets called finally.
The final chain(call) invokes the builtin default functions of the interpreter. The final chain(call) invokes the builtin default functions of the interpreter.
@ -35,4 +32,3 @@ Remember from the users point of view, there are no different modules:
// "doSth( constructorType2 )" // "doSth( constructorType2 )"
doSth gets dispatched to the correct module because of the type signature. You get function and operator abstraction for free. You don't need to combine different implementations into one type. That would be duplicating the repsonsibility of the dispatcher. doSth gets dispatched to the correct module because of the type signature. You get function and operator abstraction for free. You don't need to combine different implementations into one type. That would be duplicating the repsonsibility of the dispatcher.
*/ */
}

View File

@ -0,0 +1,185 @@
module ExpressionValue = ReducerInterface_ExpressionValue
type expressionValue = ReducerInterface_ExpressionValue.expressionValue
let runGenericOperation = GenericDist_GenericOperation.run(
~env={
sampleCount: 1000,
xyPointLength: 1000,
},
)
module Helpers = {
let arithmeticMap = r =>
switch r {
| "add" => #Add
| "dotAdd" => #Add
| "subtract" => #Subtract
| "dotSubtract" => #Subtract
| "divide" => #Divide
| "log" => #Logarithm
| "dotDivide" => #Divide
| "pow" => #Exponentiate
| "dotPow" => #Exponentiate
| "multiply" => #Multiply
| "dotMultiply" => #Multiply
| "dotLog" => #Logarithm
| _ => #Multiply
}
let catchAndConvertTwoArgsToDists = (args: array<expressionValue>): option<(
GenericDist_Types.genericDist,
GenericDist_Types.genericDist,
)> => {
switch args {
| [EvDistribution(a), EvDistribution(b)] => Some((a, b))
| [EvNumber(a), EvDistribution(b)] => Some((GenericDist.fromFloat(a), b))
| [EvDistribution(a), EvNumber(b)] => Some((a, GenericDist.fromFloat(b)))
| _ => None
}
}
let toFloatFn = (
fnCall: GenericDist_Types.Operation.toFloat,
dist: GenericDist_Types.genericDist,
) => {
FromDist(GenericDist_Types.Operation.ToFloat(fnCall), dist)->runGenericOperation->Some
}
let toDistFn = (fnCall: GenericDist_Types.Operation.toDist, dist) => {
FromDist(GenericDist_Types.Operation.ToDist(fnCall), dist)->runGenericOperation->Some
}
let twoDiststoDistFn = (direction, arithmetic, dist1, dist2) => {
FromDist(
GenericDist_Types.Operation.ToDistCombination(
direction,
arithmeticMap(arithmetic),
#Dist(dist2),
),
dist1,
)->runGenericOperation
}
}
module SymbolicConstructors = {
let oneFloat = name =>
switch name {
| "exponential" => Ok(SymbolicDist.Exponential.make)
| _ => Error("Unreachable state")
}
let twoFloat = name =>
switch name {
| "normal" => Ok(SymbolicDist.Normal.make)
| "uniform" => Ok(SymbolicDist.Uniform.make)
| "beta" => Ok(SymbolicDist.Beta.make)
| "lognormal" => Ok(SymbolicDist.Lognormal.make)
| "to" => Ok(SymbolicDist.From90thPercentile.make)
| _ => Error("Unreachable state")
}
let threeFloat = name =>
switch name {
| "triangular" => Ok(SymbolicDist.Triangular.make)
| _ => Error("Unreachable state")
}
let symbolicResultToOutput = (
symbolicResult: result<SymbolicDistTypes.symbolicDist, string>,
): option<GenericDist_GenericOperation.outputType> =>
switch symbolicResult {
| Ok(r) => Some(Dist(Symbolic(r)))
| Error(r) => Some(GenDistError(Other(r)))
}
}
module Math = {
let e = 2.718281828459
}
let dispatchToGenericOutput = (call: ExpressionValue.functionCall): option<
GenericDist_GenericOperation.outputType,
> => {
let (fnName, args) = call
switch (fnName, args) {
| ("exponential" as fnName, [EvNumber(f1)]) =>
SymbolicConstructors.oneFloat(fnName)
->E.R.bind(r => r(f1))
->SymbolicConstructors.symbolicResultToOutput
| (
("normal" | "uniform" | "beta" | "lognormal" | "to") as fnName,
[EvNumber(f1), EvNumber(f2)],
) =>
SymbolicConstructors.twoFloat(fnName)
->E.R.bind(r => r(f1, f2))
->SymbolicConstructors.symbolicResultToOutput
| ("triangular" as fnName, [EvNumber(f1), EvNumber(f2), EvNumber(f3)]) =>
SymbolicConstructors.threeFloat(fnName)
->E.R.bind(r => r(f1, f2, f3))
->SymbolicConstructors.symbolicResultToOutput
| ("sample", [EvDistribution(dist)]) => Helpers.toFloatFn(#Sample, dist)
| ("mean", [EvDistribution(dist)]) => Helpers.toFloatFn(#Mean, dist)
| ("exp", [EvDistribution(a)]) =>
// https://mathjs.org/docs/reference/functions/exp.html
Helpers.twoDiststoDistFn(Algebraic, "pow", GenericDist.fromFloat(Math.e), a)->Some
| ("normalize", [EvDistribution(dist)]) => Helpers.toDistFn(Normalize, dist)
| ("toPointSet", [EvDistribution(dist)]) => Helpers.toDistFn(ToPointSet, dist)
| ("cdf", [EvDistribution(dist), EvNumber(float)]) => Helpers.toFloatFn(#Cdf(float), dist)
| ("pdf", [EvDistribution(dist), EvNumber(float)]) => Helpers.toFloatFn(#Pdf(float), dist)
| ("inv", [EvDistribution(dist), EvNumber(float)]) => Helpers.toFloatFn(#Inv(float), dist)
| ("toSampleSet", [EvDistribution(dist), EvNumber(float)]) =>
Helpers.toDistFn(ToSampleSet(Belt.Int.fromFloat(float)), dist)
| ("truncateLeft", [EvDistribution(dist), EvNumber(float)]) =>
Helpers.toDistFn(Truncate(Some(float), None), dist)
| ("truncateRight", [EvDistribution(dist), EvNumber(float)]) =>
Helpers.toDistFn(Truncate(None, Some(float)), dist)
| ("truncate", [EvDistribution(dist), EvNumber(float1), EvNumber(float2)]) =>
Helpers.toDistFn(Truncate(Some(float1), Some(float2)), dist)
| ("log", [EvDistribution(a)]) =>
Helpers.twoDiststoDistFn(Algebraic, "log", a, GenericDist.fromFloat(Math.e))->Some
| ("log10", [EvDistribution(a)]) =>
Helpers.twoDiststoDistFn(Algebraic, "log", a, GenericDist.fromFloat(10.0))->Some
| ("unaryMinus", [EvDistribution(a)]) =>
Helpers.twoDiststoDistFn(Algebraic, "multiply", a, GenericDist.fromFloat(-1.0))->Some
| (("add" | "multiply" | "subtract" | "divide" | "pow" | "log") as arithmetic, [a, b] as args) =>
Helpers.catchAndConvertTwoArgsToDists(args)->E.O2.fmap(((fst, snd)) =>
Helpers.twoDiststoDistFn(Algebraic, arithmetic, fst, snd)
)
| (
("dotAdd"
| "dotMultiply"
| "dotSubtract"
| "dotDivide"
| "dotPow"
| "dotLog") as arithmetic,
[a, b] as args,
) =>
Helpers.catchAndConvertTwoArgsToDists(args)->E.O2.fmap(((fst, snd)) =>
Helpers.twoDiststoDistFn(Pointwise, arithmetic, fst, snd)
)
| ("dotLog", [EvDistribution(a)]) =>
Helpers.twoDiststoDistFn(Pointwise, "dotLog", a, GenericDist.fromFloat(Math.e))->Some
| ("dotExp", [EvDistribution(a)]) =>
Helpers.twoDiststoDistFn(Pointwise, "dotPow", GenericDist.fromFloat(Math.e), a)->Some
| _ => 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 => {
dispatchToGenericOutput(call)->E.O2.fmap(genericOutputToReducerValue)
}

View File

@ -0,0 +1,3 @@
let dispatch: ReducerInterface_ExpressionValue.functionCall => option<
result<ReducerInterface_ExpressionValue.expressionValue, Reducer_ErrorValue.errorValue>,
>

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

@ -2,7 +2,7 @@ open SymbolicDistTypes
module Normal = { module Normal = {
type t = normal type t = normal
let make = (mean: float, stdev: float): result<symbolicDist,string> => let make = (mean: float, stdev: float): result<symbolicDist, string> =>
stdev > 0.0 stdev > 0.0
? Ok(#Normal({mean: mean, stdev: stdev})) ? Ok(#Normal({mean: mean, stdev: stdev}))
: Error("Standard deviation of normal distribution must be larger than 0") : Error("Standard deviation of normal distribution must be larger than 0")
@ -48,11 +48,13 @@ module Normal = {
module Exponential = { 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)
@ -165,7 +164,7 @@ module Uniform = {
let mean = (t: t) => Ok(Jstat.Uniform.mean(t.low, t.high)) let mean = (t: t) => Ok(Jstat.Uniform.mean(t.low, t.high))
let toString = ({low, high}: t) => j`Uniform($low,$high)` let toString = ({low, high}: t) => j`Uniform($low,$high)`
let truncate = (low, high, t: t): t => { let truncate = (low, high, t: t): t => {
//todo: add check //todo: add check
let newLow = max(E.O.default(neg_infinity, low), t.low) let newLow = max(E.O.default(neg_infinity, low), t.low)
let newHigh = min(E.O.default(infinity, high), t.high) let newHigh = min(E.O.default(infinity, high), t.high)
{low: newLow, high: newHigh} {low: newLow, high: newHigh}
@ -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

@ -100,6 +100,7 @@ module O = {
module O2 = { module O2 = {
let default = (a, b) => O.default(b, a) let default = (a, b) => O.default(b, a)
let toExn = (a, b) => O.toExn(b, a) let toExn = (a, b) => O.toExn(b, a)
let fmap = (a, b) => O.fmap(b, a)
} }
/* Functions */ /* Functions */

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
} }
} }