Merge pull request #147 from QURIresearch/issue-100
Combining GenericDistribution library with Reducer library
This commit is contained in:
commit
cbd4ad3a79
|
@ -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))")
|
||||
})
|
||||
})
|
|
@ -41,7 +41,7 @@
|
|||
},
|
||||
"refmt": 3,
|
||||
"warnings": {
|
||||
"number": "+A-42-48-9-30-4-102"
|
||||
"number": "+A-42-48-9-30-4-102-20-27-41"
|
||||
},
|
||||
"ppx-flags": []
|
||||
}
|
||||
|
|
|
@ -228,7 +228,7 @@ let pointwiseCombinationFloat = (
|
|||
): result<t, error> => {
|
||||
let m = switch arithmeticOperation {
|
||||
| #Add | #Subtract => Error(GenericDist_Types.DistributionVerticalShiftIsInvalid)
|
||||
| (#Multiply | #Divide | #Exponentiate | #Log) as arithmeticOperation =>
|
||||
| (#Multiply | #Divide | #Exponentiate | #Logarithm) as arithmeticOperation =>
|
||||
toPointSetFn(t)->E.R2.fmap(t => {
|
||||
//TODO: Move to PointSet codebase
|
||||
let fn = (secondary, main) => Operation.Scale.toFn(arithmeticOperation, main, secondary)
|
||||
|
|
|
@ -48,12 +48,24 @@ module OutputLocal = {
|
|||
| _ => None
|
||||
}
|
||||
|
||||
let toFloatR = (t: t): result<float, error> =>
|
||||
switch t {
|
||||
| Float(r) => Ok(r)
|
||||
| e => Error(toErrorOrUnreachable(e))
|
||||
}
|
||||
|
||||
let toString = (t: t) =>
|
||||
switch t {
|
||||
| String(d) => Some(d)
|
||||
| _ => 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.
|
||||
let fromResult = (r: result<t, error>): outputType =>
|
||||
switch r {
|
||||
|
|
|
@ -26,7 +26,9 @@ module Output: {
|
|||
let toDist: t => option<GenericDist_Types.genericDist>
|
||||
let toDistR: t => result<GenericDist_Types.genericDist, GenericDist_Types.error>
|
||||
let toFloat: t => option<float>
|
||||
let toFloatR: t => result<float, GenericDist_Types.error>
|
||||
let toString: t => option<string>
|
||||
let toStringR: t => result<string, GenericDist_Types.error>
|
||||
let toError: t => option<GenericDist_Types.error>
|
||||
let fmap: (~env: env, t, GenericDist_Types.Operation.singleParamaterFunction) => t
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ module Operation = {
|
|||
| #Subtract
|
||||
| #Divide
|
||||
| #Exponentiate
|
||||
| #Log
|
||||
| #Logarithm
|
||||
]
|
||||
|
||||
let arithmeticToFn = (arithmetic: arithmeticOperation) =>
|
||||
|
@ -30,7 +30,7 @@ module Operation = {
|
|||
| #Subtract => \"-."
|
||||
| #Exponentiate => \"**"
|
||||
| #Divide => \"/."
|
||||
| #Log => (a, b) => log(a) /. log(b)
|
||||
| #Logarithm => (a, b) => log(a) /. log(b)
|
||||
}
|
||||
|
||||
type toFloat = [
|
||||
|
|
|
@ -12,6 +12,7 @@ type rec expressionValue =
|
|||
| EvSymbol(string)
|
||||
| EvArray(array<expressionValue>)
|
||||
| EvRecord(Js.Dict.t<expressionValue>)
|
||||
| EvDistribution(GenericDist_Types.genericDist)
|
||||
|
||||
type functionCall = (string, array<expressionValue>)
|
||||
|
||||
|
@ -35,6 +36,7 @@ let rec toString = aValue =>
|
|||
->Js.String.concatMany("")
|
||||
`{${pairs}}`
|
||||
}
|
||||
| EvDistribution(dist) => `${GenericDist.toString(dist)}`
|
||||
}
|
||||
|
||||
let toStringWithType = aValue =>
|
||||
|
@ -45,6 +47,7 @@ let toStringWithType = aValue =>
|
|||
| EvSymbol(_) => `Symbol::${toString(aValue)}`
|
||||
| EvArray(_) => `Array::${toString(aValue)}`
|
||||
| EvRecord(_) => `Record::${toString(aValue)}`
|
||||
| EvDistribution(_) => `Distribution::${toString(aValue)}`
|
||||
}
|
||||
|
||||
let argsToString = (args: array<expressionValue>): string => {
|
||||
|
|
|
@ -13,13 +13,10 @@ module Sample = {
|
|||
/*
|
||||
Map external calls of Reducer
|
||||
*/
|
||||
|
||||
let dispatch = (call: ExpressionValue.functionCall, chain): result<expressionValue, 'e> =>
|
||||
switch call {
|
||||
| ("add", [EvNumber(a), EvNumber(b)]) => Sample.customAdd(a, b)->EvNumber->Ok
|
||||
|
||||
| call => chain(call)
|
||||
|
||||
/*
|
||||
ReducerInterface_GenericDistribution.dispatch(call) |> E.O.default(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.
|
||||
|
||||
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 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.
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
let dispatch: ReducerInterface_ExpressionValue.functionCall => option<
|
||||
result<ReducerInterface_ExpressionValue.expressionValue, Reducer_ErrorValue.errorValue>,
|
||||
>
|
|
@ -229,6 +229,6 @@ let all = [
|
|||
),
|
||||
makeRenderedDistFloat("scaleExp", (dist, float) => verticalScaling(#Exponentiate, 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,
|
||||
]
|
||||
|
|
|
@ -115,7 +115,7 @@ let combineShapesContinuousContinuous = (
|
|||
| #Multiply => (m1, m2) => m1 *. m2
|
||||
| #Divide => (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)
|
||||
|
||||
// TODO: I don't know what the variances are for exponentatiation
|
||||
|
@ -233,7 +233,7 @@ let combineShapesContinuousDiscrete = (
|
|||
}
|
||||
| #Multiply
|
||||
| #Exponentiate
|
||||
| #Log
|
||||
| #Logarithm
|
||||
| #Divide =>
|
||||
for j in 0 to t2n - 1 {
|
||||
// creates a new continuous shape for each one of the discrete points, and collects them in outXYShapes.
|
||||
|
|
|
@ -2,7 +2,7 @@ open SymbolicDistTypes
|
|||
|
||||
module 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
|
||||
? Ok(#Normal({mean: mean, stdev: stdev}))
|
||||
: Error("Standard deviation of normal distribution must be larger than 0")
|
||||
|
@ -48,11 +48,13 @@ module Normal = {
|
|||
|
||||
module Exponential = {
|
||||
type t = exponential
|
||||
let make = (rate: float): result<symbolicDist,string> =>
|
||||
let make = (rate: float): result<symbolicDist, string> =>
|
||||
rate > 0.0
|
||||
? Ok(#Exponential({
|
||||
rate: rate,
|
||||
}))
|
||||
? Ok(
|
||||
#Exponential({
|
||||
rate: rate,
|
||||
}),
|
||||
)
|
||||
: Error("Exponential distributions mean must be larger than 0")
|
||||
let pdf = (x, t: t) => Jstat.Exponential.pdf(x, t.rate)
|
||||
let cdf = (x, t: t) => Jstat.Exponential.cdf(x, t.rate)
|
||||
|
@ -89,7 +91,7 @@ module Triangular = {
|
|||
|
||||
module Beta = {
|
||||
type t = beta
|
||||
let make = (alpha, beta) =>
|
||||
let make = (alpha, beta) =>
|
||||
alpha > 0.0 && beta > 0.0
|
||||
? Ok(#Beta({alpha: alpha, beta: beta}))
|
||||
: Error("Beta distribution parameters must be positive")
|
||||
|
@ -103,10 +105,10 @@ module Beta = {
|
|||
|
||||
module Lognormal = {
|
||||
type t = lognormal
|
||||
let make = (mu, sigma) =>
|
||||
sigma > 0.0
|
||||
? Ok(#Lognormal({mu: mu, sigma: sigma}))
|
||||
: Error("Lognormal standard deviation must be larger than 0")
|
||||
let make = (mu, sigma) =>
|
||||
sigma > 0.0
|
||||
? Ok(#Lognormal({mu: mu, sigma: sigma}))
|
||||
: Error("Lognormal standard deviation must be larger than 0")
|
||||
let pdf = (x, t: t) => Jstat.Lognormal.pdf(x, t.mu, t.sigma)
|
||||
let cdf = (x, t: t) => Jstat.Lognormal.cdf(x, t.mu, t.sigma)
|
||||
let inv = (p, t: t) => Jstat.Lognormal.inv(p, t.mu, t.sigma)
|
||||
|
@ -127,8 +129,7 @@ module Lognormal = {
|
|||
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)
|
||||
Ok(#Lognormal({mu: mu, sigma: sigma}))
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
Error("Lognormal standard deviation must be larger than 0")
|
||||
}
|
||||
}
|
||||
|
@ -154,9 +155,7 @@ module Lognormal = {
|
|||
module Uniform = {
|
||||
type t = uniform
|
||||
let make = (low, high) =>
|
||||
high > low
|
||||
? Ok(#Uniform({low: low, high: high}))
|
||||
: Error("High must be larger than low")
|
||||
high > 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 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 toString = ({low, high}: t) => j`Uniform($low,$high)`
|
||||
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 newHigh = min(E.O.default(infinity, high), t.high)
|
||||
{low: newLow, high: newHigh}
|
||||
|
@ -183,6 +182,15 @@ module Float = {
|
|||
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 = {
|
||||
let minCdfValue = 0.0001
|
||||
let maxCdfValue = 0.9999
|
||||
|
|
|
@ -100,6 +100,7 @@ module O = {
|
|||
module O2 = {
|
||||
let default = (a, b) => O.default(b, a)
|
||||
let toExn = (a, b) => O.toExn(b, a)
|
||||
let fmap = (a, b) => O.fmap(b, a)
|
||||
}
|
||||
|
||||
/* Functions */
|
||||
|
|
|
@ -7,11 +7,11 @@ type algebraicOperation = [
|
|||
| #Subtract
|
||||
| #Divide
|
||||
| #Exponentiate
|
||||
| #Log
|
||||
| #Logarithm
|
||||
]
|
||||
@genType
|
||||
type pointwiseOperation = [#Add | #Multiply | #Exponentiate]
|
||||
type scaleOperation = [#Multiply | #Exponentiate | #Log | #Divide]
|
||||
type scaleOperation = [#Multiply | #Exponentiate | #Logarithm | #Divide]
|
||||
type distToFloatOperation = [
|
||||
| #Pdf(float)
|
||||
| #Cdf(float)
|
||||
|
@ -29,7 +29,7 @@ module Algebraic = {
|
|||
| #Multiply => \"*."
|
||||
| #Exponentiate => \"**"
|
||||
| #Divide => \"/."
|
||||
| #Log => (a, b) => log(a) /. log(b)
|
||||
| #Logarithm => (a, b) => log(a) /. log(b)
|
||||
}
|
||||
|
||||
let applyFn = (t, f1, f2) =>
|
||||
|
@ -45,7 +45,7 @@ module Algebraic = {
|
|||
| #Multiply => "*"
|
||||
| #Exponentiate => "**"
|
||||
| #Divide => "/"
|
||||
| #Log => "log"
|
||||
| #Logarithm => "log"
|
||||
}
|
||||
|
||||
let format = (a, b, c) => b ++ (" " ++ (toString(a) ++ (" " ++ c)))
|
||||
|
@ -84,7 +84,7 @@ module Scale = {
|
|||
| #Multiply => \"*."
|
||||
| #Divide => \"/."
|
||||
| #Exponentiate => \"**"
|
||||
| #Log => (a, b) => log(a) /. log(b)
|
||||
| #Logarithm => (a, b) => log(a) /. log(b)
|
||||
}
|
||||
|
||||
let format = (operation: t, value, scaleBy) =>
|
||||
|
@ -92,7 +92,7 @@ module Scale = {
|
|||
| #Multiply => j`verticalMultiply($value, $scaleBy) `
|
||||
| #Divide => j`verticalDivide($value, $scaleBy) `
|
||||
| #Exponentiate => j`verticalExponentiate($value, $scaleBy) `
|
||||
| #Log => j`verticalLog($value, $scaleBy) `
|
||||
| #Logarithm => j`verticalLog($value, $scaleBy) `
|
||||
}
|
||||
|
||||
let toIntegralSumCacheFn = x =>
|
||||
|
@ -100,7 +100,7 @@ module Scale = {
|
|||
| #Multiply => (a, b) => Some(a *. b)
|
||||
| #Divide => (a, b) => Some(a /. b)
|
||||
| #Exponentiate => (_, _) => None
|
||||
| #Log => (_, _) => None
|
||||
| #Logarithm => (_, _) => None
|
||||
}
|
||||
|
||||
let toIntegralCacheFn = x =>
|
||||
|
@ -108,7 +108,7 @@ module Scale = {
|
|||
| #Multiply => (_, _) => None // TODO: this could probably just be multiplied out (using Continuous.scaleBy)
|
||||
| #Divide => (_, _) => None
|
||||
| #Exponentiate => (_, _) => None
|
||||
| #Log => (_, _) => None
|
||||
| #Logarithm => (_, _) => None
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user