From 102a147b974284f6d4b0637562f425233ae12909 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Thu, 23 Jul 2020 15:59:46 +0100 Subject: [PATCH] First attempt at showing the use of a simple variable --- src/components/DistBuilder.re | 37 +++++++++++--- src/components/charts/DistPlusPlotReducer.re | 3 +- src/distPlus/distribution/Continuous.re | 1 + src/distPlus/expressionTree/MathJsParser.re | 52 +++++++++++++------- src/distPlus/renderers/DistPlusRenderer.re | 3 ++ src/distPlus/renderers/RenderTypes.re | 26 +++++++--- src/distPlus/renderers/ShapeRenderer.re | 8 +-- src/distPlus/symbolic/SymbolicDist.re | 1 + 8 files changed, 90 insertions(+), 41 deletions(-) diff --git a/src/components/DistBuilder.re b/src/components/DistBuilder.re index 2a28a5a6..7568e98e 100644 --- a/src/components/DistBuilder.re +++ b/src/components/DistBuilder.re @@ -133,7 +133,7 @@ module DemoDist = { ~unit, (), ); - let inputs = + let inputs1 = RenderTypes.DistPlusRenderer.make( ~samplingInputs={ sampleCount: Some(options.sampleCount), @@ -143,14 +143,37 @@ module DemoDist = { ~distPlusIngredients, ~shouldDownsample=options.downsampleTo |> E.O.isSome, ~recommendedLength=options.downsampleTo |> E.O.default(1000), + ~inputVariables= + [|("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); + let inputs2 = + RenderTypes.DistPlusRenderer.make( + ~samplingInputs={ + sampleCount: Some(options.sampleCount), + outputXYPoints: Some(options.outputXYPoints), + kernelWidth: options.kernelWidth, + }, + ~distPlusIngredients, + ~shouldDownsample=options.downsampleTo |> E.O.isSome, + ~recommendedLength=options.downsampleTo |> E.O.default(1000), + ~inputVariables= + [|("p", `SymbolicDist(`Float(2.0)))|] + ->Belt.Map.String.fromArray, + (), + ); + let response2 = DistPlusRenderer.run(inputs2); + switch (response1, response2) { + | (Ok(distPlus1), Ok(distPlus2)) => + <> + + + + | (Error(r), _) => r |> R.ste + | (_, Error(r)) => r |> R.ste }; | _ => "Nothing to show. Try to change the distribution description." 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/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/expressionTree/MathJsParser.re b/src/distPlus/expressionTree/MathJsParser.re index 31e33475..f168479d 100644 --- a/src/distPlus/expressionTree/MathJsParser.re +++ b/src/distPlus/expressionTree/MathJsParser.re @@ -1,3 +1,5 @@ +type inputVars = Belt.Map.String.t(ExpressionTypes.ExpressionTree.node); + module MathJsonToMathJsAdt = { type arg = | Symbol(string) @@ -50,24 +52,33 @@ module MathJsonToMathJsAdt = { module MathAdtToDistDst = { open MathJsonToMathJsAdt; + let handleSymbol = (inputVars: inputVars, sym) => { + switch (Belt.Map.String.get(inputVars, sym)) { + | Some(s) => Ok(s) + | None => Error("Couldn't find.") + }; + }; + 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)) @@ -347,24 +358,26 @@ module MathAdtToDistDst = { }; }; - let rec nodeParser = + let rec nodeParser = inputVars => fun | Value(f) => Ok(`SymbolicDist(`Float(f))) - | Fn({name, args}) => functionParser(nodeParser, name, args) + | Symbol(s) => handleSymbol(inputVars, s) + | Fn({name, args}) => functionParser(nodeParser(inputVars), name, args) | _ => { Error("This type not currently supported"); }; - let topLevel = + let topLevel = inputVars => fun - | Value(_) as r => nodeParser(r) - | Fn(_) as r => nodeParser(r) + | Value(_) as r => nodeParser(inputVars, r) + | Fn(_) as r => nodeParser(inputVars, r) | Array(_) => Error("Array not valid as top level") - | Symbol(_) => Error("Symbol not valid as top level") + | Symbol(s) => handleSymbol(inputVars, s) | Object(_) => Error("Object not valid as top level"); - let run = (r): result(ExpressionTypes.ExpressionTree.node, string) => - r |> MathAdtCleaner.run |> topLevel; + let run = + (inputVars, r): result(ExpressionTypes.ExpressionTree.node, string) => + r |> MathAdtCleaner.run |> topLevel(inputVars); }; /* The MathJs parser doesn't support '.+' syntax, but we want it because it @@ -374,7 +387,7 @@ module MathAdtToDistDst = { */ let pointwiseToRightLogShift = Js.String.replaceByRe([%re "/\.\+/g"], ">>>"); -let fromString = str => { +let fromString2 = (inputVars: inputVars, 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 @@ -384,7 +397,6 @@ let fromString = str => { Inside of this function, MathAdtToDistDst is called whenever a distribution function is encountered. */ let mathJsToJson = str |> pointwiseToRightLogShift |> Mathjs.parseMath; - Js.log(mathJsToJson); let mathJsParse = E.R.bind(mathJsToJson, r => { switch (MathJsonToMathJsAdt.run(r)) { @@ -394,6 +406,10 @@ let fromString = str => { }); Js.log(mathJsParse); - let value = E.R.bind(mathJsParse, MathAdtToDistDst.run); + let value = E.R.bind(mathJsParse, MathAdtToDistDst.run(inputVars)); value; }; + +let fromString = (str, vars: inputVars) => { + fromString2(vars, str); +}; diff --git a/src/distPlus/renderers/DistPlusRenderer.re b/src/distPlus/renderers/DistPlusRenderer.re index 1d06eb4a..a43a7822 100644 --- a/src/distPlus/renderers/DistPlusRenderer.re +++ b/src/distPlus/renderers/DistPlusRenderer.re @@ -8,10 +8,13 @@ let run = (inputs: RenderTypes.DistPlusRenderer.inputs) => { (), ) |> DistPlus.T.normalize; + // let symbolicDist: ExpressionTypes.ExpressionTree.node = `SymbolicDist(`Float(30.0)); + // inputVariables: [|("p", symbolicDist)|] -> Belt.Map.String.fromArray, let output = ShapeRenderer.run({ samplingInputs: inputs.samplingInputs, guesstimatorString: inputs.distPlusIngredients.guesstimatorString, + inputVariables: inputs.inputVariables, symbolicInputs: { length: inputs.recommendedLength, }, diff --git a/src/distPlus/renderers/RenderTypes.re b/src/distPlus/renderers/RenderTypes.re index 9b37503f..c7dc418f 100644 --- a/src/distPlus/renderers/RenderTypes.re +++ b/src/distPlus/renderers/RenderTypes.re @@ -1,3 +1,5 @@ +module MS = Belt.Map.String; + module ShapeRenderer = { module Sampling = { type inputs = { @@ -54,16 +56,18 @@ module ShapeRenderer = { samplingInputs: Sampling.inputs, symbolicInputs: Symbolic.inputs, guesstimatorString: string, + inputVariables: MS.t(ExpressionTypes.ExpressionTree.node), }; type outputs = { symbolic: option(Belt.Result.t(Symbolic.outputs, string)), sampling: option(Sampling.outputs), }; - let methodUsed = ({symbolic, sampling}:outputs) => switch(symbolic, sampling){ + 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) @@ -86,6 +90,7 @@ module DistPlusRenderer = { samplingInputs: ShapeRenderer.Sampling.inputs, recommendedLength: int, shouldDownsample: bool, + inputVariables: MS.t(ExpressionTypes.ExpressionTree.node), }; module Ingredients = { let make = @@ -107,6 +112,7 @@ module DistPlusRenderer = { ~recommendedLength=defaultRecommendedLength, ~shouldDownsample=defaultShouldDownsample, ~distPlusIngredients, + ~inputVariables=[||]->Belt.Map.String.fromArray, (), ) : inputs => { @@ -114,14 +120,18 @@ module DistPlusRenderer = { samplingInputs, recommendedLength, shouldDownsample, + inputVariables, }; type outputs = { shapeRenderOutputs: ShapeRenderer.Combined.outputs, - distPlus: option(DistTypes.distPlus) - } + distPlus: option(DistTypes.distPlus), + }; module Outputs = { - let distplus = (t:outputs) => t.distPlus - let shapeRenderOutputs = (t:outputs) => t.shapeRenderOutputs - let make = (shapeRenderOutputs, distPlus) => {shapeRenderOutputs, distPlus}; - } + 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 index c9ac37c7..f9822409 100644 --- a/src/distPlus/renderers/ShapeRenderer.re +++ b/src/distPlus/renderers/ShapeRenderer.re @@ -14,9 +14,9 @@ let formatString = str => { str |> formatMessyArray; }; -let runSymbolic = (inputs: RenderTypes.ShapeRenderer.Combined.inputs) => { +let run = (inputs: RenderTypes.ShapeRenderer.Combined.inputs) => { let str = formatString(inputs.guesstimatorString); - let graph = MathJsParser.fromString(str); + let graph = MathJsParser.fromString(str, inputs.inputVariables); graph |> E.R.bind(_, g => ExpressionTree.toShape( @@ -33,7 +33,3 @@ let runSymbolic = (inputs: RenderTypes.ShapeRenderer.Combined.inputs) => { |> E.R.fmap(RenderTypes.ShapeRenderer.Symbolic.make(g)) ); }; - -let run = (inputs: RenderTypes.ShapeRenderer.Combined.inputs) => { - runSymbolic(inputs); -}; diff --git a/src/distPlus/symbolic/SymbolicDist.re b/src/distPlus/symbolic/SymbolicDist.re index efa4e49b..43c5377b 100644 --- a/src/distPlus/symbolic/SymbolicDist.re +++ b/src/distPlus/symbolic/SymbolicDist.re @@ -32,6 +32,7 @@ module Triangular = { module Normal = { type t = normal; + let make = (mean, stdev):t => {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);