squiggle/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res

364 lines
14 KiB
Plaintext
Raw Normal View History

module ExpressionValue = ReducerInterface_ExpressionValue
type expressionValue = ExpressionValue.expressionValue
module Helpers = {
let arithmeticMap = r =>
switch r {
| "add" => #Add
| "dotAdd" => #Add
| "subtract" => #Subtract
| "dotSubtract" => #Subtract
| "divide" => #Divide
| "log" => #Logarithm
| "dotDivide" => #Divide
2022-04-09 16:37:26 +00:00
| "pow" => #Power
| "dotPow" => #Power
| "multiply" => #Multiply
| "dotMultiply" => #Multiply
| _ => #Multiply
}
let catchAndConvertTwoArgsToDists = (args: array<expressionValue>): option<(
DistributionTypes.genericDist,
DistributionTypes.genericDist,
2022-04-29 18:42:34 +00:00
)> =>
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 = (
2022-04-23 18:09:06 +00:00
fnCall: DistributionTypes.DistributionOperation.toFloat,
dist: DistributionTypes.genericDist,
2022-05-13 20:16:52 +00:00
~env: DistributionOperation.env,
) => {
FromDist(DistributionTypes.DistributionOperation.ToFloat(fnCall), dist)
2022-05-13 20:16:52 +00:00
->DistributionOperation.run(~env)
->Some
}
let toStringFn = (
fnCall: DistributionTypes.DistributionOperation.toString,
dist: DistributionTypes.genericDist,
2022-05-13 20:16:52 +00:00
~env: DistributionOperation.env,
) => {
FromDist(DistributionTypes.DistributionOperation.ToString(fnCall), dist)
2022-05-13 20:16:52 +00:00
->DistributionOperation.run(~env)
->Some
}
let toBoolFn = (
fnCall: DistributionTypes.DistributionOperation.toBool,
dist: DistributionTypes.genericDist,
2022-05-13 20:16:52 +00:00
~env: DistributionOperation.env,
) => {
FromDist(DistributionTypes.DistributionOperation.ToBool(fnCall), dist)
2022-05-13 20:16:52 +00:00
->DistributionOperation.run(~env)
->Some
}
2022-05-13 20:16:52 +00:00
let toDistFn = (
fnCall: DistributionTypes.DistributionOperation.toDist,
dist,
~env: DistributionOperation.env,
) => {
FromDist(DistributionTypes.DistributionOperation.ToDist(fnCall), dist)
2022-05-13 20:16:52 +00:00
->DistributionOperation.run(~env)
->Some
}
2022-05-13 20:16:52 +00:00
let twoDiststoDistFn = (direction, arithmetic, dist1, dist2, ~env: DistributionOperation.env) => {
FromDist(
DistributionTypes.DistributionOperation.ToDistCombination(
direction,
arithmeticMap(arithmetic),
#Dist(dist2),
),
dist1,
2022-05-13 20:16:52 +00:00
)->DistributionOperation.run(~env)
}
2022-04-29 00:24:13 +00:00
let parseNumber = (args: expressionValue): Belt.Result.t<float, string> =>
2022-04-12 07:39:38 +00:00
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
2022-04-12 07:39:38 +00:00
let parseDist = (args: expressionValue): Belt.Result.t<DistributionTypes.genericDist, string> =>
2022-04-12 07:39:38 +00:00
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<DistributionTypes.genericDist>,
string,
> => E.A.fmap(parseDist, ags) |> E.A.R.firstErrorOrOpen
2022-04-12 07:39:38 +00:00
let mixtureWithGivenWeights = (
distributions: array<DistributionTypes.genericDist>,
weights: array<float>,
2022-05-13 20:16:52 +00:00
~env: DistributionOperation.env,
): DistributionOperation.outputType =>
E.A.length(distributions) == E.A.length(weights)
2022-05-13 20:16:52 +00:00
? Mixture(Belt.Array.zip(distributions, weights))->DistributionOperation.run(~env)
: GenDistError(
ArgumentError("Error, mixture call has different number of distributions and weights"),
)
let mixtureWithDefaultWeights = (
distributions: array<DistributionTypes.genericDist>,
2022-05-13 20:16:52 +00:00
~env: DistributionOperation.env,
): DistributionOperation.outputType => {
let length = E.A.length(distributions)
let weights = Belt.Array.make(length, 1.0 /. Belt.Int.toFloat(length))
2022-05-13 20:16:52 +00:00
mixtureWithGivenWeights(distributions, weights, ~env)
}
2022-05-13 20:16:52 +00:00
let mixture = (
args: array<expressionValue>,
~env: DistributionOperation.env,
): DistributionOperation.outputType => {
2022-05-10 20:34:03 +00:00
let error = (err: string): DistributionOperation.outputType =>
err->DistributionTypes.ArgumentError->GenDistError
switch args {
| [EvArray(distributions)] =>
switch parseDistributionArray(distributions) {
2022-05-13 20:16:52 +00:00
| Ok(distrs) => mixtureWithDefaultWeights(distrs, ~env)
| Error(err) => error(err)
}
| [EvArray(distributions), EvArray(weights)] =>
switch (parseDistributionArray(distributions), parseNumberArray(weights)) {
2022-05-13 20:16:52 +00:00
| (Ok(distrs), Ok(wghts)) => mixtureWithGivenWeights(distrs, wghts, ~env)
| (Error(err), Ok(_)) => error(err)
| (Ok(_), Error(err)) => error(err)
| (Error(err1), Error(err2)) => error(`${err1}|${err2}`)
}
| _ =>
switch E.A.last(args) {
| Some(EvArray(b)) => {
let weights = parseNumberArray(b)
let distributions = parseDistributionArray(
Belt.Array.slice(args, ~offset=0, ~len=E.A.length(args) - 1),
)
switch E.R.merge(distributions, weights) {
2022-05-13 20:16:52 +00:00
| Ok(d, w) => mixtureWithGivenWeights(d, w, ~env)
| Error(err) => error(err)
}
}
| Some(EvNumber(_))
| Some(EvDistribution(_)) =>
switch parseDistributionArray(args) {
2022-05-13 20:16:52 +00:00
| Ok(distributions) => mixtureWithDefaultWeights(distributions, ~env)
| Error(err) => error(err)
2022-04-12 07:39:38 +00:00
}
| _ => error("Last argument of mx must be array or distribution")
}
2022-04-12 07:39:38 +00:00
}
}
let klDivergenceWithPrior = (
prediction: DistributionTypes.genericDist,
answer: DistributionTypes.genericDist,
prior: DistributionTypes.genericDist,
env: DistributionOperation.env,
) => {
let term1 = DistributionOperation.Constructors.klDivergence(~env, prediction, answer)
let term2 = DistributionOperation.Constructors.klDivergence(~env, prior, answer)
switch E.R.merge(term1, term2)->E.R2.fmap(((a, b)) => a -. b) {
| Ok(x) => x->DistributionOperation.Float->Some
| Error(_) => None
}
}
}
module SymbolicConstructors = {
let threeFloat = name =>
switch name {
| "triangular" => Ok(SymbolicDist.Triangular.make)
2022-04-02 15:01:53 +00:00
| _ => Error("Unreachable state")
}
let symbolicResultToOutput = (
symbolicResult: result<SymbolicDistTypes.symbolicDist, string>,
): option<DistributionOperation.outputType> =>
switch symbolicResult {
| Ok(r) => Some(Dist(Symbolic(r)))
2022-04-23 18:13:38 +00:00
| Error(r) => Some(GenDistError(OtherError(r)))
}
}
let dispatchToGenericOutput = (
2022-05-13 20:16:52 +00:00
call: ExpressionValue.functionCall,
env: DistributionOperation.env,
): option<DistributionOperation.outputType> => {
let (fnName, args) = call
switch (fnName, args) {
| ("triangular" as fnName, [EvNumber(f1), EvNumber(f2), EvNumber(f3)]) =>
SymbolicConstructors.threeFloat(fnName)
->E.R.bind(r => r(f1, f2, f3))
->SymbolicConstructors.symbolicResultToOutput
2022-05-13 20:16:52 +00:00
| ("sample", [EvDistribution(dist)]) => Helpers.toFloatFn(#Sample, dist, ~env)
2022-05-15 14:58:29 +00:00
| ("sampleN", [EvDistribution(dist), EvNumber(n)]) =>
Some(FloatArray(GenericDist.sampleN(dist, Belt.Int.fromFloat(n))))
| (("mean" | "stdev" | "variance" | "min" | "max" | "mode") as op, [EvDistribution(dist)]) => {
let fn = switch op {
| "mean" => #Mean
| "stdev" => #Stdev
| "variance" => #Variance
| "min" => #Min
| "max" => #Max
| "mode" => #Mode
| _ => #Mean
}
Helpers.toFloatFn(fn, dist, ~env)
}
2022-05-13 20:16:52 +00:00
| ("integralSum", [EvDistribution(dist)]) => Helpers.toFloatFn(#IntegralSum, dist, ~env)
| ("toString", [EvDistribution(dist)]) => Helpers.toStringFn(ToString, dist, ~env)
| ("toSparkline", [EvDistribution(dist)]) =>
Helpers.toStringFn(ToSparkline(MagicNumbers.Environment.sparklineLength), dist, ~env)
2022-04-11 14:51:15 +00:00
| ("toSparkline", [EvDistribution(dist), EvNumber(n)]) =>
2022-05-13 20:16:52 +00:00
Helpers.toStringFn(ToSparkline(Belt.Float.toInt(n)), dist, ~env)
| ("exp", [EvDistribution(a)]) =>
// https://mathjs.org/docs/reference/functions/exp.html
2022-04-27 17:59:33 +00:00
Helpers.twoDiststoDistFn(
Algebraic(AsDefault),
"pow",
GenericDist.fromFloat(MagicNumbers.Math.e),
a,
2022-05-13 20:16:52 +00:00
~env,
2022-04-27 17:59:33 +00:00
)->Some
2022-05-13 20:16:52 +00:00
| ("normalize", [EvDistribution(dist)]) => Helpers.toDistFn(Normalize, dist, ~env)
| ("klDivergence", [EvDistribution(prediction), EvDistribution(answer)]) =>
Some(DistributionOperation.run(FromDist(ToScore(KLDivergence(answer)), prediction), ~env))
| ("klDivergence", [EvDistribution(prediction), EvDistribution(answer), EvDistribution(prior)]) =>
Helpers.klDivergenceWithPrior(prediction, answer, prior, env)
| (
2022-05-16 16:06:21 +00:00
"logScoreWithPointAnswer",
[EvDistribution(prediction), EvNumber(answer), EvDistribution(prior)],
)
| (
2022-05-16 16:06:21 +00:00
"logScoreWithPointAnswer",
[EvDistribution(prediction), EvDistribution(Symbolic(#Float(answer))), EvDistribution(prior)],
) =>
DistributionOperation.run(
FromDist(ToScore(LogScore(answer, prior->Some)), prediction),
~env,
)->Some
2022-05-16 16:06:21 +00:00
| ("logScoreWithPointAnswer", [EvDistribution(prediction), EvNumber(answer)])
| (
2022-05-16 16:06:21 +00:00
"logScoreWithPointAnswer",
[EvDistribution(prediction), EvDistribution(Symbolic(#Float(answer)))],
) =>
DistributionOperation.run(FromDist(ToScore(LogScore(answer, None)), prediction), ~env)->Some
2022-05-13 20:16:52 +00:00
| ("isNormalized", [EvDistribution(dist)]) => Helpers.toBoolFn(IsNormalized, dist, ~env)
| ("toPointSet", [EvDistribution(dist)]) => Helpers.toDistFn(ToPointSet, dist, ~env)
2022-04-29 01:14:03 +00:00
| ("scaleLog", [EvDistribution(dist)]) =>
2022-05-13 20:16:52 +00:00
Helpers.toDistFn(Scale(#Logarithm, MagicNumbers.Math.e), dist, ~env)
| ("scaleLog10", [EvDistribution(dist)]) => Helpers.toDistFn(Scale(#Logarithm, 10.0), dist, ~env)
2022-04-29 01:14:03 +00:00
| ("scaleLog", [EvDistribution(dist), EvNumber(float)]) =>
2022-05-13 20:16:52 +00:00
Helpers.toDistFn(Scale(#Logarithm, float), dist, ~env)
| ("scaleLogWithThreshold", [EvDistribution(dist), EvNumber(base), EvNumber(eps)]) =>
2022-05-13 20:16:52 +00:00
Helpers.toDistFn(Scale(#LogarithmWithThreshold(eps), base), dist, ~env)
2022-04-29 01:14:03 +00:00
| ("scalePow", [EvDistribution(dist), EvNumber(float)]) =>
2022-05-13 20:16:52 +00:00
Helpers.toDistFn(Scale(#Power, float), dist, ~env)
2022-04-29 01:14:03 +00:00
| ("scaleExp", [EvDistribution(dist)]) =>
2022-05-13 20:16:52 +00:00
Helpers.toDistFn(Scale(#Power, MagicNumbers.Math.e), dist, ~env)
| ("cdf", [EvDistribution(dist), EvNumber(float)]) => Helpers.toFloatFn(#Cdf(float), dist, ~env)
| ("pdf", [EvDistribution(dist), EvNumber(float)]) => Helpers.toFloatFn(#Pdf(float), dist, ~env)
| ("inv", [EvDistribution(dist), EvNumber(float)]) => Helpers.toFloatFn(#Inv(float), dist, ~env)
2022-06-13 04:19:28 +00:00
| ("quantile", [EvDistribution(dist), EvNumber(float)]) => Helpers.toFloatFn(#Inv(float), dist, ~env)
2022-04-01 00:28:48 +00:00
| ("toSampleSet", [EvDistribution(dist), EvNumber(float)]) =>
2022-05-13 20:16:52 +00:00
Helpers.toDistFn(ToSampleSet(Belt.Int.fromFloat(float)), dist, ~env)
| ("toSampleSet", [EvDistribution(dist)]) =>
2022-05-13 20:16:52 +00:00
Helpers.toDistFn(ToSampleSet(env.sampleCount), dist, ~env)
2022-06-11 15:47:52 +00:00
| ("toList", [EvDistribution(SampleSet(dist))]) => Some(FloatArray(SampleSetDist.T.get(dist)))
2022-04-30 01:41:09 +00:00
| ("fromSamples", [EvArray(inputArray)]) => {
let _wrapInputErrors = x => SampleSetDist.NonNumericInput(x)
let parsedArray = Helpers.parseNumberArray(inputArray)->E.R2.errMap(_wrapInputErrors)
switch parsedArray {
2022-05-13 20:16:52 +00:00
| Ok(array) => DistributionOperation.run(FromSamples(array), ~env)
2022-04-30 01:41:09 +00:00
| Error(e) => GenDistError(SampleSetError(e))
}->Some
}
2022-05-13 20:16:52 +00:00
| ("inspect", [EvDistribution(dist)]) => Helpers.toDistFn(Inspect, dist, ~env)
| ("truncateLeft", [EvDistribution(dist), EvNumber(float)]) =>
2022-05-13 20:16:52 +00:00
Helpers.toDistFn(Truncate(Some(float), None), dist, ~env)
2022-04-01 00:28:48 +00:00
| ("truncateRight", [EvDistribution(dist), EvNumber(float)]) =>
2022-05-13 20:16:52 +00:00
Helpers.toDistFn(Truncate(None, Some(float)), dist, ~env)
2022-04-01 00:28:48 +00:00
| ("truncate", [EvDistribution(dist), EvNumber(float1), EvNumber(float2)]) =>
2022-05-13 20:16:52 +00:00
Helpers.toDistFn(Truncate(Some(float1), Some(float2)), dist, ~env)
| ("mx" | "mixture", args) => Helpers.mixture(args, ~env)->Some
| ("log", [EvDistribution(a)]) =>
2022-04-27 17:59:33 +00:00
Helpers.twoDiststoDistFn(
Algebraic(AsDefault),
"log",
a,
GenericDist.fromFloat(MagicNumbers.Math.e),
2022-05-13 20:16:52 +00:00
~env,
2022-04-27 17:59:33 +00:00
)->Some
| ("log10", [EvDistribution(a)]) =>
2022-05-13 20:16:52 +00:00
Helpers.twoDiststoDistFn(
Algebraic(AsDefault),
"log",
a,
GenericDist.fromFloat(10.0),
~env,
)->Some
| ("unaryMinus", [EvDistribution(a)]) =>
2022-05-13 20:16:52 +00:00
Helpers.twoDiststoDistFn(
Algebraic(AsDefault),
"multiply",
a,
GenericDist.fromFloat(-1.0),
~env,
)->Some
2022-04-21 22:09:06 +00:00
| (("add" | "multiply" | "subtract" | "divide" | "pow" | "log") as arithmetic, [_, _] as args) =>
Helpers.catchAndConvertTwoArgsToDists(args)->E.O2.fmap(((fst, snd)) =>
2022-05-13 20:16:52 +00:00
Helpers.twoDiststoDistFn(Algebraic(AsDefault), arithmetic, fst, snd, ~env)
)
| (
("dotAdd"
| "dotMultiply"
| "dotSubtract"
| "dotDivide"
2022-04-23 18:09:06 +00:00
| "dotPow") as arithmetic,
2022-04-21 22:09:06 +00:00
[_, _] as args,
) =>
Helpers.catchAndConvertTwoArgsToDists(args)->E.O2.fmap(((fst, snd)) =>
2022-05-13 20:16:52 +00:00
Helpers.twoDiststoDistFn(Pointwise, arithmetic, fst, snd, ~env)
)
| ("dotExp", [EvDistribution(a)]) =>
2022-04-27 17:59:33 +00:00
Helpers.twoDiststoDistFn(
Pointwise,
"dotPow",
GenericDist.fromFloat(MagicNumbers.Math.e),
a,
2022-05-13 20:16:52 +00:00
~env,
2022-04-27 17:59:33 +00:00
)->Some
| _ => None
}
}
let genericOutputToReducerValue = (o: DistributionOperation.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))
| Bool(d) => Ok(EvBool(d))
2022-05-15 14:58:29 +00:00
| FloatArray(d) => Ok(EvArray(d |> E.A.fmap(r => ReducerInterface_ExpressionValue.EvNumber(r))))
| GenDistError(err) => Error(REDistributionError(err))
}
2022-06-06 21:42:49 +00:00
let dispatch = (call: ExpressionValue.functionCall, environment) =>
dispatchToGenericOutput(call, environment)->E.O2.fmap(genericOutputToReducerValue)