diff --git a/__tests__/Samples__test.re b/__tests__/Samples__test.re index bc61cb99..9afbaf86 100644 --- a/__tests__/Samples__test.re +++ b/__tests__/Samples__test.re @@ -14,12 +14,12 @@ describe("Lodash", () => { describe("Lodash", () => { makeTest( "split", - Samples.T.splitContinuousAndDiscrete([|1.432, 1.33455, 2.0|]), + SamplesToShape.Internals.T.splitContinuousAndDiscrete([|1.432, 1.33455, 2.0|]), ([|1.432, 1.33455, 2.0|], E.FloatFloatMap.empty()), ); makeTest( "split", - Samples.T.splitContinuousAndDiscrete([| + SamplesToShape.Internals.T.splitContinuousAndDiscrete([| 1.432, 1.33455, 2.0, @@ -39,12 +39,12 @@ describe("Lodash", () => { }; let (_, discrete) = - Samples.T.splitContinuousAndDiscrete(makeDuplicatedArray(10)); + SamplesToShape.Internals.T.splitContinuousAndDiscrete(makeDuplicatedArray(10)); let toArr = discrete |> E.FloatFloatMap.toArray; makeTest("splitMedium", toArr |> Belt.Array.length, 10); let (c, discrete) = - Samples.T.splitContinuousAndDiscrete(makeDuplicatedArray(500)); + SamplesToShape.Internals.T.splitContinuousAndDiscrete(makeDuplicatedArray(500)); let toArr = discrete |> E.FloatFloatMap.toArray; makeTest("splitMedium", toArr |> Belt.Array.length, 500); }) diff --git a/showcase/entries/Continuous2.re b/showcase/entries/Continuous2.re index f6cb9a53..e46218ef 100644 --- a/showcase/entries/Continuous2.re +++ b/showcase/entries/Continuous2.re @@ -6,18 +6,18 @@ // uniform(0,1) > 0.3 ? lognormal(6.652, -0.41): 0 let timeDist ={ - let ingredients = RenderTypes.DistPlusRenderer.Ingredients.make( + let ingredients = DistPlusRenderer.Inputs.Ingredients.make( ~guesstimatorString="(floor(10 to 15))", ~domain=RightLimited({xPoint: 50.0, excludingProbabilityMass: 0.3}), ~unit= DistTypes.TimeDistribution({zero: MomentRe.momentNow(), unit: `years}), ()); - let inputs = RenderTypes.DistPlusRenderer.make(~distPlusIngredients=ingredients,()) + let inputs = DistPlusRenderer.Inputs.make(~distPlusIngredients=ingredients,()) inputs |> DistPlusRenderer.run } let setup = dist => - RenderTypes.DistPlusRenderer.make(~distPlusIngredients=dist,()) + DistPlusRenderer.Inputs.make(~distPlusIngredients=dist,()) |> DistPlusRenderer.run |> E.R.fmap(distPlus => ) |> E.R.toOption @@ -28,7 +28,7 @@ let simpleExample = (name, guesstimatorString) =>

{name |> ReasonReact.string}

- {setup(RenderTypes.DistPlusRenderer.Ingredients.make(~guesstimatorString, ()))} + {setup(DistPlusRenderer.Inputs.Ingredients.make(~guesstimatorString, ()))} ; let timeExample = (name, guesstimatorString) => @@ -37,7 +37,7 @@ let timeExample = (name, guesstimatorString) => {name |> ReasonReact.string} {setup( - RenderTypes.DistPlusRenderer.Ingredients.make( + DistPlusRenderer.Inputs.Ingredients.make( ~guesstimatorString, ~unit=TimeDistribution({zero: MomentRe.momentNow(), unit: `years}), (), diff --git a/showcase/entries/ExpressionTreeExamples.re b/showcase/entries/ExpressionTreeExamples.re index 2ca91990..a93f61d1 100644 --- a/showcase/entries/ExpressionTreeExamples.re +++ b/showcase/entries/ExpressionTreeExamples.re @@ -1,5 +1,5 @@ let setup = dist => - RenderTypes.DistPlusRenderer.make(~distPlusIngredients=dist, ()) + DistPlusRenderer.Inputs.make(~distPlusIngredients=dist, ()) |> DistPlusRenderer.run |> E.R.fmap(distPlus => ) |> E.R.toOption @@ -10,7 +10,7 @@ let simpleExample = (guesstimatorString, ~problem="", ()) =>

{guesstimatorString |> ReasonReact.string}

{problem |> (e => "problem: " ++ e) |> ReasonReact.string}

{setup( - RenderTypes.DistPlusRenderer.Ingredients.make(~guesstimatorString, ()), + DistPlusRenderer.Inputs.Ingredients.make(~guesstimatorString, ()), )} ; diff --git a/src/components/DistBuilder.re b/src/components/DistBuilder.re index 2a28a5a6..04e197bb 100644 --- a/src/components/DistBuilder.re +++ b/src/components/DistBuilder.re @@ -33,6 +33,23 @@ module Form = ReForm.Make(FormConfig); let schema = Form.Validation.Schema([||]); +module FieldText = { + [@react.component] + let make = (~field, ~label) => { + + R.ste}> + validate()} + /> + + } + />; + }; +}; module FieldString = { [@react.component] let make = (~field, ~label) => { @@ -111,13 +128,6 @@ module Styles = { ]); }; -type inputs = { - samplingInputs: RenderTypes.ShapeRenderer.Sampling.inputs, - guesstimatorString: string, - length: int, - shouldDownsampleSampledDistribution: int, -}; - module DemoDist = { [@react.component] let make = (~guesstimatorString, ~domain, ~unit, ~options) => { @@ -127,30 +137,34 @@ module DemoDist = { {switch (domain, unit, options) { | (Some(domain), Some(unit), Some(options)) => let distPlusIngredients = - RenderTypes.DistPlusRenderer.Ingredients.make( + DistPlusRenderer.Inputs.Ingredients.make( ~guesstimatorString, ~domain, ~unit, (), ); - let inputs = - RenderTypes.DistPlusRenderer.make( + let inputs1 = + DistPlusRenderer.Inputs.make( ~samplingInputs={ sampleCount: Some(options.sampleCount), outputXYPoints: Some(options.outputXYPoints), kernelWidth: options.kernelWidth, + shapeLength: Some(options.downsampleTo |> E.O.default(1000)) }, ~distPlusIngredients, - ~shouldDownsample=options.downsampleTo |> E.O.isSome, - ~recommendedLength=options.downsampleTo |> E.O.default(1000), + ~environment= + [|("p", `SymbolicDist(`Float(1.0)))|] + ->Belt.Map.String.fromArray, (), ); - let response = DistPlusRenderer.run(inputs); - switch (response) { - | Ok(distPlus) => - let normalizedDistPlus = DistPlus.T.normalize(distPlus); - ; - | Error(r) => r |> R.ste + + let response1 = DistPlusRenderer.run(inputs1); + switch (response1) { + | (Ok(distPlus1)) => + <> + + + | (Error(r)) => r |> R.ste }; | _ => "Nothing to show. Try to change the distribution description." @@ -314,7 +328,7 @@ let make = () => { - diff --git a/src/components/Drawer.re b/src/components/Drawer.re index c8777b54..7b9be5e7 100644 --- a/src/components/Drawer.re +++ b/src/components/Drawer.re @@ -392,8 +392,8 @@ module Draw = { let normal: SymbolicTypes.symbolicDist = `Normal({mean, stdev}); let normalShape = ExpressionTree.toShape( - numSamples, - {sampleCount: 10000, outputXYPoints: 10000, kernelWidth: None}, + {sampleCount: 10000, outputXYPoints: 10000, kernelWidth: None, shapeLength:numSamples}, + ExpressionTypes.ExpressionTree.Environment.empty, `SymbolicDist(normal), ) |> E.R.toExn; let xyShape: Types.xyShape = diff --git a/src/components/charts/DistPlusPlotReducer.re b/src/components/charts/DistPlusPlotReducer.re index 7eaa48f1..0c4ec0ad 100644 --- a/src/components/charts/DistPlusPlotReducer.re +++ b/src/components/charts/DistPlusPlotReducer.re @@ -106,7 +106,6 @@ let init = { showParams: false, showPercentiles: true, distributions: [ - {yLog: false, xLog: false, isCumulative: false, height: 4}, - {yLog: false, xLog: false, isCumulative: true, height: 1}, + {yLog: false, xLog: false, isCumulative: false, height: 1}, ], }; \ No newline at end of file diff --git a/src/distPlus/distribution/AlgebraicShapeCombination.re b/src/distPlus/distribution/AlgebraicShapeCombination.re index fd6f7453..631fb3a5 100644 --- a/src/distPlus/distribution/AlgebraicShapeCombination.re +++ b/src/distPlus/distribution/AlgebraicShapeCombination.re @@ -27,15 +27,15 @@ let toDiscretePointMassesFromTriangulars = let xsProdN1: array(float) = Belt.Array.makeUninitializedUnsafe(n - 1); let xsProdN2: array(float) = Belt.Array.makeUninitializedUnsafe(n - 2); for (i in 0 to n - 1) { - Belt.Array.set(xsSq, i, xs[i] *. xs[i]) |> ignore; + Belt.Array.set(xsSq, i, xs[i] *. xs[i]) |> ignore; (); }; for (i in 0 to n - 2) { - Belt.Array.set(xsProdN1, i, xs[i] *. xs[i + 1]) |> ignore; + Belt.Array.set(xsProdN1, i, xs[i] *. xs[i + 1]) |> ignore; (); }; for (i in 0 to n - 3) { - Belt.Array.set(xsProdN2, i, xs[i] *. xs[i + 2]) |> ignore; + Belt.Array.set(xsProdN2, i, xs[i] *. xs[i + 2]) |> ignore; (); }; // means and variances @@ -45,11 +45,8 @@ let toDiscretePointMassesFromTriangulars = if (inverse) { for (i in 1 to n - 2) { - Belt.Array.set( - masses, - i - 1, - (xs[i + 1] -. xs[i - 1]) *. ys[i] /. 2., - ) |> ignore; + Belt.Array.set(masses, i - 1, (xs[i + 1] -. xs[i - 1]) *. ys[i] /. 2.) + |> ignore; // this only works when the whole triange is either on the left or on the right of zero let a = xs[i - 1]; @@ -70,9 +67,9 @@ let toDiscretePointMassesFromTriangulars = -. inverseMean ** 2.; - Belt.Array.set(means, i - 1, inverseMean) |> ignore; + Belt.Array.set(means, i - 1, inverseMean) |> ignore; - Belt.Array.set(variances, i - 1, inverseVar) |> ignore; + Belt.Array.set(variances, i - 1, inverseVar) |> ignore; (); }; @@ -80,14 +77,12 @@ let toDiscretePointMassesFromTriangulars = } else { for (i in 1 to n - 2) { // area of triangle = width * height / 2 - Belt.Array.set( - masses, - i - 1, - (xs[i + 1] -. xs[i - 1]) *. ys[i] /. 2., - ) |> ignore; + Belt.Array.set(masses, i - 1, (xs[i + 1] -. xs[i - 1]) *. ys[i] /. 2.) + |> ignore; // means of triangle = (a + b + c) / 3 - Belt.Array.set(means, i - 1, (xs[i - 1] +. xs[i] +. xs[i + 1]) /. 3.) |> ignore; + Belt.Array.set(means, i - 1, (xs[i - 1] +. xs[i] +. xs[i + 1]) /. 3.) + |> ignore; // variance of triangle = (a^2 + b^2 + c^2 - ab - ac - bc) / 18 Belt.Array.set( @@ -102,7 +97,8 @@ let toDiscretePointMassesFromTriangulars = -. xsProdN2[i - 1] ) /. 18., - ) |> ignore; + ) + |> ignore; (); }; {n: n - 2, masses, means, variances}; @@ -134,8 +130,10 @@ let combineShapesContinuousContinuous = | `Subtract => ((m1, m2) => m1 -. m2) | `Multiply => ((m1, m2) => m1 *. m2) | `Divide => ((m1, mInv2) => m1 *. mInv2) + | `Exponentiate => ((m1, mInv2) => m1 ** mInv2) }; // note: here, mInv2 = mean(1 / t2) ~= 1 / mean(t2) + // TODO: I don't know what the variances are for exponentatiation // converts the variances and means of the two inputs into the variance of the output let combineVariancesFn = switch (op) { @@ -144,6 +142,8 @@ let combineShapesContinuousContinuous = | `Multiply => ( (v1, v2, m1, m2) => v1 *. v2 +. v1 *. m2 ** 2. +. v2 *. m1 ** 2. ) + | `Exponentiate => + ((v1, v2, m1, m2) => v1 *. v2 +. v1 *. m2 ** 2. +. v2 *. m1 ** 2.); | `Divide => ( (v1, vInv2, m1, mInv2) => v1 *. vInv2 +. v1 *. mInv2 ** 2. +. vInv2 *. m1 ** 2. @@ -225,9 +225,12 @@ let toDiscretePointMassesFromDiscrete = }; let combineShapesContinuousDiscrete = - (op: ExpressionTypes.algebraicOperation, continuousShape: DistTypes.xyShape, discreteShape: DistTypes.xyShape) + ( + op: ExpressionTypes.algebraicOperation, + continuousShape: DistTypes.xyShape, + discreteShape: DistTypes.xyShape, + ) : DistTypes.xyShape => { - let t1n = continuousShape |> XYShape.T.length; let t2n = discreteShape |> XYShape.T.length; @@ -248,15 +251,19 @@ let combineShapesContinuousDiscrete = Belt.Array.set( dxyShape, i, - (fn(continuousShape.xs[i], discreteShape.xs[j]), - continuousShape.ys[i] *. discreteShape.ys[j]), - ) |> ignore; + ( + fn(continuousShape.xs[i], discreteShape.xs[j]), + continuousShape.ys[i] *. discreteShape.ys[j], + ), + ) + |> ignore; (); }; Belt.Array.set(outXYShapes, j, dxyShape) |> ignore; (); } | `Multiply + | `Exponentiate | `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. @@ -266,8 +273,12 @@ let combineShapesContinuousDiscrete = Belt.Array.set( dxyShape, i, - (fn(continuousShape.xs[i], discreteShape.xs[j]), continuousShape.ys[i] *. discreteShape.ys[j] /. discreteShape.xs[j]), - ) |> ignore; + ( + fn(continuousShape.xs[i], discreteShape.xs[j]), + {continuousShape.ys[i] *. discreteShape.ys[j] /. discreteShape.xs[j]} + ), + ) + |> ignore; (); }; Belt.Array.set(outXYShapes, j, dxyShape) |> ignore; @@ -278,7 +289,10 @@ let combineShapesContinuousDiscrete = outXYShapes |> E.A.fmap(XYShape.T.fromZippedArray) |> E.A.fold_left( - XYShape.PointwiseCombination.combine((+.), - XYShape.XtoY.continuousInterpolator(`Linear, `UseZero)), - XYShape.T.empty); + XYShape.PointwiseCombination.combine( + (+.), + XYShape.XtoY.continuousInterpolator(`Linear, `UseZero), + ), + XYShape.T.empty, + ); }; diff --git a/src/distPlus/distribution/Continuous.re b/src/distPlus/distribution/Continuous.re index 7c5451e7..276cec88 100644 --- a/src/distPlus/distribution/Continuous.re +++ b/src/distPlus/distribution/Continuous.re @@ -37,6 +37,7 @@ let empty: DistTypes.continuousShape = { let stepwiseToLinear = (t: t): t => make(~integralSumCache=t.integralSumCache, ~integralCache=t.integralCache, XYShape.Range.stepwiseToLinear(t.xyShape)); +// Note: This results in a distribution with as many points as the sum of those in t1 and t2. let combinePointwise = ( ~integralSumCachesFn=(_, _) => None, diff --git a/src/distPlus/distribution/Shape.re b/src/distPlus/distribution/Shape.re index 01685c3c..fa0be2b8 100644 --- a/src/distPlus/distribution/Shape.re +++ b/src/distPlus/distribution/Shape.re @@ -216,6 +216,11 @@ let sample = (t: t): float => { bar; }; +let isFloat = (t:t) => switch(t){ +| Discrete({xyShape: {xs: [|_|], ys: [|1.0|]}}) => true +| _ => false +} + let sampleNRendered = (n, dist) => { let integralCache = T.Integral.get(dist); let distWithUpdatedIntegralCache = T.updateIntegralCache(Some(integralCache), dist); diff --git a/src/distPlus/expressionTree/ExpressionTree.re b/src/distPlus/expressionTree/ExpressionTree.re index 66ccf3a6..797aa14e 100644 --- a/src/distPlus/expressionTree/ExpressionTree.re +++ b/src/distPlus/expressionTree/ExpressionTree.re @@ -1,13 +1,17 @@ open ExpressionTypes.ExpressionTree; -let toShape = (intendedShapeLength: int, samplingInputs, node: node) => { +let toLeaf = (samplingInputs, environment, node: node) => { + node + |> ExpressionTreeEvaluator.toLeaf({ + samplingInputs, + environment, + evaluateNode: ExpressionTreeEvaluator.toLeaf, + }); +}; + +let toShape = (samplingInputs, environment, node: node) => { let renderResult = - `Render(`Normalize(node)) - |> ExpressionTreeEvaluator.toLeaf({ - samplingInputs, - intendedShapeLength, - evaluateNode: ExpressionTreeEvaluator.toLeaf, - }); + `Render(`Normalize(node)) |> toLeaf(samplingInputs, environment); switch (renderResult) { | Ok(`RenderedDist(shape)) => Ok(shape) diff --git a/src/distPlus/expressionTree/ExpressionTreeEvaluator.re b/src/distPlus/expressionTree/ExpressionTreeEvaluator.re index 2ad84adc..27570b59 100644 --- a/src/distPlus/expressionTree/ExpressionTreeEvaluator.re +++ b/src/distPlus/expressionTree/ExpressionTreeEvaluator.re @@ -151,7 +151,7 @@ module PointwiseCombination = { }; }; - let pointwiseMultiply = (evaluationParams: evaluationParams, t1: t, t2: t) => { + let pointwiseCombine = (fn, evaluationParams: evaluationParams, t1: t, t2: t) => { // TODO: construct a function that we can easily sample from, to construct // a RenderedDist. Use the xMin and xMax of the rendered shapes to tell the sampling function where to look. // TODO: This should work for symbolic distributions too! @@ -176,7 +176,8 @@ module PointwiseCombination = { ) => { switch (pointwiseOp) { | `Add => pointwiseAdd(evaluationParams, t1, t2) - | `Multiply => pointwiseMultiply(evaluationParams, t1, t2) + | `Multiply => pointwiseCombine(( *. ),evaluationParams, t1, t2) + | `Exponentiate => pointwiseCombine(( *. ),evaluationParams, t1, t2) }; }; }; @@ -259,14 +260,27 @@ module FloatFromDist = { }; }; +// TODO: This forces things to be floats +let callableFunction = (evaluationParams, name, args) => { + let b = + args + |> E.A.fmap(a => + Render.render(evaluationParams, a) + |> E.R.bind(_, Render.toFloat) + ) + |> E.A.R.firstErrorOrOpen; + b |> E.R.bind(_, Functions.fnn(evaluationParams, name)); +}; + module Render = { let rec operationToLeaf = (evaluationParams: evaluationParams, t: node): result(t, string) => { switch (t) { + | `Function(_) => Error("Cannot render a function") | `SymbolicDist(d) => Ok( `RenderedDist( - SymbolicDist.T.toShape(evaluationParams.intendedShapeLength, d), + SymbolicDist.T.toShape(evaluationParams.samplingInputs.shapeLength, d), ), ) | `RenderedDist(_) as t => Ok(t) // already a rendered shape, we're done here @@ -275,6 +289,13 @@ module Render = { }; }; +let run = (node, fnNode) => { + switch (fnNode) { + | `Function(r) => Ok(r(node)) + | _ => Error("Not a function") + }; +}; + /* This function recursively goes through the nodes of the parse tree, replacing each Operation node and its subtree with a Data node. Whenever possible, the replacement produces a new Symbolic Data node, @@ -314,5 +335,9 @@ let toLeaf = FloatFromDist.operationToLeaf(evaluationParams, distToFloatOp, t) | `Normalize(t) => Normalize.operationToLeaf(evaluationParams, t) | `Render(t) => Render.operationToLeaf(evaluationParams, t) + | `Function(t) => Ok(`Function(t)) + | `Symbol(r) => ExpressionTypes.ExpressionTree.Environment.get(evaluationParams.environment, r) |> E.O.toResult("Undeclared variable " ++ r) + | `FunctionCall(name, args) => + callableFunction(evaluationParams, name, args) }; }; diff --git a/src/distPlus/expressionTree/ExpressionTypes.re b/src/distPlus/expressionTree/ExpressionTypes.re index a76aee74..6453667a 100644 --- a/src/distPlus/expressionTree/ExpressionTypes.re +++ b/src/distPlus/expressionTree/ExpressionTypes.re @@ -1,5 +1,5 @@ -type algebraicOperation = [ | `Add | `Multiply | `Subtract | `Divide]; -type pointwiseOperation = [ | `Add | `Multiply]; +type algebraicOperation = [ | `Add | `Multiply | `Subtract | `Divide | `Exponentiate]; +type pointwiseOperation = [ | `Add | `Multiply | `Exponentiate]; type scaleOperation = [ | `Multiply | `Exponentiate | `Log]; type distToFloatOperation = [ | `Pdf(float) @@ -20,17 +20,37 @@ module ExpressionTree = { | `Truncate(option(float), option(float), node) | `Normalize(node) | `FloatFromDist(distToFloatOperation, node) + | `Function(array(string), node) + | `FunctionCall(string, array(node)) + | `Symbol(string) ]; type samplingInputs = { sampleCount: int, outputXYPoints: int, kernelWidth: option(float), + shapeLength: int }; + type environment = Belt.Map.String.t(node); + + module Environment = { + type t = environment + module MS = Belt.Map.String; + let fromArray = MS.fromArray + let empty:t = [||]->fromArray; + let mergeKeepSecond = (a:t,b:t) => MS.merge(a,b, (_,a,b) =>switch(a,b){ + | (_, Some(b)) => Some(b) + | (Some(a), _) => Some(a) + | _ => None + }) + let update = (t,str, fn) => MS.update(t, str, fn) + let get = (t:t,str) => MS.get(t, str) + } + type evaluationParams = { samplingInputs, - intendedShapeLength: int, + environment, evaluateNode: (evaluationParams, node) => Belt.Result.t(node, string), }; @@ -71,8 +91,20 @@ module ExpressionTree = { | `RenderedDist(r) => Some(r) | _ => None }; - }; + let _toFloat = (t: DistTypes.shape) => + switch (t) { + | Discrete({xyShape: {xs: [|x|], ys: [|1.0|]}}) => + Some(`SymbolicDist(`Float(x))) + | _ => None + }; + + let toFloat = (item: node): result(node, string) => + item + |> getShape + |> E.O.bind(_, _toFloat) + |> E.O.toResult("Not valid shape"); + }; }; type simplificationResult = [ @@ -80,3 +112,8 @@ type simplificationResult = [ | `Error(string) | `NoSolution ]; + +module Program = { + type statement = [ | `Assignment(string, ExpressionTree.node) | `Expression(ExpressionTree.node)]; + type program = array(statement); +} \ No newline at end of file diff --git a/src/distPlus/expressionTree/Functions.re b/src/distPlus/expressionTree/Functions.re new file mode 100644 index 00000000..f32c570c --- /dev/null +++ b/src/distPlus/expressionTree/Functions.re @@ -0,0 +1,122 @@ +type node = ExpressionTypes.ExpressionTree.node; + +let toOkSym = r => Ok(`SymbolicDist(r)); + +let twoFloats = (fn, n1: node, n2: node): result(node, string) => + switch (n1, n2) { + | (`SymbolicDist(`Float(a)), `SymbolicDist(`Float(b))) => fn(a, b) + | _ => Error("Variables have wrong type") + }; + +let threeFloats = (fn, n1: node, n2: node, n3: node): result(node, string) => + switch (n1, n2, n3) { + | ( + `SymbolicDist(`Float(a)), + `SymbolicDist(`Float(b)), + `SymbolicDist(`Float(c)), + ) => + fn(a, b, c) + | _ => Error("Variables have wrong type") + }; + +let twoFloatsToOkSym = fn => twoFloats((f1, f2) => fn(f1, f2) |> toOkSym); + +let threeFloats = fn => threeFloats((f1, f2, f3) => fn(f1, f2, f3)); + +let apply2 = (fn, args): result(node, string) => + switch (args) { + | [|a, b|] => fn(a, b) + | _ => Error("Needs 2 args") + }; + +let apply3 = (fn, args: array(node)): result(node, string) => + switch (args) { + | [|a, b, c|] => fn(a, b, c) + | _ => Error("Needs 3 args") + }; + +let to_: array(node) => result(node, string) = + fun + | [|`SymbolicDist(`Float(low)), `SymbolicDist(`Float(high))|] + when low <= 0.0 && low < high => { + Ok(`SymbolicDist(SymbolicDist.Normal.from90PercentCI(low, high))); + } + | [|`SymbolicDist(`Float(low)), `SymbolicDist(`Float(high))|] + when low < high => { + Ok(`SymbolicDist(SymbolicDist.Lognormal.from90PercentCI(low, high))); + } + | [|`SymbolicDist(`Float(_)), `SymbolicDist(_)|] => + Error("Low value must be less than high value.") + | _ => Error("Requires 2 variables"); + +let processCustomFn = + ( + evaluationParams: ExpressionTypes.ExpressionTree.evaluationParams, + args: array(node), + argNames: array(string), + fnResult: node, + ) => + if (E.A.length(args) == E.A.length(argNames)) { + let newEnvironment = + Belt.Array.zip(argNames, args) + |> ExpressionTypes.ExpressionTree.Environment.fromArray; + let newEvaluationParams: ExpressionTypes.ExpressionTree.evaluationParams = { + samplingInputs: evaluationParams.samplingInputs, + environment: + ExpressionTypes.ExpressionTree.Environment.mergeKeepSecond( + evaluationParams.environment, + newEnvironment, + ), + evaluateNode: evaluationParams.evaluateNode, + }; + evaluationParams.evaluateNode(newEvaluationParams, fnResult); + } else { + Error("Failure"); + }; + +let fnn = + ( + evaluationParams: ExpressionTypes.ExpressionTree.evaluationParams, + name, + args: array(node), + ) => + switch ( + name, + ExpressionTypes.ExpressionTree.Environment.get( + evaluationParams.environment, + name, + ), + ) { + | (_, Some(`Function(argNames, tt))) => + processCustomFn(evaluationParams, args, argNames, tt) + | ("normal", _) => + apply2(twoFloatsToOkSym(SymbolicDist.Normal.make), args) + | ("uniform", _) => + apply2(twoFloatsToOkSym(SymbolicDist.Uniform.make), args) + | ("beta", _) => apply2(twoFloatsToOkSym(SymbolicDist.Beta.make), args) + | ("cauchy", _) => + apply2(twoFloatsToOkSym(SymbolicDist.Cauchy.make), args) + | ("lognormal", _) => + apply2(twoFloatsToOkSym(SymbolicDist.Lognormal.make), args) + | ("lognormalFromMeanAndStdDev", _) => + apply2(twoFloatsToOkSym(SymbolicDist.Lognormal.fromMeanAndStdev), args) + | ("exponential", _) => + switch (args) { + | [|`SymbolicDist(`Float(a))|] => + Ok(`SymbolicDist(SymbolicDist.Exponential.make(a))) + | _ => Error("Needs 3 valid arguments") + } + | ("triangular", _) => + switch (args) { + | [| + `SymbolicDist(`Float(a)), + `SymbolicDist(`Float(b)), + `SymbolicDist(`Float(c)), + |] => + SymbolicDist.Triangular.make(a, b, c) + |> E.R.fmap(r => `SymbolicDist(r)) + | _ => Error("Needs 3 valid arguments") + } + | ("to", _) => to_(args) + | _ => Error("Function not found") + }; diff --git a/src/distPlus/expressionTree/MathJsParser.re b/src/distPlus/expressionTree/MathJsParser.re index 1704c2de..52fb90b2 100644 --- a/src/distPlus/expressionTree/MathJsParser.re +++ b/src/distPlus/expressionTree/MathJsParser.re @@ -4,10 +4,18 @@ module MathJsonToMathJsAdt = { | Value(float) | Fn(fn) | Array(array(arg)) + | Blocks(array(arg)) | Object(Js.Dict.t(arg)) + | Assignment(arg, arg) + | FunctionAssignment(fnAssignment) and fn = { name: string, args: array(arg), + } + and fnAssignment = { + name: string, + args: array(string), + expression: arg, }; let rec run = (j: Js.Json.t) => @@ -40,6 +48,25 @@ module MathJsonToMathJsAdt = { let items = field("items", array(run), j); Some(Array(items |> E.A.O.concatSomes)); | "SymbolNode" => Some(Symbol(field("name", string, j))) + | "AssignmentNode" => + let object_ = j |> field("object", run); + let value_ = j |> field("value", run); + switch (object_, value_) { + | (Some(o), Some(v)) => Some(Assignment(o, v)) + | _ => None + }; + | "BlockNode" => + let block = r => r |> field("node", run); + let args = j |> field("blocks", array(block)) |> E.A.O.concatSomes; + Some(Blocks(args)); + | "FunctionAssignmentNode" => + let name = j |> field("name", string); + let args = j |> field("params", array(field("name", string))); + let expression = j |> field("expr", run); + expression + |> E.O.fmap(expression => + FunctionAssignment({name, args, expression}) + ); | n => Js.log3("Couldn't parse mathjs node", j, n); None; @@ -50,29 +77,37 @@ module MathJsonToMathJsAdt = { module MathAdtToDistDst = { open MathJsonToMathJsAdt; + let handleSymbol = sym => { + Ok(`Symbol(sym)); + }; + module MathAdtCleaner = { let transformWithSymbol = (f: float, s: string) => switch (s) { | "K" - | "k" => f *. 1000. + | "k" => Some(f *. 1000.) | "M" - | "m" => f *. 1000000. + | "m" => Some(f *. 1000000.) | "B" - | "b" => f *. 1000000000. + | "b" => Some(f *. 1000000000.) | "T" - | "t" => f *. 1000000000000. - | _ => f + | "t" => Some(f *. 1000000000000.) + | _ => None }; - let rec run = fun - | Fn({name: "multiply", args: [|Value(f), Symbol(s)|]}) => - Value(transformWithSymbol(f, s)) + | Fn({name: "multiply", args: [|Value(f), Symbol(s)|]}) as doNothing => + transformWithSymbol(f, s) + |> E.O.fmap(r => Value(r)) + |> E.O.default(doNothing) | Fn({name: "unaryMinus", args: [|Value(f)|]}) => Value((-1.0) *. f) | Fn({name, args}) => Fn({name, args: args |> E.A.fmap(run)}) | Array(args) => Array(args |> E.A.fmap(run)) | Symbol(s) => Symbol(s) | Value(v) => Value(v) + | Blocks(args) => Blocks(args |> E.A.fmap(run)) + | Assignment(a, b) => Assignment(a, run(b)) + | FunctionAssignment(a) => FunctionAssignment(a) | Object(v) => Object( v @@ -82,83 +117,31 @@ module MathAdtToDistDst = { ); }; - let normal: - array(arg) => result(ExpressionTypes.ExpressionTree.node, string) = - fun - | [|Value(mean), Value(stdev)|] => - Ok(`SymbolicDist(`Normal({mean, stdev}))) - | _ => Error("Wrong number of variables in normal distribution"); - - let lognormal: - array(arg) => result(ExpressionTypes.ExpressionTree.node, string) = - fun - | [|Value(mu), Value(sigma)|] => - Ok(`SymbolicDist(`Lognormal({mu, sigma}))) - | [|Object(o)|] => { - let g = Js.Dict.get(o); - switch (g("mean"), g("stdev"), g("mu"), g("sigma")) { - | (Some(Value(mean)), Some(Value(stdev)), _, _) => - Ok( - `SymbolicDist( - SymbolicDist.Lognormal.fromMeanAndStdev(mean, stdev), - ), - ) - | (_, _, Some(Value(mu)), Some(Value(sigma))) => - Ok(`SymbolicDist(`Lognormal({mu, sigma}))) - | _ => Error("Lognormal distribution would need mean and stdev") - }; - } - | _ => Error("Wrong number of variables in lognormal distribution"); - - let to_: array(arg) => result(ExpressionTypes.ExpressionTree.node, string) = - fun - | [|Value(low), Value(high)|] when low <= 0.0 && low < high => { - Ok(`SymbolicDist(SymbolicDist.Normal.from90PercentCI(low, high))); - } - | [|Value(low), Value(high)|] when low < high => { + let lognormal = (args, parseArgs, nodeParser) => + switch (args) { + | [|Object(o)|] => + let g = s => + Js.Dict.get(o, s) + |> E.O.toResult("Variable was empty") + |> E.R.bind(_, nodeParser); + switch (g("mean"), g("stdev"), g("mu"), g("sigma")) { + | (Ok(mean), Ok(stdev), _, _) => Ok( - `SymbolicDist(SymbolicDist.Lognormal.from90PercentCI(low, high)), - ); - } - | [|Value(_), Value(_)|] => - Error("Low value must be less than high value.") - | _ => Error("Wrong number of variables in lognormal distribution"); - - let uniform: - array(arg) => result(ExpressionTypes.ExpressionTree.node, string) = - fun - | [|Value(low), Value(high)|] => - Ok(`SymbolicDist(`Uniform({low, high}))) - | _ => Error("Wrong number of variables in lognormal distribution"); - - let beta: array(arg) => result(ExpressionTypes.ExpressionTree.node, string) = - fun - | [|Value(alpha), Value(beta)|] => - Ok(`SymbolicDist(`Beta({alpha, beta}))) - | _ => Error("Wrong number of variables in lognormal distribution"); - - let exponential: - array(arg) => result(ExpressionTypes.ExpressionTree.node, string) = - fun - | [|Value(rate)|] => Ok(`SymbolicDist(`Exponential({rate: rate}))) - | _ => Error("Wrong number of variables in Exponential distribution"); - - let cauchy: - array(arg) => result(ExpressionTypes.ExpressionTree.node, string) = - fun - | [|Value(local), Value(scale)|] => - Ok(`SymbolicDist(`Cauchy({local, scale}))) - | _ => Error("Wrong number of variables in cauchy distribution"); - - let triangular: - array(arg) => result(ExpressionTypes.ExpressionTree.node, string) = - fun - | [|Value(low), Value(medium), Value(high)|] - when low < medium && medium < high => - Ok(`SymbolicDist(`Triangular({low, medium, high}))) - | [|Value(_), Value(_), Value(_)|] => - Error("Triangular values must be increasing order") - | _ => Error("Wrong number of variables in triangle distribution"); + `FunctionCall(("lognormalFromMeanAndStdDev", [|mean, stdev|])), + ) + | (_, _, Ok(mu), Ok(sigma)) => + Ok(`FunctionCall(("lognormal", [|mu, sigma|]))) + | _ => + Error( + "Lognormal distribution needs either mean and stdev or mu and sigma", + ) + }; + | _ => + parseArgs() + |> E.R.fmap((args: array(ExpressionTypes.ExpressionTree.node)) => + `FunctionCall(("lognormal", args)) + ) + }; let multiModal = ( @@ -166,15 +149,6 @@ module MathAdtToDistDst = { weights: option(array(float)), ) => { let weights = weights |> E.O.default([||]); - - /*let dists: = - args - |> E.A.fmap( - fun - | Ok(a) => a - | Error(e) => Error(e) - );*/ - let firstWithError = args |> Belt.Array.getBy(_, Belt.Result.isError); let withoutErrors = args |> E.A.fmap(E.R.toOption) |> E.A.O.concatSomes; @@ -203,101 +177,75 @@ module MathAdtToDistDst = { }; }; - // let arrayParser = - // (args: array(arg)) - // : result(ExpressionTypes.ExpressionTree.node, string) => { - // let samples = - // args - // |> E.A.fmap( - // fun - // | Value(n) => Some(n) - // | _ => None, - // ) - // |> E.A.O.concatSomes; - // let outputs = Samples.T.fromSamples(samples); - // let pdf = - // outputs.shape |> E.O.bind(_, Shape.T.toContinuous); - // let shape = - // pdf - // |> E.O.fmap(pdf => { - // let _pdf = Continuous.T.normalize(pdf); - // let cdf = Continuous.T.integral(~cache=None, _pdf); - // SymbolicDist.ContinuousShape.make(_pdf, cdf); - // }); - // switch (shape) { - // | Some(s) => Ok(`SymbolicDist(`ContinuousShape(s))) - // | None => Error("Rendering did not work") - // }; - // }; - + // Error("Dotwise exponentiation needs two operands") let operationParser = ( name: string, - args: array(result(ExpressionTypes.ExpressionTree.node, string)), - ) => { + args: result(array(ExpressionTypes.ExpressionTree.node), string), + ):result(ExpressionTypes.ExpressionTree.node,string) => { let toOkAlgebraic = r => Ok(`AlgebraicCombination(r)); let toOkPointwise = r => Ok(`PointwiseCombination(r)); let toOkTruncate = r => Ok(`Truncate(r)); let toOkFloatFromDist = r => Ok(`FloatFromDist(r)); - switch (name, args) { - | ("add", [|Ok(l), Ok(r)|]) => toOkAlgebraic((`Add, l, r)) - | ("add", _) => Error("Addition needs two operands") - | ("subtract", [|Ok(l), Ok(r)|]) => toOkAlgebraic((`Subtract, l, r)) - | ("subtract", _) => Error("Subtraction needs two operands") - | ("multiply", [|Ok(l), Ok(r)|]) => toOkAlgebraic((`Multiply, l, r)) - | ("multiply", _) => Error("Multiplication needs two operands") - | ("dotMultiply", [|Ok(l), Ok(r)|]) => toOkPointwise((`Multiply, l, r)) - | ("dotMultiply", _) => - Error("Dotwise multiplication needs two operands") - | ("rightLogShift", [|Ok(l), Ok(r)|]) => toOkPointwise((`Add, l, r)) - | ("rightLogShift", _) => Error("Dotwise addition needs two operands") - | ("divide", [|Ok(l), Ok(r)|]) => toOkAlgebraic((`Divide, l, r)) - | ("divide", _) => Error("Division needs two operands") - | ("pow", _) => Error("Exponentiation is not yet supported.") - | ("leftTruncate", [|Ok(d), Ok(`SymbolicDist(`Float(lc)))|]) => - toOkTruncate((Some(lc), None, d)) - | ("leftTruncate", _) => - Error("leftTruncate needs two arguments: the expression and the cutoff") - | ("rightTruncate", [|Ok(d), Ok(`SymbolicDist(`Float(rc)))|]) => - toOkTruncate((None, Some(rc), d)) - | ("rightTruncate", _) => - Error( - "rightTruncate needs two arguments: the expression and the cutoff", - ) - | ( - "truncate", - [| - Ok(d), - Ok(`SymbolicDist(`Float(lc))), - Ok(`SymbolicDist(`Float(rc))), - |], - ) => - toOkTruncate((Some(lc), Some(rc), d)) - | ("truncate", _) => - Error("truncate needs three arguments: the expression and both cutoffs") - | ("pdf", [|Ok(d), Ok(`SymbolicDist(`Float(v)))|]) => - toOkFloatFromDist((`Pdf(v), d)) - | ("cdf", [|Ok(d), Ok(`SymbolicDist(`Float(v)))|]) => - toOkFloatFromDist((`Cdf(v), d)) - | ("inv", [|Ok(d), Ok(`SymbolicDist(`Float(v)))|]) => - toOkFloatFromDist((`Inv(v), d)) - | ("mean", [|Ok(d)|]) => toOkFloatFromDist((`Mean, d)) - | ("sample", [|Ok(d)|]) => toOkFloatFromDist((`Sample, d)) - | _ => Error("This type not currently supported") - }; + args + |> E.R.bind(_, args => { + switch (name, args) { + | ("add", [|l, r|]) => toOkAlgebraic((`Add, l, r)) + | ("add", _) => Error("Addition needs two operands") + | ("subtract", [|l, r|]) => toOkAlgebraic((`Subtract, l, r)) + | ("subtract", _) => Error("Subtraction needs two operands") + | ("multiply", [|l, r|]) => toOkAlgebraic((`Multiply, l, r)) + | ("multiply", _) => Error("Multiplication needs two operands") + | ("pow", [|l,r|]) => toOkAlgebraic((`Exponentiate, l, r)) + | ("pow", _) => Error("Exponentiation needs two operands") + | ("dotMultiply", [|l, r|]) => toOkPointwise((`Multiply, l, r)) + | ("dotMultiply", _) => + Error("Dotwise multiplication needs two operands") + | ("rightLogShift", [|l, r|]) => toOkPointwise((`Add, l, r)) + | ("rightLogShift", _) => + Error("Dotwise addition needs two operands") + | ("divide", [|l, r|]) => toOkAlgebraic((`Divide, l, r)) + | ("divide", _) => Error("Division needs two operands") + | ("leftTruncate", [|d, `SymbolicDist(`Float(lc))|]) => + toOkTruncate((Some(lc), None, d)) + | ("leftTruncate", _) => + Error( + "leftTruncate needs two arguments: the expression and the cutoff", + ) + | ("rightTruncate", [|d, `SymbolicDist(`Float(rc))|]) => + toOkTruncate((None, Some(rc), d)) + | ("rightTruncate", _) => + Error( + "rightTruncate needs two arguments: the expression and the cutoff", + ) + | ( + "truncate", + [|d, `SymbolicDist(`Float(lc)), `SymbolicDist(`Float(rc))|], + ) => + toOkTruncate((Some(lc), Some(rc), d)) + | ("truncate", _) => + Error( + "truncate needs three arguments: the expression and both cutoffs", + ) + | ("pdf", [|d, `SymbolicDist(`Float(v))|]) => + toOkFloatFromDist((`Pdf(v), d)) + | ("cdf", [|d, `SymbolicDist(`Float(v))|]) => + toOkFloatFromDist((`Cdf(v), d)) + | ("inv", [|d, `SymbolicDist(`Float(v))|]) => + toOkFloatFromDist((`Inv(v), d)) + | ("mean", [|d|]) => toOkFloatFromDist((`Mean, d)) + | ("sample", [|d|]) => toOkFloatFromDist((`Sample, d)) + | _ => Error("This type not currently supported") + } + }); }; let functionParser = (nodeParser, name, args) => { - let parseArgs = () => args |> E.A.fmap(nodeParser); + let parseArray = ags => + ags |> E.A.fmap(nodeParser) |> E.A.R.firstErrorOrOpen; + let parseArgs = () => parseArray(args); switch (name) { - | "normal" => normal(args) - | "lognormal" => lognormal(args) - | "uniform" => uniform(args) - | "beta" => beta(args) - | "to" => to_(args) - | "exponential" => exponential(args) - | "cauchy" => cauchy(args) - | "triangular" => triangular(args) + | "lognormal" => lognormal(args, parseArgs, nodeParser) | "mm" => let weights = args @@ -338,27 +286,55 @@ module MathAdtToDistDst = { | "sample" | "cdf" | "pdf" => operationParser(name, parseArgs()) - | n => Error(n ++ "(...) is not currently supported") + | name => + parseArgs() + |> E.R.fmap((args: array(ExpressionTypes.ExpressionTree.node)) => + `FunctionCall((name, args)) + ) }; }; - let rec nodeParser = + let rec nodeParser: + MathJsonToMathJsAdt.arg => + result(ExpressionTypes.ExpressionTree.node, string) = fun | Value(f) => Ok(`SymbolicDist(`Float(f))) + | Symbol(sym) => Ok(`Symbol(sym)) | Fn({name, args}) => functionParser(nodeParser, name, args) | _ => { - Error("This type not currently supported"); + Error("This type not currently supported") }; - let topLevel = - fun - | Value(_) as r => nodeParser(r) - | Fn(_) as r => nodeParser(r) + // | FunctionAssignment({name, args, expression}) => { + // let evaluatedExpression = run(expression); + // `Function(_ => Ok(evaluatedExpression)); + // } + let rec topLevel = (r): result(ExpressionTypes.Program.program, string) => + switch (r) { + | FunctionAssignment({name, args, expression}) => + switch (nodeParser(expression)) { + | Ok(r) => Ok([|`Assignment((name, `Function((args, r))))|]) + | Error(r) => Error(r) + } + | Value(_) as r => nodeParser(r) |> E.R.fmap(r => [|`Expression(r)|]) + | Fn(_) as r => nodeParser(r) |> E.R.fmap(r => [|`Expression(r)|]) | Array(_) => Error("Array not valid as top level") - | Symbol(_) => Error("Symbol not valid as top level") - | Object(_) => Error("Object not valid as top level"); + | Symbol(s) => handleSymbol(s) |> E.R.fmap(r => [|`Expression(r)|]) + | Object(_) => Error("Object not valid as top level") + | Assignment(name, value) => + switch (name) { + | Symbol(symbol) => + nodeParser(value) |> E.R.fmap(r => [|`Assignment((symbol, r))|]) + | _ => Error("Symbol not a string") + } + | Blocks(blocks) => + blocks + |> E.A.fmap(b => topLevel(b)) + |> E.A.R.firstErrorOrOpen + |> E.R.fmap(E.A.concatMany) + }; - let run = (r): result(ExpressionTypes.ExpressionTree.node, string) => + let run = (r): result(ExpressionTypes.Program.program, string) => r |> MathAdtCleaner.run |> topLevel; }; @@ -369,7 +345,7 @@ module MathAdtToDistDst = { */ let pointwiseToRightLogShift = Js.String.replaceByRe([%re "/\.\+/g"], ">>>"); -let fromString = str => { +let fromString2 = str => { /* We feed the user-typed string into Mathjs.parseMath, which returns a JSON with (hopefully) a single-element array. This array element is the top-level node of a nested-object tree @@ -390,3 +366,7 @@ let fromString = str => { let value = E.R.bind(mathJsParse, MathAdtToDistDst.run); value; }; + +let fromString = str => { + fromString2(str); +}; diff --git a/src/distPlus/expressionTree/Operation.re b/src/distPlus/expressionTree/Operation.re index 26826f3f..e415b5de 100644 --- a/src/distPlus/expressionTree/Operation.re +++ b/src/distPlus/expressionTree/Operation.re @@ -7,6 +7,7 @@ module Algebraic = { | `Add => (+.) | `Subtract => (-.) | `Multiply => ( *. ) + | `Exponentiate => ( ** ) | `Divide => (/.); let applyFn = (t, f1, f2) => { @@ -21,6 +22,7 @@ module Algebraic = { | `Add => "+" | `Subtract => "-" | `Multiply => "*" + | `Exponentiate => ( "**" ) | `Divide => "/"; let format = (a, b, c) => b ++ " " ++ toString(a) ++ " " ++ c; diff --git a/src/distPlus/expressionTree/Program.re b/src/distPlus/expressionTree/Program.re new file mode 100644 index 00000000..7adfb3d8 --- /dev/null +++ b/src/distPlus/expressionTree/Program.re @@ -0,0 +1,5 @@ +type t = ExpressionTypes.Program.program; + +let last = (r:t) => E.A.last(r) |> E.O.toResult("No rendered lines"); +// let run = (p:program) => p |> E.A.last |> E.O.fmap(r => +// ) \ No newline at end of file diff --git a/src/distPlus/expressionTree/SamplingDistribution.re b/src/distPlus/expressionTree/SamplingDistribution.re index 2f95be1e..de9ef58b 100644 --- a/src/distPlus/expressionTree/SamplingDistribution.re +++ b/src/distPlus/expressionTree/SamplingDistribution.re @@ -60,17 +60,11 @@ let combineShapesUsingSampling = let shape = samples |> E.O.fmap( - Samples.T.fromSamples( - ~samplingInputs={ - sampleCount: - Some(evaluationParams.samplingInputs.sampleCount), - outputXYPoints: - Some(evaluationParams.samplingInputs.outputXYPoints), - kernelWidth: evaluationParams.samplingInputs.kernelWidth, - }, + SamplesToShape.fromSamples( + ~samplingInputs=evaluationParams.samplingInputs, ), ) - |> E.O.bind(_, (r) => r.shape) + |> E.O.bind(_, r => r.shape) |> E.O.toResult("No response"); shape |> E.R.fmap(r => `Normalize(`RenderedDist(r))); }, diff --git a/src/distPlus/renderers/DistPlusRenderer.re b/src/distPlus/renderers/DistPlusRenderer.re index 1d06eb4a..7c39e17c 100644 --- a/src/distPlus/renderers/DistPlusRenderer.re +++ b/src/distPlus/renderers/DistPlusRenderer.re @@ -1,23 +1,147 @@ -let run = (inputs: RenderTypes.DistPlusRenderer.inputs) => { - let toDist = shape => +// TODO: This setup is more confusing than it should be, there's more work to do in cleanup here. +module Inputs = { + module SamplingInputs = { + type t = { + sampleCount: option(int), + outputXYPoints: option(int), + kernelWidth: option(float), + shapeLength: option(int), + }; + }; + let defaultRecommendedLength = 10000; + let defaultShouldDownsample = true; + + type ingredients = { + guesstimatorString: string, + domain: DistTypes.domain, + unit: DistTypes.distributionUnit, + }; + module Ingredients = { + type t = ingredients; + let make = + ( + ~guesstimatorString, + ~domain=DistTypes.Complete, + ~unit=DistTypes.UnspecifiedDistribution, + (), + ) + : t => { + guesstimatorString, + domain, + unit, + }; + }; + + type inputs = { + distPlusIngredients: ingredients, + samplingInputs: SamplingInputs.t, + environment: ExpressionTypes.ExpressionTree.environment + }; + + let empty: SamplingInputs.t = { + sampleCount: None, + outputXYPoints: None, + kernelWidth: None, + shapeLength: None, + }; + + let make = + ( + ~samplingInputs=empty, + ~distPlusIngredients, + ~environment=ExpressionTypes.ExpressionTree.Environment.empty, + (), + ) + : inputs => { + distPlusIngredients, + samplingInputs, + environment, + }; +}; + +module Internals = { + type inputs = { + samplingInputs: Inputs.SamplingInputs.t, + guesstimatorString: string, + environment: ExpressionTypes.ExpressionTree.environment, + }; + + let addVariable = + ( + {samplingInputs, guesstimatorString, environment}: inputs, + str, + node, + ) + : inputs => { + samplingInputs, + guesstimatorString, + environment: + ExpressionTypes.ExpressionTree.Environment.update(environment, str, _ => Some(node)) + }; + + let distPlusRenderInputsToInputs = (inputs: Inputs.inputs): inputs => { + { + samplingInputs: inputs.samplingInputs, + guesstimatorString: inputs.distPlusIngredients.guesstimatorString, + environment: inputs.environment, + }; + }; + + type outputs = { + graph: ExpressionTypes.ExpressionTree.node, + shape: DistTypes.shape, + }; + let makeOutputs = (graph, shape): outputs => {graph, shape}; + + let runNode = (inputs, node) => { + ExpressionTree.toShape( + { + sampleCount: inputs.samplingInputs.sampleCount |> E.O.default(10000), + outputXYPoints: + inputs.samplingInputs.outputXYPoints |> E.O.default(10000), + kernelWidth: inputs.samplingInputs.kernelWidth, + shapeLength: inputs.samplingInputs.shapeLength |> E.O.default(10000), + }, + inputs.environment, + node, + ); + }; + + let runProgram = (inputs: inputs, p: ExpressionTypes.Program.program) => { + let ins = ref(inputs); + p + |> E.A.fmap(statement => + switch (statement) { + | `Assignment(name, node) => + ins := addVariable(ins^, name, node); + None; + | `Expression(node) => Some(runNode(ins^, node)) + } + ) + |> E.A.O.concatSomes + |> E.A.R.firstErrorOrOpen; + }; + + let inputsToShape = (inputs: inputs) => { + MathJsParser.fromString(inputs.guesstimatorString) + |> E.R.bind(_, g => runProgram(inputs, g)) + |> E.R.bind(_, r => E.A.last(r) |> E.O.toResult("No rendered lines") |> E.R.fmap(Shape.T.normalize)); + }; + + let outputToDistPlus = (inputs: Inputs.inputs, shape: DistTypes.shape) => { DistPlus.make( ~shape, ~domain=inputs.distPlusIngredients.domain, ~unit=inputs.distPlusIngredients.unit, ~guesstimatorString=Some(inputs.distPlusIngredients.guesstimatorString), (), - ) - |> DistPlus.T.normalize; - let output = - ShapeRenderer.run({ - samplingInputs: inputs.samplingInputs, - guesstimatorString: inputs.distPlusIngredients.guesstimatorString, - symbolicInputs: { - length: inputs.recommendedLength, - }, - }); - output - |> E.R.fmap((o: RenderTypes.ShapeRenderer.Symbolic.outputs) => - toDist(o.shape) - ); + ); + }; +}; + +let run = (inputs: Inputs.inputs) => { + inputs + |> Internals.distPlusRenderInputsToInputs + |> Internals.inputsToShape + |> E.R.fmap(Internals.outputToDistPlus(inputs)); }; diff --git a/src/distPlus/renderers/ProgramRunner.re b/src/distPlus/renderers/ProgramRunner.re new file mode 100644 index 00000000..e69de29b diff --git a/src/distPlus/renderers/RenderTypes.re b/src/distPlus/renderers/RenderTypes.re deleted file mode 100644 index 9b37503f..00000000 --- a/src/distPlus/renderers/RenderTypes.re +++ /dev/null @@ -1,127 +0,0 @@ -module ShapeRenderer = { - module Sampling = { - type inputs = { - sampleCount: option(int), - outputXYPoints: option(int), - kernelWidth: option(float), - }; - type samplingStats = { - sampleCount: int, - outputXYPoints: int, - bandwidthXSuggested: float, - bandwidthUnitSuggested: float, - bandwidthXImplemented: float, - bandwidthUnitImplemented: float, - }; - type outputs = { - continuousParseParams: option(samplingStats), - shape: option(DistTypes.shape), - }; - module Inputs = { - let defaultSampleCount = 5000; - let defaultOutputXYPoints = 10000; - let empty = { - sampleCount: None, - outputXYPoints: None, - kernelWidth: None, - }; - - type fInputs = { - sampleCount: int, - outputXYPoints: int, - kernelWidth: option(float), - }; - let toF = (i: inputs): fInputs => { - sampleCount: i.sampleCount |> E.O.default(defaultSampleCount), - outputXYPoints: - i.outputXYPoints |> E.O.default(defaultOutputXYPoints), - kernelWidth: i.kernelWidth, - }; - }; - }; - - module Symbolic = { - type inputs = {length: int}; - type outputs = { - graph: ExpressionTypes.ExpressionTree.node, - shape: DistTypes.shape, - }; - let make = (graph, shape) => {graph, shape}; - }; - - module Combined = { - type inputs = { - samplingInputs: Sampling.inputs, - symbolicInputs: Symbolic.inputs, - guesstimatorString: string, - }; - type outputs = { - symbolic: option(Belt.Result.t(Symbolic.outputs, string)), - sampling: option(Sampling.outputs), - }; - let methodUsed = ({symbolic, sampling}:outputs) => switch(symbolic, sampling){ - | (Some(Ok(_)), _) => `Symbolic - | (_, Some({shape: Some(_)})) => `Sampling - | _ => `None - } - let getShape = (r: outputs) => - switch (r.symbolic, r.sampling) { - | (Some(Ok({shape})), _) => Some(shape) - | (_, Some({shape})) => shape - | _ => None - }; - }; -}; - -module DistPlusRenderer = { - let defaultRecommendedLength = 10000; - let defaultShouldDownsample = true; - type ingredients = { - guesstimatorString: string, - domain: DistTypes.domain, - unit: DistTypes.distributionUnit, - }; - type inputs = { - distPlusIngredients: ingredients, - samplingInputs: ShapeRenderer.Sampling.inputs, - recommendedLength: int, - shouldDownsample: bool, - }; - module Ingredients = { - let make = - ( - ~guesstimatorString, - ~domain=DistTypes.Complete, - ~unit=DistTypes.UnspecifiedDistribution, - (), - ) - : ingredients => { - guesstimatorString, - domain, - unit, - }; - }; - let make = - ( - ~samplingInputs=ShapeRenderer.Sampling.Inputs.empty, - ~recommendedLength=defaultRecommendedLength, - ~shouldDownsample=defaultShouldDownsample, - ~distPlusIngredients, - (), - ) - : inputs => { - distPlusIngredients, - samplingInputs, - recommendedLength, - shouldDownsample, - }; - type outputs = { - shapeRenderOutputs: ShapeRenderer.Combined.outputs, - distPlus: option(DistTypes.distPlus) - } - module Outputs = { - let distplus = (t:outputs) => t.distPlus - let shapeRenderOutputs = (t:outputs) => t.shapeRenderOutputs - let make = (shapeRenderOutputs, distPlus) => {shapeRenderOutputs, distPlus}; - } -}; diff --git a/src/distPlus/renderers/ShapeRenderer.re b/src/distPlus/renderers/ShapeRenderer.re deleted file mode 100644 index c9ac37c7..00000000 --- a/src/distPlus/renderers/ShapeRenderer.re +++ /dev/null @@ -1,39 +0,0 @@ -// This transforms an array intersperced with spaces or newlines with a normally formatted one. -// "3 4 5 3 2 1 " -> "[3,4,5,3,2,1]"" -let formatMessyArray = str => { - let split = Js.String.splitByRe([%re "/\\n|\\r|\\s/"], str); - if (E.A.length(split) > 20) { - let inner = split |> Js.Array.joinWith(","); - {j|[$inner]|j}; - } else { - str; - }; -}; - -let formatString = str => { - str |> formatMessyArray; -}; - -let runSymbolic = (inputs: RenderTypes.ShapeRenderer.Combined.inputs) => { - let str = formatString(inputs.guesstimatorString); - let graph = MathJsParser.fromString(str); - graph - |> E.R.bind(_, g => - ExpressionTree.toShape( - inputs.symbolicInputs.length, - { - sampleCount: - inputs.samplingInputs.sampleCount |> E.O.default(10000), - outputXYPoints: - inputs.samplingInputs.outputXYPoints |> E.O.default(10000), - kernelWidth: inputs.samplingInputs.kernelWidth, - }, - g, - ) - |> E.R.fmap(RenderTypes.ShapeRenderer.Symbolic.make(g)) - ); -}; - -let run = (inputs: RenderTypes.ShapeRenderer.Combined.inputs) => { - runSymbolic(inputs); -}; diff --git a/src/distPlus/renderers/samplesRenderer/Samples.re b/src/distPlus/renderers/samplesRenderer/Samples.re deleted file mode 100644 index 7f9b10aa..00000000 --- a/src/distPlus/renderers/samplesRenderer/Samples.re +++ /dev/null @@ -1,185 +0,0 @@ -module Types = { - let defaultSampleCount = 5000; - let defaultOutputXYPoints = 10000; - - type inputs = { - sampleCount: option(int), - outputXYPoints: option(int), - kernelWidth: option(float), - }; - - type samplingStats = { - sampleCount: int, - outputXYPoints: int, - bandwidthXSuggested: float, - bandwidthUnitSuggested: float, - bandwidthXImplemented: float, - bandwidthUnitImplemented: float, - }; - - type outputs = { - continuousParseParams: option(samplingStats), - shape: option(DistTypes.shape), - }; - - let empty = {sampleCount: None, outputXYPoints: None, kernelWidth: None}; - - type fInputs = { - sampleCount: int, - outputXYPoints: int, - kernelWidth: option(float), - }; - - let toF = (i: inputs): fInputs => { - sampleCount: i.sampleCount |> E.O.default(defaultSampleCount), - outputXYPoints: i.outputXYPoints |> E.O.default(defaultOutputXYPoints), - kernelWidth: i.kernelWidth, - }; -}; - -module JS = { - [@bs.deriving abstract] - type distJs = { - xs: array(float), - ys: array(float), - }; - - let jsToDist = (d: distJs): DistTypes.xyShape => { - xs: xsGet(d), - ys: ysGet(d), - }; - - [@bs.module "./KdeLibrary.js"] - external samplesToContinuousPdf: (array(float), int, int) => distJs = - "samplesToContinuousPdf"; -}; - -module KDE = { - let normalSampling = (samples, outputXYPoints, kernelWidth) => { - samples - |> JS.samplesToContinuousPdf(_, outputXYPoints, kernelWidth) - |> JS.jsToDist; - }; -}; - -module T = { - type t = array(float); - - let splitContinuousAndDiscrete = (sortedArray: t) => { - let continuous = [||]; - let discrete = E.FloatFloatMap.empty(); - Belt.Array.forEachWithIndex( - sortedArray, - (index, element) => { - let maxIndex = (sortedArray |> Array.length) - 1; - let possiblySimilarElements = - ( - switch (index) { - | 0 => [|index + 1|] - | n when n == maxIndex => [|index - 1|] - | _ => [|index - 1, index + 1|] - } - ) - |> Belt.Array.map(_, r => sortedArray[r]); - let hasSimilarElement = - Belt.Array.some(possiblySimilarElements, r => r == element); - hasSimilarElement - ? E.FloatFloatMap.increment(element, discrete) - : { - let _ = Js.Array.push(element, continuous); - (); - }; - (); - }, - ); - (continuous, discrete); - }; - - let xWidthToUnitWidth = (samples, outputXYPoints, xWidth) => { - let xyPointRange = E.A.Sorted.range(samples) |> E.O.default(0.0); - let xyPointWidth = xyPointRange /. float_of_int(outputXYPoints); - xWidth /. xyPointWidth; - }; - - let formatUnitWidth = w => Jstat.max([|w, 1.0|]) |> int_of_float; - - let suggestedUnitWidth = (samples, outputXYPoints) => { - let suggestedXWidth = Bandwidth.nrd0(samples); - xWidthToUnitWidth(samples, outputXYPoints, suggestedXWidth); - }; - - let kde = (~samples, ~outputXYPoints, width) => { - KDE.normalSampling(samples, outputXYPoints, width); - }; - - let toShape = - ( - ~samples: t, - ~samplingInputs: Types.fInputs, - (), - ) => { - Array.fast_sort(compare, samples); - let (continuousPart, discretePart) = E.A.Sorted.Floats.split(samples); - let length = samples |> E.A.length |> float_of_int; - let discrete: DistTypes.discreteShape = - discretePart - |> E.FloatFloatMap.fmap(r => r /. length) - |> E.FloatFloatMap.toArray - |> XYShape.T.fromZippedArray - |> Discrete.make; - - let pdf = - continuousPart |> E.A.length > 5 - ? { - let _suggestedXWidth = Bandwidth.nrd0(continuousPart); - // todo: This does some recalculating from the last step. - let _suggestedUnitWidth = - suggestedUnitWidth(continuousPart, samplingInputs.outputXYPoints); - let usedWidth = - samplingInputs.kernelWidth |> E.O.default(_suggestedXWidth); - let usedUnitWidth = - xWidthToUnitWidth( - samples, - samplingInputs.outputXYPoints, - usedWidth, - ); - let foo: Types.samplingStats = { - sampleCount: samplingInputs.sampleCount, - outputXYPoints: samplingInputs.outputXYPoints, - bandwidthXSuggested: _suggestedXWidth, - bandwidthUnitSuggested: _suggestedUnitWidth, - bandwidthXImplemented: usedWidth, - bandwidthUnitImplemented: usedUnitWidth, - }; - continuousPart - |> kde( - ~samples=_, - ~outputXYPoints=samplingInputs.outputXYPoints, - formatUnitWidth(usedUnitWidth), - ) - |> Continuous.make - |> (r => Some((r, foo))); - } - : None; - let shape = - MixedShapeBuilder.buildSimple( - ~continuous=pdf |> E.O.fmap(fst), - ~discrete=Some(discrete), - ); - let samplesParse: Types.outputs = { - continuousParseParams: pdf |> E.O.fmap(snd), - shape, - }; - samplesParse; - }; - - let fromSamples = - ( - ~samplingInputs=Types.empty, - samples, - ) => { - let samplingInputs = - Types.toF(samplingInputs); - toShape(~samples, ~samplingInputs, ()); - }; -}; diff --git a/src/distPlus/renderers/samplesRenderer/SamplesToShape.re b/src/distPlus/renderers/samplesRenderer/SamplesToShape.re new file mode 100644 index 00000000..7b3c231f --- /dev/null +++ b/src/distPlus/renderers/samplesRenderer/SamplesToShape.re @@ -0,0 +1,164 @@ +module Internals = { + module Types = { + type samplingStats = { + sampleCount: int, + outputXYPoints: int, + bandwidthXSuggested: float, + bandwidthUnitSuggested: float, + bandwidthXImplemented: float, + bandwidthUnitImplemented: float, + }; + + type outputs = { + continuousParseParams: option(samplingStats), + shape: option(DistTypes.shape), + }; + }; + + module JS = { + [@bs.deriving abstract] + type distJs = { + xs: array(float), + ys: array(float), + }; + + let jsToDist = (d: distJs): DistTypes.xyShape => { + xs: xsGet(d), + ys: ysGet(d), + }; + + [@bs.module "./KdeLibrary.js"] + external samplesToContinuousPdf: (array(float), int, int) => distJs = + "samplesToContinuousPdf"; + }; + + module KDE = { + let normalSampling = (samples, outputXYPoints, kernelWidth) => { + samples + |> JS.samplesToContinuousPdf(_, outputXYPoints, kernelWidth) + |> JS.jsToDist; + }; + }; + + module T = { + type t = array(float); + + let splitContinuousAndDiscrete = (sortedArray: t) => { + let continuous = [||]; + let discrete = E.FloatFloatMap.empty(); + Belt.Array.forEachWithIndex( + sortedArray, + (index, element) => { + let maxIndex = (sortedArray |> Array.length) - 1; + let possiblySimilarElements = + ( + switch (index) { + | 0 => [|index + 1|] + | n when n == maxIndex => [|index - 1|] + | _ => [|index - 1, index + 1|] + } + ) + |> Belt.Array.map(_, r => sortedArray[r]); + let hasSimilarElement = + Belt.Array.some(possiblySimilarElements, r => r == element); + hasSimilarElement + ? E.FloatFloatMap.increment(element, discrete) + : { + let _ = Js.Array.push(element, continuous); + (); + }; + (); + }, + ); + (continuous, discrete); + }; + + let xWidthToUnitWidth = (samples, outputXYPoints, xWidth) => { + let xyPointRange = E.A.Sorted.range(samples) |> E.O.default(0.0); + let xyPointWidth = xyPointRange /. float_of_int(outputXYPoints); + xWidth /. xyPointWidth; + }; + + let formatUnitWidth = w => Jstat.max([|w, 1.0|]) |> int_of_float; + + let suggestedUnitWidth = (samples, outputXYPoints) => { + let suggestedXWidth = Bandwidth.nrd0(samples); + xWidthToUnitWidth(samples, outputXYPoints, suggestedXWidth); + }; + + let kde = (~samples, ~outputXYPoints, width) => { + KDE.normalSampling(samples, outputXYPoints, width); + }; + }; +}; + +let toShape = + ( + ~samples: Internals.T.t, + ~samplingInputs: ExpressionTypes.ExpressionTree.samplingInputs, + (), + ) => { + Array.fast_sort(compare, samples); + let (continuousPart, discretePart) = E.A.Sorted.Floats.split(samples); + let length = samples |> E.A.length |> float_of_int; + let discrete: DistTypes.discreteShape = + discretePart + |> E.FloatFloatMap.fmap(r => r /. length) + |> E.FloatFloatMap.toArray + |> XYShape.T.fromZippedArray + |> Discrete.make; + + let pdf = + continuousPart |> E.A.length > 5 + ? { + let _suggestedXWidth = Bandwidth.nrd0(continuousPart); + // todo: This does some recalculating from the last step. + let _suggestedUnitWidth = + Internals.T.suggestedUnitWidth( + continuousPart, + samplingInputs.outputXYPoints, + ); + let usedWidth = + samplingInputs.kernelWidth |> E.O.default(_suggestedXWidth); + let usedUnitWidth = + Internals.T.xWidthToUnitWidth( + samples, + samplingInputs.outputXYPoints, + usedWidth, + ); + let samplingStats: Internals.Types.samplingStats = { + sampleCount: samplingInputs.sampleCount, + outputXYPoints: samplingInputs.outputXYPoints, + bandwidthXSuggested: _suggestedXWidth, + bandwidthUnitSuggested: _suggestedUnitWidth, + bandwidthXImplemented: usedWidth, + bandwidthUnitImplemented: usedUnitWidth, + }; + continuousPart + |> Internals.T.kde( + ~samples=_, + ~outputXYPoints=samplingInputs.outputXYPoints, + Internals.T.formatUnitWidth(usedUnitWidth), + ) + |> Continuous.make + |> (r => Some((r, samplingStats))); + } + : None; + + let shape = + MixedShapeBuilder.buildSimple( + ~continuous=pdf |> E.O.fmap(fst), + ~discrete=Some(discrete), + ); + + let samplesParse: Internals.Types.outputs = { + continuousParseParams: pdf |> E.O.fmap(snd), + shape, + }; + + samplesParse; +}; + +let fromSamples = (~samplingInputs, samples) => { + toShape(~samples, ~samplingInputs, ()); +}; diff --git a/src/distPlus/symbolic/SymbolicDist.re b/src/distPlus/symbolic/SymbolicDist.re index efa4e49b..33942187 100644 --- a/src/distPlus/symbolic/SymbolicDist.re +++ b/src/distPlus/symbolic/SymbolicDist.re @@ -2,6 +2,12 @@ open SymbolicTypes; module Exponential = { type t = exponential; + let make = (rate:float): symbolicDist => + `Exponential( + { + rate:rate + }, + ); let pdf = (x, t: t) => Jstat.exponential##pdf(x, t.rate); let cdf = (x, t: t) => Jstat.exponential##cdf(x, t.rate); let inv = (p, t: t) => Jstat.exponential##inv(p, t.rate); @@ -12,6 +18,7 @@ module Exponential = { module Cauchy = { type t = cauchy; + let make = (local, scale): symbolicDist => `Cauchy({local, scale}); let pdf = (x, t: t) => Jstat.cauchy##pdf(x, t.local, t.scale); let cdf = (x, t: t) => Jstat.cauchy##cdf(x, t.local, t.scale); let inv = (p, t: t) => Jstat.cauchy##inv(p, t.local, t.scale); @@ -22,6 +29,10 @@ module Cauchy = { module Triangular = { type t = triangular; + let make = (low, medium, high): result(symbolicDist, string) => + low < medium && medium < high + ? Ok(`Triangular({low, medium, high})) + : Error("Triangular values must be increasing order"); let pdf = (x, t: t) => Jstat.triangular##pdf(x, t.low, t.high, t.medium); let cdf = (x, t: t) => Jstat.triangular##cdf(x, t.low, t.high, t.medium); let inv = (p, t: t) => Jstat.triangular##inv(p, t.low, t.high, t.medium); @@ -32,6 +43,7 @@ module Triangular = { module Normal = { type t = normal; + let make = (mean, stdev): symbolicDist => `Normal({mean, stdev}); let pdf = (x, t: t) => Jstat.normal##pdf(x, t.mean, t.stdev); let cdf = (x, t: t) => Jstat.normal##cdf(x, t.mean, t.stdev); @@ -75,6 +87,7 @@ module Normal = { module Beta = { type t = beta; + let make = (alpha, beta) => `Beta({alpha, beta}); let pdf = (x, t: t) => Jstat.beta##pdf(x, t.alpha, t.beta); let cdf = (x, t: t) => Jstat.beta##cdf(x, t.alpha, t.beta); let inv = (p, t: t) => Jstat.beta##inv(p, t.alpha, t.beta); @@ -85,6 +98,7 @@ module Beta = { module Lognormal = { type t = lognormal; + let make = (mu, sigma) => `Lognormal({mu, sigma}); 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); @@ -131,6 +145,7 @@ module Lognormal = { module Uniform = { type t = uniform; + let make = (low, high) => `Uniform({low, 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 inv = (p, t: t) => Jstat.uniform##inv(p, t.low, t.high); @@ -146,6 +161,7 @@ module Uniform = { module Float = { type t = float; + let make = t => `Float(t); let pdf = (x, t: t) => x == t ? 1.0 : 0.0; let cdf = (x, t: t) => x >= t ? 1.0 : 0.0; let inv = (p, t: t) => p < t ? 0.0 : 1.0; @@ -317,13 +333,14 @@ module T = { switch (d) { | `Float(v) => Discrete( - Discrete.make(~integralSumCache=Some(1.0), {xs: [|v|], ys: [|1.0|]}), + Discrete.make( + ~integralSumCache=Some(1.0), + {xs: [|v|], ys: [|1.0|]}, + ), ) | _ => let xs = interpolateXs(~xSelection=`ByWeight, d, sampleCount); let ys = xs |> E.A.fmap(x => pdf(x, d)); - Continuous( - Continuous.make(~integralSumCache=Some(1.0), {xs, ys}), - ); + Continuous(Continuous.make(~integralSumCache=Some(1.0), {xs, ys})); }; }; diff --git a/src/interface/FormBuilder.re b/src/interface/FormBuilder.re index ea06e318..428d5556 100644 --- a/src/interface/FormBuilder.re +++ b/src/interface/FormBuilder.re @@ -17,12 +17,10 @@ let propValue = (t: Prop.Value.t) => { switch (t) { | SelectSingle(r) => r |> ReasonReact.string | ConditionalArray(r) => "Array" |> ReasonReact.string - | DistPlusIngredients((r: RenderTypes.DistPlusRenderer.ingredients)) => + | DistPlusIngredients((r: DistPlusRenderer.Inputs.ingredients)) => let newDistribution = - RenderTypes.DistPlusRenderer.make( + DistPlusRenderer.Inputs.make( ~distPlusIngredients=r, - ~recommendedLength=10000, - ~shouldDownsample=true, (), ) |> DistPlusRenderer.run; diff --git a/src/interface/Prop.re b/src/interface/Prop.re index c956da04..e20a1f8c 100644 --- a/src/interface/Prop.re +++ b/src/interface/Prop.re @@ -9,7 +9,7 @@ module Value = { | DateTime(MomentRe.Moment.t) | FloatPoint(float) | Probability(float) - | DistPlusIngredients(RenderTypes.DistPlusRenderer.ingredients) + | DistPlusIngredients(DistPlusRenderer.Inputs.ingredients) | ConditionalArray(array(conditional)) | FloatCdf(string); @@ -85,7 +85,7 @@ module ValueCluster = { [ | `combination(range(MomentRe.Moment.t)) | `item(string)], ) | Probability([ | `item(string)]) - | DistPlusIngredients([ | `item(RenderTypes.DistPlusRenderer.ingredients)]) + | DistPlusIngredients([ | `item(DistPlusRenderer.Inputs.ingredients)]) | ConditionalArray([ | `item(array(conditional))]) | FloatCdf([ | `item(string)]); }; diff --git a/src/models/EAFunds.re b/src/models/EAFunds.re index 215bfe2a..c4dd6dd1 100644 --- a/src/models/EAFunds.re +++ b/src/models/EAFunds.re @@ -110,7 +110,7 @@ module Model = { // TODO: Fixe number that integral is calculated for let getGlobalCatastropheChance = dateTime => { GlobalCatastrophe.makeI(MomentRe.momentNow()) - |> RenderTypes.DistPlusRenderer.make(~distPlusIngredients=_, ()) + |> DistPlusRenderer.Inputs.make(~distPlusIngredients=_, ()) |> DistPlusRenderer.run |> E.R.bind(_, r => r @@ -157,7 +157,7 @@ module Model = { }; let distPlusIngredients = - RenderTypes.DistPlusRenderer.Ingredients.make( + DistPlusRenderer.Inputs.Ingredients.make( ~guesstimatorString=str, ~domain=Complete, ~unit=UnspecifiedDistribution, @@ -167,7 +167,7 @@ module Model = { | CHANCE_OF_EXISTENCE => Prop.Value.DistPlusIngredients( - RenderTypes.DistPlusRenderer.Ingredients.make( + DistPlusRenderer.Inputs.Ingredients.make( ~guesstimatorString= GuesstimatorDist.min( GlobalCatastrophe.guesstimatorString, diff --git a/src/models/GlobalCatastrophe.re b/src/models/GlobalCatastrophe.re index e01422b3..9f675267 100644 --- a/src/models/GlobalCatastrophe.re +++ b/src/models/GlobalCatastrophe.re @@ -1,7 +1,7 @@ let guesstimatorString = "uniform(1, 100)"; let makeI = (currentDateTime: MomentRe.Moment.t) => { - RenderTypes.DistPlusRenderer.Ingredients.make( + DistPlusRenderer.Inputs.Ingredients.make( ~guesstimatorString, ~unit=TimeDistribution({zero: currentDateTime, unit: `years}), ~domain=RightLimited({xPoint: 300.0, excludingProbabilityMass: 0.3}), diff --git a/src/models/Human.re b/src/models/Human.re index 9ff30a77..2439adcc 100644 --- a/src/models/Human.re +++ b/src/models/Human.re @@ -2,7 +2,7 @@ let guesstimatorString = age => GuesstimatorDist.normal(72.0 -. age, 5.0 -. age *. 0.01); let makeI = (age: float) => { - RenderTypes.DistPlusRenderer.Ingredients.make( + DistPlusRenderer.Inputs.Ingredients.make( ~guesstimatorString=guesstimatorString(age), ~unit=TimeDistribution({zero: MomentRe.momentNow(), unit: `years}), ~domain=RightLimited({xPoint: 300.0, excludingProbabilityMass: 0.3}),