2022-04-01 00:24:59 +00:00
|
|
|
module ExpressionValue = ReducerInterface_ExpressionValue
|
|
|
|
type expressionValue = ReducerInterface_ExpressionValue.expressionValue
|
|
|
|
|
2022-04-04 15:59:14 +00:00
|
|
|
let runGenericOperation = DistributionOperation.run(
|
2022-04-01 19:41:11 +00:00
|
|
|
~env={
|
|
|
|
sampleCount: 1000,
|
|
|
|
xyPointLength: 1000,
|
|
|
|
},
|
|
|
|
)
|
2022-04-01 00:24:59 +00:00
|
|
|
|
2022-04-01 19:41:11 +00:00
|
|
|
module Helpers = {
|
|
|
|
let arithmeticMap = r =>
|
|
|
|
switch r {
|
|
|
|
| "add" => #Add
|
2022-04-02 20:25:41 +00:00
|
|
|
| "dotAdd" => #Add
|
2022-04-01 19:41:11 +00:00
|
|
|
| "subtract" => #Subtract
|
2022-04-02 20:25:41 +00:00
|
|
|
| "dotSubtract" => #Subtract
|
2022-04-01 19:41:11 +00:00
|
|
|
| "divide" => #Divide
|
2022-04-02 20:25:41 +00:00
|
|
|
| "log" => #Logarithm
|
|
|
|
| "dotDivide" => #Divide
|
2022-04-09 16:37:26 +00:00
|
|
|
| "pow" => #Power
|
|
|
|
| "dotPow" => #Power
|
2022-04-01 19:41:11 +00:00
|
|
|
| "multiply" => #Multiply
|
2022-04-02 20:25:41 +00:00
|
|
|
| "dotMultiply" => #Multiply
|
|
|
|
| "dotLog" => #Logarithm
|
2022-04-01 19:41:11 +00:00
|
|
|
| _ => #Multiply
|
|
|
|
}
|
2022-04-01 00:24:59 +00:00
|
|
|
|
2022-04-01 19:41:11 +00:00
|
|
|
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
|
|
|
|
}
|
2022-04-01 00:24:59 +00:00
|
|
|
}
|
|
|
|
|
2022-04-01 19:41:11 +00:00
|
|
|
let toFloatFn = (
|
|
|
|
fnCall: GenericDist_Types.Operation.toFloat,
|
|
|
|
dist: GenericDist_Types.genericDist,
|
|
|
|
) => {
|
|
|
|
FromDist(GenericDist_Types.Operation.ToFloat(fnCall), dist)->runGenericOperation->Some
|
2022-04-01 00:24:59 +00:00
|
|
|
}
|
|
|
|
|
2022-04-08 13:08:49 +00:00
|
|
|
let toStringFn = (
|
|
|
|
fnCall: GenericDist_Types.Operation.toString,
|
|
|
|
dist: GenericDist_Types.genericDist,
|
|
|
|
) => {
|
|
|
|
FromDist(GenericDist_Types.Operation.ToString(fnCall), dist)->runGenericOperation->Some
|
|
|
|
}
|
|
|
|
|
2022-04-01 19:41:11 +00:00
|
|
|
let toDistFn = (fnCall: GenericDist_Types.Operation.toDist, dist) => {
|
|
|
|
FromDist(GenericDist_Types.Operation.ToDist(fnCall), dist)->runGenericOperation->Some
|
|
|
|
}
|
2022-04-01 00:24:59 +00:00
|
|
|
|
2022-04-01 19:41:11 +00:00
|
|
|
let twoDiststoDistFn = (direction, arithmetic, dist1, dist2) => {
|
|
|
|
FromDist(
|
|
|
|
GenericDist_Types.Operation.ToDistCombination(
|
|
|
|
direction,
|
|
|
|
arithmeticMap(arithmetic),
|
|
|
|
#Dist(dist2),
|
|
|
|
),
|
|
|
|
dist1,
|
|
|
|
)->runGenericOperation
|
2022-04-01 00:24:59 +00:00
|
|
|
}
|
2022-04-12 07:39:38 +00:00
|
|
|
let parseNumber = (args: expressionValue) : Belt.Result.t<float, string> =>
|
|
|
|
switch args {
|
|
|
|
| EvNumber(x) => Ok(x)
|
|
|
|
| _ => Error("Not a number")
|
|
|
|
}
|
|
|
|
|
|
|
|
let parseNumberArray = (ags: array<expressionValue>) : Belt.Result.t<array<float>, string> => E.A.fmap(parseNumber, ags) |> E.A.R.firstErrorOrOpen
|
|
|
|
|
|
|
|
let parseDist = (args: expressionValue): Belt.Result.t<GenericDist_Types.genericDist, string> =>
|
|
|
|
switch args {
|
|
|
|
| EvDistribution(x) => Ok(x)
|
|
|
|
| EvNumber(x) => Ok(GenericDist.fromFloat(x))
|
|
|
|
| _ => Error("Not a distribution")
|
|
|
|
}
|
|
|
|
|
|
|
|
let parseDistributionArray = (ags: array<expressionValue>) : Belt.Result.t<array<GenericDist_Types.genericDist>, string> => E.A.fmap(parseDist, ags) |> E.A.R.firstErrorOrOpen
|
|
|
|
|
|
|
|
let mixture = (args : array<expressionValue>): DistributionOperation.outputType => {
|
|
|
|
let givenWeights = E.A.last(args)
|
|
|
|
let calculatedWeights =
|
|
|
|
switch givenWeights {
|
|
|
|
| Some(EvArray(b)) => parseNumberArray(b)
|
|
|
|
| None =>
|
|
|
|
Ok(Belt.Array.make(E.A.length(args), 1.0 /. Belt.Int.toFloat(E.A.length(args))))
|
|
|
|
| _ => Error("Last argument of mx must be array")
|
|
|
|
}
|
|
|
|
switch (parseDistributionArray(Belt.Array.slice(args, ~offset=0, ~len=Belt.Array.length(args)-1)), calculatedWeights) {
|
|
|
|
| (Ok(distArray), Ok(w)) => Mixture(Belt.Array.zip(distArray, w)) -> runGenericOperation
|
|
|
|
| (Error(err), _) => GenDistError(ArgumentError(err))
|
|
|
|
| (_, Error(err)) => GenDistError(ArgumentError(err))
|
|
|
|
}
|
|
|
|
}
|
2022-04-01 19:41:11 +00:00
|
|
|
}
|
2022-04-01 00:24:59 +00:00
|
|
|
|
2022-04-01 19:41:11 +00:00
|
|
|
module SymbolicConstructors = {
|
2022-04-01 17:21:24 +00:00
|
|
|
let oneFloat = name =>
|
|
|
|
switch name {
|
|
|
|
| "exponential" => Ok(SymbolicDist.Exponential.make)
|
2022-04-02 15:01:53 +00:00
|
|
|
| _ => Error("Unreachable state")
|
2022-04-01 17:21:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
2022-04-01 19:41:11 +00:00
|
|
|
| "to" => Ok(SymbolicDist.From90thPercentile.make)
|
2022-04-02 15:01:53 +00:00
|
|
|
| _ => Error("Unreachable state")
|
2022-04-01 17:21:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let threeFloat = name =>
|
|
|
|
switch name {
|
|
|
|
| "triangular" => Ok(SymbolicDist.Triangular.make)
|
2022-04-02 15:01:53 +00:00
|
|
|
| _ => Error("Unreachable state")
|
2022-04-01 17:21:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let symbolicResultToOutput = (
|
|
|
|
symbolicResult: result<SymbolicDistTypes.symbolicDist, string>,
|
2022-04-04 15:59:14 +00:00
|
|
|
): option<DistributionOperation.outputType> =>
|
2022-04-01 17:21:24 +00:00
|
|
|
switch symbolicResult {
|
|
|
|
| Ok(r) => Some(Dist(Symbolic(r)))
|
|
|
|
| Error(r) => Some(GenDistError(Other(r)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-02 20:25:41 +00:00
|
|
|
module Math = {
|
|
|
|
let e = 2.718281828459
|
|
|
|
}
|
|
|
|
|
2022-04-01 00:24:59 +00:00
|
|
|
let dispatchToGenericOutput = (call: ExpressionValue.functionCall): option<
|
2022-04-04 15:59:14 +00:00
|
|
|
DistributionOperation.outputType,
|
2022-04-01 00:24:59 +00:00
|
|
|
> => {
|
|
|
|
let (fnName, args) = call
|
|
|
|
switch (fnName, args) {
|
2022-04-01 17:21:24 +00:00
|
|
|
| ("exponential" as fnName, [EvNumber(f1)]) =>
|
2022-04-01 19:41:11 +00:00
|
|
|
SymbolicConstructors.oneFloat(fnName)
|
2022-04-01 17:21:24 +00:00
|
|
|
->E.R.bind(r => r(f1))
|
2022-04-01 19:41:11 +00:00
|
|
|
->SymbolicConstructors.symbolicResultToOutput
|
|
|
|
| (
|
|
|
|
("normal" | "uniform" | "beta" | "lognormal" | "to") as fnName,
|
|
|
|
[EvNumber(f1), EvNumber(f2)],
|
|
|
|
) =>
|
|
|
|
SymbolicConstructors.twoFloat(fnName)
|
2022-04-01 17:21:24 +00:00
|
|
|
->E.R.bind(r => r(f1, f2))
|
2022-04-01 19:41:11 +00:00
|
|
|
->SymbolicConstructors.symbolicResultToOutput
|
2022-04-01 17:21:24 +00:00
|
|
|
| ("triangular" as fnName, [EvNumber(f1), EvNumber(f2), EvNumber(f3)]) =>
|
2022-04-01 19:41:11 +00:00
|
|
|
SymbolicConstructors.threeFloat(fnName)
|
2022-04-01 17:21:24 +00:00
|
|
|
->E.R.bind(r => r(f1, f2, f3))
|
2022-04-01 19:41:11 +00:00
|
|
|
->SymbolicConstructors.symbolicResultToOutput
|
|
|
|
| ("sample", [EvDistribution(dist)]) => Helpers.toFloatFn(#Sample, dist)
|
|
|
|
| ("mean", [EvDistribution(dist)]) => Helpers.toFloatFn(#Mean, dist)
|
2022-04-08 13:08:49 +00:00
|
|
|
| ("toString", [EvDistribution(dist)]) => Helpers.toStringFn(ToString, dist)
|
|
|
|
| ("toSparkline", [EvDistribution(dist)]) => Helpers.toStringFn(ToSparkline(20), dist)
|
2022-04-11 14:51:15 +00:00
|
|
|
| ("toSparkline", [EvDistribution(dist), EvNumber(n)]) =>
|
|
|
|
Helpers.toStringFn(ToSparkline(Belt.Float.toInt(n)), dist)
|
2022-04-02 20:25:41 +00:00
|
|
|
| ("exp", [EvDistribution(a)]) =>
|
|
|
|
// https://mathjs.org/docs/reference/functions/exp.html
|
|
|
|
Helpers.twoDiststoDistFn(Algebraic, "pow", GenericDist.fromFloat(Math.e), a)->Some
|
2022-04-01 19:41:11 +00:00
|
|
|
| ("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)
|
2022-04-01 00:28:48 +00:00
|
|
|
| ("toSampleSet", [EvDistribution(dist), EvNumber(float)]) =>
|
2022-04-01 19:41:11 +00:00
|
|
|
Helpers.toDistFn(ToSampleSet(Belt.Int.fromFloat(float)), dist)
|
2022-04-01 17:21:24 +00:00
|
|
|
| ("truncateLeft", [EvDistribution(dist), EvNumber(float)]) =>
|
2022-04-01 19:41:11 +00:00
|
|
|
Helpers.toDistFn(Truncate(Some(float), None), dist)
|
2022-04-01 00:28:48 +00:00
|
|
|
| ("truncateRight", [EvDistribution(dist), EvNumber(float)]) =>
|
2022-04-01 19:41:11 +00:00
|
|
|
Helpers.toDistFn(Truncate(None, Some(float)), dist)
|
2022-04-01 00:28:48 +00:00
|
|
|
| ("truncate", [EvDistribution(dist), EvNumber(float1), EvNumber(float2)]) =>
|
2022-04-01 19:41:11 +00:00
|
|
|
Helpers.toDistFn(Truncate(Some(float1), Some(float2)), dist)
|
2022-04-12 07:45:44 +00:00
|
|
|
| (("mx" | "mixture"), args) =>
|
2022-04-12 07:39:38 +00:00
|
|
|
Helpers.mixture(args) -> Some
|
2022-04-02 20:25:41 +00:00
|
|
|
| ("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
|
2022-04-02 21:06:57 +00:00
|
|
|
| ("unaryMinus", [EvDistribution(a)]) =>
|
|
|
|
Helpers.twoDiststoDistFn(Algebraic, "multiply", a, GenericDist.fromFloat(-1.0))->Some
|
2022-04-02 20:25:41 +00:00
|
|
|
| (("add" | "multiply" | "subtract" | "divide" | "pow" | "log") as arithmetic, [a, b] as args) =>
|
2022-04-01 19:41:11 +00:00
|
|
|
Helpers.catchAndConvertTwoArgsToDists(args)->E.O2.fmap(((fst, snd)) =>
|
|
|
|
Helpers.twoDiststoDistFn(Algebraic, arithmetic, fst, snd)
|
2022-04-01 00:24:59 +00:00
|
|
|
)
|
|
|
|
| (
|
2022-04-02 20:25:41 +00:00
|
|
|
("dotAdd"
|
|
|
|
| "dotMultiply"
|
|
|
|
| "dotSubtract"
|
|
|
|
| "dotDivide"
|
|
|
|
| "dotPow"
|
|
|
|
| "dotLog") as arithmetic,
|
2022-04-01 00:24:59 +00:00
|
|
|
[a, b] as args,
|
|
|
|
) =>
|
2022-04-01 19:41:11 +00:00
|
|
|
Helpers.catchAndConvertTwoArgsToDists(args)->E.O2.fmap(((fst, snd)) =>
|
|
|
|
Helpers.twoDiststoDistFn(Pointwise, arithmetic, fst, snd)
|
2022-04-01 00:24:59 +00:00
|
|
|
)
|
2022-04-02 20:25:41 +00:00
|
|
|
| ("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
|
2022-04-01 00:24:59 +00:00
|
|
|
| _ => None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-04 15:59:14 +00:00
|
|
|
let genericOutputToReducerValue = (o: DistributionOperation.outputType): result<
|
2022-04-01 19:41:11 +00:00
|
|
|
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) =>
|
2022-04-12 07:39:38 +00:00
|
|
|
Error(RETodo("Distribution Vertical Shift Is Invalid"))
|
|
|
|
| GenDistError(ArgumentError(err)) =>
|
|
|
|
Error(RETodo("Argument Error: " ++ err))
|
2022-04-01 19:41:11 +00:00
|
|
|
| GenDistError(Other(s)) => Error(RETodo(s))
|
|
|
|
}
|
|
|
|
|
2022-04-01 00:24:59 +00:00
|
|
|
let dispatch = call => {
|
2022-04-01 17:21:24 +00:00
|
|
|
dispatchToGenericOutput(call)->E.O2.fmap(genericOutputToReducerValue)
|
2022-04-01 00:24:59 +00:00
|
|
|
}
|