From 95b8ba20366421d897299dc3c1b35f9e0b682792 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Sun, 19 Jul 2020 21:19:57 +0100 Subject: [PATCH] Attempt to simplify Render code --- showcase/entries/Continuous2.re | 5 +- showcase/entries/ExpressionTreeExamples.re | 5 +- src/components/DistBuilder.re | 16 ++-- .../expressionTree/ExpressionTreeEvaluator.re | 48 ++++++++++- .../expressionTree/SamplingDistribution.re | 6 +- src/distPlus/renderers/DistPlusRenderer.re | 26 ++---- src/distPlus/renderers/ShapeRenderer.re | 7 +- .../renderers/samplesRenderer/Samples.re | 83 ++++++++++--------- src/interface/FormBuilder.re | 8 +- src/models/EAFunds.re | 10 ++- 10 files changed, 125 insertions(+), 89 deletions(-) diff --git a/showcase/entries/Continuous2.re b/showcase/entries/Continuous2.re index 86823fc0..f6cb9a53 100644 --- a/showcase/entries/Continuous2.re +++ b/showcase/entries/Continuous2.re @@ -19,8 +19,9 @@ let timeDist ={ let setup = dist => RenderTypes.DistPlusRenderer.make(~distPlusIngredients=dist,()) |> DistPlusRenderer.run - |> RenderTypes.DistPlusRenderer.Outputs.distplus - |> R.O.fmapOrNull(distPlus => ); + |> E.R.fmap(distPlus => ) + |> E.R.toOption + |> E.O.toExn("") let simpleExample = (name, guesstimatorString) => <> diff --git a/showcase/entries/ExpressionTreeExamples.re b/showcase/entries/ExpressionTreeExamples.re index ef29cdaf..2ca91990 100644 --- a/showcase/entries/ExpressionTreeExamples.re +++ b/showcase/entries/ExpressionTreeExamples.re @@ -1,8 +1,9 @@ let setup = dist => RenderTypes.DistPlusRenderer.make(~distPlusIngredients=dist, ()) |> DistPlusRenderer.run - |> RenderTypes.DistPlusRenderer.Outputs.distplus - |> R.O.fmapOrNull(distPlus => ); + |> E.R.fmap(distPlus => ) + |> E.R.toOption + |> E.O.toExn("") let simpleExample = (guesstimatorString, ~problem="", ()) => <> diff --git a/src/components/DistBuilder.re b/src/components/DistBuilder.re index 87746905..ae00fbb6 100644 --- a/src/components/DistBuilder.re +++ b/src/components/DistBuilder.re @@ -146,14 +146,11 @@ module DemoDist = { (), ); let response = DistPlusRenderer.run(inputs); - switch (RenderTypes.DistPlusRenderer.Outputs.distplus(response)) { - | Some(distPlus) => { + switch (response) { + | Ok(distPlus) => let normalizedDistPlus = DistPlus.T.normalize(distPlus); - ; - } - | _ => - "Correct Guesstimator string input to show a distribution." - |> R.ste + ; + | Error(r) => r |> R.ste }; | _ => "Nothing to show. Try to change the distribution description." @@ -484,7 +481,10 @@ let make = () => { /> - + diff --git a/src/distPlus/expressionTree/ExpressionTreeEvaluator.re b/src/distPlus/expressionTree/ExpressionTreeEvaluator.re index 134fbdbe..e93176b2 100644 --- a/src/distPlus/expressionTree/ExpressionTreeEvaluator.re +++ b/src/distPlus/expressionTree/ExpressionTreeEvaluator.re @@ -32,6 +32,50 @@ module AlgebraicCombination = { ); }; + let choose = (t1: node, t2: node) => { + let dLength = (r: DistTypes.discreteShape) => + r.xyShape |> XYShape.T.length; + switch (t1, t2) { + | (`RenderedDist(Continuous(_)), `RenderedDist(Continuous(_))) => `Sampling + | (`RenderedDist(Discrete(m1)), `RenderedDist(Discrete(m2))) + when dLength(m1) * dLength(m2) > 1000 => `Analytical + | (`RenderedDist(Discrete(_)), `RenderedDist(Discrete(_))) => `Sampling + | (`RenderedDist(Discrete(d)), `SymbolicDist(_)) + | (`SymbolicDist(_), `RenderedDist(Discrete(d))) + | (`RenderedDist(Discrete(d)), `RenderedDist(Continuous(_))) + | (`RenderedDist(Continuous(_)), `RenderedDist(Discrete(d))) => + dLength(d) > 10 ? `Sampling : `Analytical + | _ => `Sampling + }; + }; + let foo = + (evaluationParams, algebraicOp, t1: node, t2: node) + : result(node, string) => { + E.R.merge( + SamplingDistribution.renderIfIsNotSamplingDistribution( + evaluationParams, + t1, + ), + SamplingDistribution.renderIfIsNotSamplingDistribution( + evaluationParams, + t2, + ), + ) + |> E.R.bind(_, ((a, b)) => + switch (choose(a, b)) { + | `Sampling => + SamplingDistribution.combineShapesUsingSampling( + evaluationParams, + algebraicOp, + a, + b, + ) + | `Analytical => + combinationByRendering(evaluationParams, algebraicOp, a, b) + } + ); + }; + let operationToLeaf = ( evaluationParams: evaluationParams, @@ -45,8 +89,8 @@ module AlgebraicCombination = { |> E.R.bind( _, fun - | `SymbolicDist(d) as t => Ok(t) - | _ => combinationByRendering(evaluationParams, algebraicOp, t1, t2), + | `SymbolicDist(_) as t => Ok(t) + | _ => foo(evaluationParams, algebraicOp, t1, t2), ); }; diff --git a/src/distPlus/expressionTree/SamplingDistribution.re b/src/distPlus/expressionTree/SamplingDistribution.re index 60886b40..2f95be1e 100644 --- a/src/distPlus/expressionTree/SamplingDistribution.re +++ b/src/distPlus/expressionTree/SamplingDistribution.re @@ -6,7 +6,7 @@ let isSamplingDistribution: node => bool = | `RenderedDist(_) => true | _ => false; -let renderIfIsNotSamplingDistribution = (params, t) => +let renderIfIsNotSamplingDistribution = (params, t): result(node, string) => !isSamplingDistribution(t) ? switch (Render.render(params, t)) { | Ok(r) => Ok(r) @@ -70,9 +70,7 @@ let combineShapesUsingSampling = }, ), ) - |> E.O.bind(_, (r: RenderTypes.ShapeRenderer.Sampling.outputs) => - 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 521b244d..1d06eb4a 100644 --- a/src/distPlus/renderers/DistPlusRenderer.re +++ b/src/distPlus/renderers/DistPlusRenderer.re @@ -1,18 +1,4 @@ -let downsampleIfShould = - ( - {recommendedLength, shouldDownsample}: RenderTypes.DistPlusRenderer.inputs, - outputs: RenderTypes.ShapeRenderer.Combined.outputs, - dist, - ) => { - let willDownsample = - shouldDownsample - && RenderTypes.ShapeRenderer.Combined.methodUsed(outputs) == `Sampling; - willDownsample ? dist |> DistPlus.T.downsample(recommendedLength) : dist; -}; - -let run = - (inputs: RenderTypes.DistPlusRenderer.inputs) - : RenderTypes.DistPlusRenderer.outputs => { +let run = (inputs: RenderTypes.DistPlusRenderer.inputs) => { let toDist = shape => DistPlus.make( ~shape, @@ -22,7 +8,7 @@ let run = (), ) |> DistPlus.T.normalize; - let outputs = + let output = ShapeRenderer.run({ samplingInputs: inputs.samplingInputs, guesstimatorString: inputs.distPlusIngredients.guesstimatorString, @@ -30,8 +16,8 @@ let run = length: inputs.recommendedLength, }, }); - let shape = outputs |> RenderTypes.ShapeRenderer.Combined.getShape; - let dist = - shape |> E.O.fmap(toDist) |> E.O.fmap(downsampleIfShould(inputs, outputs)); - RenderTypes.DistPlusRenderer.Outputs.make(outputs, dist); + output + |> E.R.fmap((o: RenderTypes.ShapeRenderer.Symbolic.outputs) => + toDist(o.shape) + ); }; diff --git a/src/distPlus/renderers/ShapeRenderer.re b/src/distPlus/renderers/ShapeRenderer.re index a54c8c34..bc6bf411 100644 --- a/src/distPlus/renderers/ShapeRenderer.re +++ b/src/distPlus/renderers/ShapeRenderer.re @@ -36,9 +36,6 @@ let runSymbolic = (inputs: RenderTypes.ShapeRenderer.Combined.inputs) => { ); }; -let run = - (inputs: RenderTypes.ShapeRenderer.Combined.inputs) - : RenderTypes.ShapeRenderer.Combined.outputs => { - let symbolic = runSymbolic(inputs); - {symbolic: Some(symbolic), sampling: None}; +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 index ff8f6f46..7f9b10aa 100644 --- a/src/distPlus/renderers/samplesRenderer/Samples.re +++ b/src/distPlus/renderers/samplesRenderer/Samples.re @@ -1,3 +1,42 @@ +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 = { @@ -21,39 +60,6 @@ module KDE = { |> JS.samplesToContinuousPdf(_, outputXYPoints, kernelWidth) |> JS.jsToDist; }; - - // Note: This was an experiment, but it didn't actually work that well. - let inGroups = (samples, outputXYPoints, kernelWidth, ~cuttoff=0.9, ()) => { - let partitionAt = - samples - |> E.A.length - |> float_of_int - |> (e => e *. cuttoff) - |> int_of_float; - let part1XYPoints = - outputXYPoints |> float_of_int |> (e => e *. cuttoff) |> int_of_float; - let part2XYPoints = outputXYPoints - part1XYPoints |> Js.Math.max_int(30); - let part1Data = - samples |> Belt.Array.slice(_, ~offset=0, ~len=partitionAt); - let part2DataLength = (samples |> E.A.length) - partitionAt; - let part2Data = - samples - |> Belt.Array.slice( - _, - ~offset=(-1) * part2DataLength, - ~len=part2DataLength, - ); - let part1 = - part1Data - |> JS.samplesToContinuousPdf(_, part1XYPoints, kernelWidth) - |> JS.jsToDist; - let part2 = - part2Data - |> JS.samplesToContinuousPdf(_, part2XYPoints, 3) - |> JS.jsToDist; - let opp = 1.0 -. cuttoff; - part1; - }; }; module T = { @@ -109,7 +115,7 @@ module T = { let toShape = ( ~samples: t, - ~samplingInputs: RenderTypes.ShapeRenderer.Sampling.Inputs.fInputs, + ~samplingInputs: Types.fInputs, (), ) => { Array.fast_sort(compare, samples); @@ -126,6 +132,7 @@ module T = { 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 = @@ -136,7 +143,7 @@ module T = { samplingInputs.outputXYPoints, usedWidth, ); - let foo: RenderTypes.ShapeRenderer.Sampling.samplingStats = { + let foo: Types.samplingStats = { sampleCount: samplingInputs.sampleCount, outputXYPoints: samplingInputs.outputXYPoints, bandwidthXSuggested: _suggestedXWidth, @@ -159,7 +166,7 @@ module T = { ~continuous=pdf |> E.O.fmap(fst), ~discrete=Some(discrete), ); - let samplesParse: RenderTypes.ShapeRenderer.Sampling.outputs = { + let samplesParse: Types.outputs = { continuousParseParams: pdf |> E.O.fmap(snd), shape, }; @@ -168,11 +175,11 @@ module T = { let fromSamples = ( - ~samplingInputs=RenderTypes.ShapeRenderer.Sampling.Inputs.empty, + ~samplingInputs=Types.empty, samples, ) => { let samplingInputs = - RenderTypes.ShapeRenderer.Sampling.Inputs.toF(samplingInputs); + Types.toF(samplingInputs); toShape(~samples, ~samplingInputs, ()); }; }; diff --git a/src/interface/FormBuilder.re b/src/interface/FormBuilder.re index 7556a82f..ea06e318 100644 --- a/src/interface/FormBuilder.re +++ b/src/interface/FormBuilder.re @@ -25,11 +25,9 @@ let propValue = (t: Prop.Value.t) => { ~shouldDownsample=true, (), ) - |> DistPlusRenderer.run - |> RenderTypes.DistPlusRenderer.Outputs.distplus; + |> DistPlusRenderer.run; switch (newDistribution) { - | Some(distribution) => -
+ | Ok(distribution) =>
// { // className="w-1/3 border w-1/2 rounded py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline bg-white"> // {"30 to infinity, 80% mass" |> ReasonReact.string} // - | None => "Something went wrong" |> ReasonReact.string + | Error(e) => e |> ReasonReact.string }; | FloatCdf(_) =>
| Probability(r) => diff --git a/src/models/EAFunds.re b/src/models/EAFunds.re index 65f26ef9..215bfe2a 100644 --- a/src/models/EAFunds.re +++ b/src/models/EAFunds.re @@ -112,8 +112,12 @@ module Model = { GlobalCatastrophe.makeI(MomentRe.momentNow()) |> RenderTypes.DistPlusRenderer.make(~distPlusIngredients=_, ()) |> DistPlusRenderer.run - |> RenderTypes.DistPlusRenderer.Outputs.distplus - |> E.O.bind(_, DistPlusTime.Integral.xToY(Time(dateTime))); + |> E.R.bind(_, r => + r + |> DistPlusTime.Integral.xToY(Time(dateTime)) + |> E.O.toResult("error") + ) + |> E.R.toOption; }; let make = @@ -299,4 +303,4 @@ module Interface = { outputTypes: [||], run, }; -}; \ No newline at end of file +};