From e84cfd4c92b505526775b6fa9ba8868a0a640e47 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Sat, 4 Apr 2020 21:37:58 +0100 Subject: [PATCH 1/5] Adding render options and params --- showcase/entries/Continuous.re | 8 +-- src/Samples.re | 90 +++++++++++++++++++++---- src/components/DistBuilder.re | 43 ++++++++---- src/distribution/DistPlusIngredients.re | 45 ++++++------- src/distribution/GuesstimatorToShape.re | 32 +++++++++ src/distribution/MixedShapeBuilder.re | 3 +- src/distribution/RenderTypes.re | 86 +++++++++++++++++++++++ src/interface/FormBuilder.re | 8 +-- src/models/EAFunds.re | 1 + src/symbolic/MathJsParser.re | 1 - src/symbolic/SymbolicDist.re | 2 +- 11 files changed, 251 insertions(+), 68 deletions(-) create mode 100644 src/distribution/GuesstimatorToShape.re create mode 100644 src/distribution/RenderTypes.re diff --git a/showcase/entries/Continuous.re b/showcase/entries/Continuous.re index 2d0d2e4d..5ff0567b 100644 --- a/showcase/entries/Continuous.re +++ b/showcase/entries/Continuous.re @@ -15,12 +15,8 @@ let timeDist = ); let setup = dist => - dist - |> DistPlusIngredients.toDistPlus( - ~sampleCount=2000, - ~outputXYPoints=1000, - ~truncateTo=Some(1000), - ) + RenderTypes.DistPlus.make(~distPlusIngredients=dist,()) + |> DistPlusIngredients.toDistPlus |> E.O.React.fmapOrNull(distPlus => ); let simpleExample = (name, guesstimatorString) => diff --git a/src/Samples.re b/src/Samples.re index 360f8279..093335dc 100644 --- a/src/Samples.re +++ b/src/Samples.re @@ -22,6 +22,7 @@ module KDE = { |> 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 @@ -88,33 +89,94 @@ module T = { (continuous, discrete); }; - let kde = (~samples, ~outputXYPoints) => { - let width = Bandwidth.nrd0(samples); + let xWidthToUnitWidth = (samples, outputXYPoints, xWidth) => { let xyPointRange = E.A.Sorted.range(samples) |> E.O.default(0.0); let xyPointWidth = xyPointRange /. float_of_int(outputXYPoints); - let kernelWidth = int_of_float(Jstat.max([|(width /. xyPointWidth), 1.0 |])); - KDE.normalSampling(samples, outputXYPoints, kernelWidth); + 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); }; // todo: Figure out some way of doing this without having to integrate so many times. - let toShape = (~samples: t, ~outputXYPoints=3000, ~kernelWidth=10, ()) => { + let toShape = + (~samples: t, ~samplingInputs: RenderTypes.Sampling.Inputs.fInputs, ()) => { Array.fast_sort(compare, samples); let (continuousPart, discretePart) = E.A.Sorted.Floats.split(samples); - let length = samples |> E.A.length; - let lengthFloat = float_of_int(length); + let length = samples |> E.A.length |> float_of_int; let discrete: DistTypes.xyShape = discretePart - |> E.FloatFloatMap.fmap(r => r /. lengthFloat) + |> E.FloatFloatMap.fmap(r => r /. length) |> E.FloatFloatMap.toArray |> XYShape.T.fromZippedArray; - let pdf: DistTypes.xyShape = + + let pdf = continuousPart |> E.A.length > 5 ? { - continuousPart |> kde(~samples=_, ~outputXYPoints) + let _suggestedXWidth = Bandwidth.nrd0(continuousPart); + let _suggestedUnitWidth = + suggestedUnitWidth(continuousPart, samplingInputs.outputXYPoints); + let usedWidth = + samplingInputs.kernelWidth |> E.O.default(_suggestedXWidth); + let usedUnitWidth = + xWidthToUnitWidth( + samples, + samplingInputs.outputXYPoints, + usedWidth, + ); + let foo: RenderTypes.Sampling.samplingStats = { + sampleCount: samplingInputs.sampleCount, + outputXYPoints: samplingInputs.outputXYPoints, + bandwidthXSuggested: _suggestedXWidth, + bandwidthUnitSuggested: _suggestedUnitWidth, + bandwidthXImplemented: usedWidth, + bandwidthUnitImplemented: usedUnitWidth, + }; + continuousPart + |> kde( + ~samples=_, + ~outputXYPoints=samplingInputs.outputXYPoints, + formatUnitWidth(usedUnitWidth), + ) + |> Distributions.Continuous.make(`Linear) + |> (r => Some((r, foo))); } - : {xs: [||], ys: [||]}; - let continuous = pdf |> Distributions.Continuous.make(`Linear); - let shape = MixedShapeBuilder.buildSimple(~continuous, ~discrete); - shape; + : None; + let shape = + MixedShapeBuilder.buildSimple( + ~continuous=pdf |> E.O.fmap(fst), + ~discrete, + ); + let samplesParse: RenderTypes.Sampling.outputs = { + continuousParseParams: pdf |> E.O.fmap(snd), + shape, + }; + samplesParse; + }; + + let fromGuesstimatorString = + ( + ~guesstimatorString, + ~samplingInputs=RenderTypes.Sampling.Inputs.empty, + (), + ) => { + let hasValidSamples = + Guesstimator.stringToSamples(guesstimatorString, 10) |> E.A.length > 0; + let samplingInputs = RenderTypes.Sampling.Inputs.toF(samplingInputs); + switch (hasValidSamples) { + | false => None + | true => + let samples = + Guesstimator.stringToSamples(guesstimatorString, samplingInputs.sampleCount); + Some(toShape(~samples, ~samplingInputs, ())); + }; }; }; \ No newline at end of file diff --git a/src/components/DistBuilder.re b/src/components/DistBuilder.re index bfcbcb51..0ab50e95 100644 --- a/src/components/DistBuilder.re +++ b/src/components/DistBuilder.re @@ -26,7 +26,7 @@ type options = { sampleCount: int, outputXYPoints: int, truncateTo: option(int), - kernelWidth: int, + kernelWidth: option(float), }; module Form = ReForm.Make(FormConfig); @@ -111,6 +111,13 @@ module Styles = { ]); }; +type inputs = { + samplingInputs: RenderTypes.Sampling.inputs, + guesstimatorString: string, + length: int, + shouldTruncateSampledDistribution: int, +}; + module DemoDist = { [@react.component] let make = (~guesstimatorString, ~domain, ~unit, ~options) => { @@ -119,14 +126,24 @@ module DemoDist = {
{switch (domain, unit, options) { | (Some(domain), Some(unit), Some(options)) => - let distPlus = - DistPlusIngredients.make(~guesstimatorString, ~domain, ~unit, ()) - |> DistPlusIngredients.toDistPlus( - ~sampleCount=options.sampleCount, - ~outputXYPoints=options.outputXYPoints, - ~truncateTo=options.truncateTo, - ~kernelWidth=options.kernelWidth, - ); + let distPlusIngredients = + DistPlusIngredients.make( + ~guesstimatorString, + ~domain, + ~unit, + (), + ); + let inputs = + RenderTypes.DistPlus.make( + ~samplingInputs={ + sampleCount: Some(options.sampleCount), + outputXYPoints: Some(options.outputXYPoints), + kernelWidth: options.kernelWidth, + }, + ~distPlusIngredients, + (), + ); + let distPlus = DistPlusIngredients.toDistPlus(inputs); switch (distPlus) { | Some(distPlus) => | _ => @@ -160,9 +177,9 @@ let make = () => { unitType: "UnspecifiedDistribution", zero: MomentRe.momentNow(), unit: "days", - sampleCount: "10000", - outputXYPoints: "500", - truncateTo: "100", + sampleCount: "30000", + outputXYPoints: "10000", + truncateTo: "1000", kernelWidth: "5", }, (), @@ -246,7 +263,7 @@ let make = () => { truncateTo: int_of_float(truncateTo) > 0 ? Some(int_of_float(truncateTo)) : None, - kernelWidth: kernelWidth |> int_of_float, + kernelWidth: kernelWidth == 0.0 ? None : Some(kernelWidth), }) | _ => None }; diff --git a/src/distribution/DistPlusIngredients.re b/src/distribution/DistPlusIngredients.re index 36a7e663..098128a5 100644 --- a/src/distribution/DistPlusIngredients.re +++ b/src/distribution/DistPlusIngredients.re @@ -15,41 +15,34 @@ let applyTruncation = (truncateTo, distPlus) => | _ => None }; + // ~samplingInputs=RenderTypes.Sampling.Inputs.empty, + // ~truncateTo: option(int), + // t: distPlusIngredients, +//Make truncation optional let toDistPlus = ( - ~sampleCount=2000, - ~outputXYPoints=1500, - ~truncateTo=Some(300), - ~kernelWidth=5, - t: distPlusIngredients, + inputs:RenderTypes.DistPlus.inputs ) : option(distPlus) => { let toDist = shape => Distributions.DistPlus.make( ~shape, - ~domain=t.domain, - ~unit=t.unit, - ~guesstimatorString=Some(t.guesstimatorString), + ~domain=inputs.distPlusIngredients.domain, + ~unit=inputs.distPlusIngredients.unit, + ~guesstimatorString=Some(inputs.distPlusIngredients.guesstimatorString), (), ) |> Distributions.DistPlus.T.scaleToIntegralSum(~intendedSum=1.0); - let parsed1 = MathJsParser.fromString(t.guesstimatorString); let shape = - switch (parsed1) { - | Ok(r) => - let shape = SymbolicDist.toShape(truncateTo |> E.O.default(10000), r); - Some(shape |> toDist); - | _ => - let fewSamples = Guesstimator.stringToSamples(t.guesstimatorString, 10); - if (fewSamples |> E.A.length > 0) { - let samples = - Guesstimator.stringToSamples(t.guesstimatorString, sampleCount); - let shape = - Samples.T.toShape(~samples, ~outputXYPoints, ~kernelWidth, ()); - shape |> E.O.fmap(toDist) |> applyTruncation(truncateTo); - } else { - None; - }; - }; - shape; + GuesstimatorToShape.run( + ~renderingInputs={ + guesstimatorString: inputs.distPlusIngredients.guesstimatorString, + shapeLength: inputs.recommendedLength, + }, + ~samplingInputs=inputs.samplingInputs, + ) + |> GuesstimatorToShape.getShape; + //TODO: Apply truncation + let foo = shape |> E.O.fmap(toDist); + foo; }; \ No newline at end of file diff --git a/src/distribution/GuesstimatorToShape.re b/src/distribution/GuesstimatorToShape.re new file mode 100644 index 00000000..360fb787 --- /dev/null +++ b/src/distribution/GuesstimatorToShape.re @@ -0,0 +1,32 @@ +let runSymbolic = + (renderingInputs: RenderTypes.primaryInputs) =>{ + let graph = MathJsParser.fromString(renderingInputs.guesstimatorString); + graph |> E.R.fmap(g => RenderTypes.Symbolic.make(g, SymbolicDist.toShape(renderingInputs.shapeLength,g))) + } + +let getShape = (r: RenderTypes.Combined.outputs) => + switch (r.symbolic, r.sampling) { + | (Some(Ok({shape})), _) => Some(shape) + | (_, Some({shape})) => shape + | _ => None + }; + +let run = + ( + ~renderingInputs: RenderTypes.primaryInputs, + ~samplingInputs: RenderTypes.Sampling.inputs, + ) + : RenderTypes.Combined.outputs => { + let symbolic = runSymbolic(renderingInputs); + let sampling = + switch (symbolic) { + | Ok(r) => None + | Error(r) => + Samples.T.fromGuesstimatorString( + ~guesstimatorString=renderingInputs.guesstimatorString, + ~samplingInputs, + (), + ) + }; + {symbolic: Some(symbolic), sampling}; +}; \ No newline at end of file diff --git a/src/distribution/MixedShapeBuilder.re b/src/distribution/MixedShapeBuilder.re index cde9251c..c8195568 100644 --- a/src/distribution/MixedShapeBuilder.re +++ b/src/distribution/MixedShapeBuilder.re @@ -8,7 +8,8 @@ type assumptions = { discreteProbabilityMass: option(float), }; -let buildSimple = (~continuous, ~discrete): option(DistTypes.shape) => { +let buildSimple = (~continuous: option(DistTypes.continuousShape), ~discrete): option(DistTypes.shape) => { + let continuous = continuous |> E.O.default(Distributions.Continuous.make(`Linear, {xs: [||], ys: [||]})) let cLength = continuous |> Distributions.Continuous.getShape diff --git a/src/distribution/RenderTypes.re b/src/distribution/RenderTypes.re new file mode 100644 index 00000000..8f4b9882 --- /dev/null +++ b/src/distribution/RenderTypes.re @@ -0,0 +1,86 @@ +type primaryInputs = { + guesstimatorString: string, + shapeLength: int, +}; + +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(ProbExample.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: ProbExample.SymbolicDist.bigDist, + shape: ProbExample.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), + }; +}; + +module DistPlus = { + let defaultRecommendedLength = 10000; + let defaultShouldTruncate = true; + type inputs = { + distPlusIngredients: DistTypes.distPlusIngredients, + samplingInputs: Sampling.inputs, + recommendedLength: int, + shouldTruncate: bool, + }; + let make = + ( + ~samplingInputs=Sampling.Inputs.empty, + ~recommendedLength=defaultRecommendedLength, + ~shouldTruncate=defaultShouldTruncate, + ~distPlusIngredients, + () + ) + : inputs => { + distPlusIngredients, + samplingInputs, + recommendedLength, + shouldTruncate, + }; +}; \ No newline at end of file diff --git a/src/interface/FormBuilder.re b/src/interface/FormBuilder.re index 94e7b4cb..7280155b 100644 --- a/src/interface/FormBuilder.re +++ b/src/interface/FormBuilder.re @@ -19,12 +19,8 @@ let propValue = (t: Prop.Value.t) => { | ConditionalArray(r) => "Array" |> ReasonReact.string | DistPlusIngredients((r: DistTypes.distPlusIngredients)) => let newDistribution = - DistPlusIngredients.toDistPlus( - ~sampleCount=1000, - ~outputXYPoints=2000, - ~truncateTo=Some(500), - r, - ); + RenderTypes.DistPlus.make(~distPlusIngredients=r, ~recommendedLength=1000, ~shouldTruncate=true,()) + |> DistPlusIngredients.toDistPlus switch (newDistribution) { | Some(distribution) =>
diff --git a/src/models/EAFunds.re b/src/models/EAFunds.re index aeacb1b2..bd42d96e 100644 --- a/src/models/EAFunds.re +++ b/src/models/EAFunds.re @@ -110,6 +110,7 @@ module Model = { // TODO: Fixe number that integral is calculated for let getGlobalCatastropheChance = dateTime => { GlobalCatastrophe.makeI(MomentRe.momentNow()) + |> RenderTypes.DistPlus.make(~distPlusIngredients=_, ()) |> DistPlusIngredients.toDistPlus |> E.O.bind(_, Distributions.DistPlusTime.Integral.xToY(Time(dateTime))); }; diff --git a/src/symbolic/MathJsParser.re b/src/symbolic/MathJsParser.re index 4f6225ff..c5bae5bd 100644 --- a/src/symbolic/MathJsParser.re +++ b/src/symbolic/MathJsParser.re @@ -245,6 +245,5 @@ let fromString = str => { } ); let value = E.R.bind(mathJsParse, MathAdtToDistDst.run); - Js.log4("fromString", mathJsToJson, mathJsParse, value); value; }; \ No newline at end of file diff --git a/src/symbolic/SymbolicDist.re b/src/symbolic/SymbolicDist.re index a4c33baf..cec8e1d6 100644 --- a/src/symbolic/SymbolicDist.re +++ b/src/symbolic/SymbolicDist.re @@ -296,7 +296,7 @@ module PointwiseAddDistributionsWeighted = { let normalized = normalizeWeights(dists); let continuous = normalized |> E.A.filter(((r,_)) => GenericSimple.contType(r) == `Continuous) |> continuousShape(_, sampleCount); let discrete = normalized |> E.A.filter(((r,_)) => GenericSimple.contType(r) == `Discrete) |> discreteShape(_, sampleCount); - let shape = MixedShapeBuilder.buildSimple(~continuous, ~discrete); + let shape = MixedShapeBuilder.buildSimple(~continuous=Some(continuous), ~discrete); shape |> E.O.toExt("") }; From 990f01b8d6bc3f862971f20cd3baf98d69a319d2 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Sun, 5 Apr 2020 07:36:14 +0100 Subject: [PATCH 2/5] Reorganization of files --- showcase/entries/Continuous.re | 2 +- src/components/DistBuilder.re | 4 +- src/{ => distPlus}/distribution/DistTypes.re | 0 .../distribution/Distributions.re | 0 .../distribution/MixedShapeBuilder.re | 2 + src/{ => distPlus}/distribution/TimeTypes.re | 0 src/{ => distPlus}/distribution/XYShape.re | 0 src/distPlus/renderers/DistPlusIngredients.re | 39 ++++++++ src/distPlus/renderers/GuesstimatorToShape.re | 31 +++++++ src/distPlus/renderers/RenderTypes.re | 88 +++++++++++++++++++ .../renderers/samplesRenderer}/Bandwidth.re | 0 .../samplesRenderer}/Guesstimator.re | 0 .../samplesRenderer}/GuesstimatorDist.re | 0 .../samplesRenderer}/GuesstimatorLibrary.js | 0 .../renderers/samplesRenderer}/KdeLibrary.js | 0 .../renderers/samplesRenderer}/Samples.re | 13 ++- src/{ => distPlus}/symbolic/MathJsParser.re | 2 + src/{ => distPlus}/symbolic/Mathjs.re | 0 src/{ => distPlus}/symbolic/MathjsWrapper.js | 0 src/{ => distPlus}/symbolic/SymbolicDist.re | 0 src/{ => distPlus}/utility/E.re | 0 src/{symbolic => distPlus/utility}/Jstat.re | 0 src/{ => distPlus}/utility/Lodash.re | 0 src/distribution/DistPlusIngredients.re | 48 ---------- src/distribution/GuesstimatorToShape.re | 32 ------- src/distribution/RenderTypes.re | 86 ------------------ src/interface/FormBuilder.re | 2 +- src/models/EAFunds.re | 2 +- 28 files changed, 173 insertions(+), 178 deletions(-) rename src/{ => distPlus}/distribution/DistTypes.re (100%) rename src/{ => distPlus}/distribution/Distributions.re (100%) rename src/{ => distPlus}/distribution/MixedShapeBuilder.re (98%) rename src/{ => distPlus}/distribution/TimeTypes.re (100%) rename src/{ => distPlus}/distribution/XYShape.re (100%) create mode 100644 src/distPlus/renderers/DistPlusIngredients.re create mode 100644 src/distPlus/renderers/GuesstimatorToShape.re create mode 100644 src/distPlus/renderers/RenderTypes.re rename src/{utility => distPlus/renderers/samplesRenderer}/Bandwidth.re (100%) rename src/{utility => distPlus/renderers/samplesRenderer}/Guesstimator.re (100%) rename src/{utility => distPlus/renderers/samplesRenderer}/GuesstimatorDist.re (100%) rename src/{utility => distPlus/renderers/samplesRenderer}/GuesstimatorLibrary.js (100%) rename src/{utility => distPlus/renderers/samplesRenderer}/KdeLibrary.js (100%) rename src/{ => distPlus/renderers/samplesRenderer}/Samples.re (91%) rename src/{ => distPlus}/symbolic/MathJsParser.re (99%) rename src/{ => distPlus}/symbolic/Mathjs.re (100%) rename src/{ => distPlus}/symbolic/MathjsWrapper.js (100%) rename src/{ => distPlus}/symbolic/SymbolicDist.re (100%) rename src/{ => distPlus}/utility/E.re (100%) rename src/{symbolic => distPlus/utility}/Jstat.re (100%) rename src/{ => distPlus}/utility/Lodash.re (100%) delete mode 100644 src/distribution/DistPlusIngredients.re delete mode 100644 src/distribution/GuesstimatorToShape.re delete mode 100644 src/distribution/RenderTypes.re diff --git a/showcase/entries/Continuous.re b/showcase/entries/Continuous.re index 5ff0567b..23c074d6 100644 --- a/showcase/entries/Continuous.re +++ b/showcase/entries/Continuous.re @@ -15,7 +15,7 @@ let timeDist = ); let setup = dist => - RenderTypes.DistPlus.make(~distPlusIngredients=dist,()) + RenderTypes.DistPlusRenderer.make(~distPlusIngredients=dist,()) |> DistPlusIngredients.toDistPlus |> E.O.React.fmapOrNull(distPlus => ); diff --git a/src/components/DistBuilder.re b/src/components/DistBuilder.re index 0ab50e95..0490e44a 100644 --- a/src/components/DistBuilder.re +++ b/src/components/DistBuilder.re @@ -112,7 +112,7 @@ module Styles = { }; type inputs = { - samplingInputs: RenderTypes.Sampling.inputs, + samplingInputs: RenderTypes.ShapeRenderer.Sampling.inputs, guesstimatorString: string, length: int, shouldTruncateSampledDistribution: int, @@ -134,7 +134,7 @@ module DemoDist = { (), ); let inputs = - RenderTypes.DistPlus.make( + RenderTypes.DistPlusRenderer.make( ~samplingInputs={ sampleCount: Some(options.sampleCount), outputXYPoints: Some(options.outputXYPoints), diff --git a/src/distribution/DistTypes.re b/src/distPlus/distribution/DistTypes.re similarity index 100% rename from src/distribution/DistTypes.re rename to src/distPlus/distribution/DistTypes.re diff --git a/src/distribution/Distributions.re b/src/distPlus/distribution/Distributions.re similarity index 100% rename from src/distribution/Distributions.re rename to src/distPlus/distribution/Distributions.re diff --git a/src/distribution/MixedShapeBuilder.re b/src/distPlus/distribution/MixedShapeBuilder.re similarity index 98% rename from src/distribution/MixedShapeBuilder.re rename to src/distPlus/distribution/MixedShapeBuilder.re index c8195568..949a6f20 100644 --- a/src/distribution/MixedShapeBuilder.re +++ b/src/distPlus/distribution/MixedShapeBuilder.re @@ -40,6 +40,8 @@ let buildSimple = (~continuous: option(DistTypes.continuousShape), ~discrete): o }; }; + +// TODO: Delete, only being used in tests let build = (~continuous, ~discrete, ~assumptions) => switch (assumptions) { | { diff --git a/src/distribution/TimeTypes.re b/src/distPlus/distribution/TimeTypes.re similarity index 100% rename from src/distribution/TimeTypes.re rename to src/distPlus/distribution/TimeTypes.re diff --git a/src/distribution/XYShape.re b/src/distPlus/distribution/XYShape.re similarity index 100% rename from src/distribution/XYShape.re rename to src/distPlus/distribution/XYShape.re diff --git a/src/distPlus/renderers/DistPlusIngredients.re b/src/distPlus/renderers/DistPlusIngredients.re new file mode 100644 index 00000000..76bdaba9 --- /dev/null +++ b/src/distPlus/renderers/DistPlusIngredients.re @@ -0,0 +1,39 @@ +open DistTypes; + +let make = + (~guesstimatorString, ~domain=Complete, ~unit=UnspecifiedDistribution, ()) + : distPlusIngredients => { + guesstimatorString, + domain, + unit, +}; + +let truncateIfShould = (inputs: RenderTypes.DistPlusRenderer.inputs, dist) => { + inputs.shouldTruncate + ? dist + : dist |> Distributions.DistPlus.T.truncate(inputs.recommendedLength); +}; + +let toDistPlus = (inputs: RenderTypes.DistPlusRenderer.inputs): option(distPlus) => { + let toDist = shape => + Distributions.DistPlus.make( + ~shape, + ~domain=inputs.distPlusIngredients.domain, + ~unit=inputs.distPlusIngredients.unit, + ~guesstimatorString=Some(inputs.distPlusIngredients.guesstimatorString), + (), + ) + |> Distributions.DistPlus.T.scaleToIntegralSum(~intendedSum=1.0); + let shape = + GuesstimatorToShape.run({ + samplingInputs: inputs.samplingInputs, + guesstimatorString: inputs.distPlusIngredients.guesstimatorString, + symbolicInputs: { + length: inputs.recommendedLength, + }, + }) + |> GuesstimatorToShape.getShape; + let dist = + shape |> E.O.fmap(toDist) |> E.O.fmap(truncateIfShould(inputs)); + dist; +}; \ No newline at end of file diff --git a/src/distPlus/renderers/GuesstimatorToShape.re b/src/distPlus/renderers/GuesstimatorToShape.re new file mode 100644 index 00000000..cf988330 --- /dev/null +++ b/src/distPlus/renderers/GuesstimatorToShape.re @@ -0,0 +1,31 @@ +let runSymbolic = + (guesstimatorString, length) =>{ + let graph = MathJsParser.fromString(guesstimatorString); + graph |> E.R.fmap(g => RenderTypes.ShapeRenderer.Symbolic.make(g, SymbolicDist.toShape(length,g))) + } + +let getShape = (r: RenderTypes.ShapeRenderer.Combined.outputs) => + switch (r.symbolic, r.sampling) { + | (Some(Ok({shape})), _) => Some(shape) + | (_, Some({shape})) => shape + | _ => None + }; + +let run = + ( + inputs: RenderTypes.ShapeRenderer.Combined.inputs + ) + : RenderTypes.ShapeRenderer.Combined.outputs => { + let symbolic = runSymbolic(inputs.guesstimatorString, inputs.symbolicInputs.length); + let sampling = + switch (symbolic) { + | Ok(r) => None + | Error(r) => + Samples.T.fromGuesstimatorString( + ~guesstimatorString=inputs.guesstimatorString, + ~samplingInputs=inputs.samplingInputs, + (), + ) + }; + {symbolic: Some(symbolic), sampling}; +}; \ No newline at end of file diff --git a/src/distPlus/renderers/RenderTypes.re b/src/distPlus/renderers/RenderTypes.re new file mode 100644 index 00000000..9d3d281c --- /dev/null +++ b/src/distPlus/renderers/RenderTypes.re @@ -0,0 +1,88 @@ +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: SymbolicDist.bigDist, + 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), + }; + }; +}; + +module DistPlusRenderer = { + let defaultRecommendedLength = 10000; + let defaultShouldTruncate = true; + type inputs = { + distPlusIngredients: DistTypes.distPlusIngredients, + samplingInputs: ShapeRenderer.Sampling.inputs, + recommendedLength: int, + shouldTruncate: bool, + }; + let make = + ( + ~samplingInputs=ShapeRenderer.Sampling.Inputs.empty, + ~recommendedLength=defaultRecommendedLength, + ~shouldTruncate=defaultShouldTruncate, + ~distPlusIngredients, + (), + ) + : inputs => { + distPlusIngredients, + samplingInputs, + recommendedLength, + shouldTruncate, + }; +}; \ No newline at end of file diff --git a/src/utility/Bandwidth.re b/src/distPlus/renderers/samplesRenderer/Bandwidth.re similarity index 100% rename from src/utility/Bandwidth.re rename to src/distPlus/renderers/samplesRenderer/Bandwidth.re diff --git a/src/utility/Guesstimator.re b/src/distPlus/renderers/samplesRenderer/Guesstimator.re similarity index 100% rename from src/utility/Guesstimator.re rename to src/distPlus/renderers/samplesRenderer/Guesstimator.re diff --git a/src/utility/GuesstimatorDist.re b/src/distPlus/renderers/samplesRenderer/GuesstimatorDist.re similarity index 100% rename from src/utility/GuesstimatorDist.re rename to src/distPlus/renderers/samplesRenderer/GuesstimatorDist.re diff --git a/src/utility/GuesstimatorLibrary.js b/src/distPlus/renderers/samplesRenderer/GuesstimatorLibrary.js similarity index 100% rename from src/utility/GuesstimatorLibrary.js rename to src/distPlus/renderers/samplesRenderer/GuesstimatorLibrary.js diff --git a/src/utility/KdeLibrary.js b/src/distPlus/renderers/samplesRenderer/KdeLibrary.js similarity index 100% rename from src/utility/KdeLibrary.js rename to src/distPlus/renderers/samplesRenderer/KdeLibrary.js diff --git a/src/Samples.re b/src/distPlus/renderers/samplesRenderer/Samples.re similarity index 91% rename from src/Samples.re rename to src/distPlus/renderers/samplesRenderer/Samples.re index 093335dc..cb48c1b5 100644 --- a/src/Samples.re +++ b/src/distPlus/renderers/samplesRenderer/Samples.re @@ -10,7 +10,7 @@ module JS = { ys: ysGet(d), }; - [@bs.module "./utility/KdeLibrary.js"] + [@bs.module "./KdeLibrary.js"] external samplesToContinuousPdf: (array(float), int, int) => distJs = "samplesToContinuousPdf"; }; @@ -106,9 +106,8 @@ module T = { KDE.normalSampling(samples, outputXYPoints, width); }; - // todo: Figure out some way of doing this without having to integrate so many times. let toShape = - (~samples: t, ~samplingInputs: RenderTypes.Sampling.Inputs.fInputs, ()) => { + (~samples: t, ~samplingInputs: RenderTypes.ShapeRenderer.Sampling.Inputs.fInputs, ()) => { Array.fast_sort(compare, samples); let (continuousPart, discretePart) = E.A.Sorted.Floats.split(samples); let length = samples |> E.A.length |> float_of_int; @@ -132,7 +131,7 @@ module T = { samplingInputs.outputXYPoints, usedWidth, ); - let foo: RenderTypes.Sampling.samplingStats = { + let foo: RenderTypes.ShapeRenderer.Sampling.samplingStats = { sampleCount: samplingInputs.sampleCount, outputXYPoints: samplingInputs.outputXYPoints, bandwidthXSuggested: _suggestedXWidth, @@ -155,7 +154,7 @@ module T = { ~continuous=pdf |> E.O.fmap(fst), ~discrete, ); - let samplesParse: RenderTypes.Sampling.outputs = { + let samplesParse: RenderTypes.ShapeRenderer.Sampling.outputs = { continuousParseParams: pdf |> E.O.fmap(snd), shape, }; @@ -165,12 +164,12 @@ module T = { let fromGuesstimatorString = ( ~guesstimatorString, - ~samplingInputs=RenderTypes.Sampling.Inputs.empty, + ~samplingInputs=RenderTypes.ShapeRenderer.Sampling.Inputs.empty, (), ) => { let hasValidSamples = Guesstimator.stringToSamples(guesstimatorString, 10) |> E.A.length > 0; - let samplingInputs = RenderTypes.Sampling.Inputs.toF(samplingInputs); + let samplingInputs = RenderTypes.ShapeRenderer.Sampling.Inputs.toF(samplingInputs); switch (hasValidSamples) { | false => None | true => diff --git a/src/symbolic/MathJsParser.re b/src/distPlus/symbolic/MathJsParser.re similarity index 99% rename from src/symbolic/MathJsParser.re rename to src/distPlus/symbolic/MathJsParser.re index c5bae5bd..5144d221 100644 --- a/src/symbolic/MathJsParser.re +++ b/src/distPlus/symbolic/MathJsParser.re @@ -1,3 +1,5 @@ +// todo: rename to SymbolicParser + module MathJsonToMathJsAdt = { type arg = | Symbol(string) diff --git a/src/symbolic/Mathjs.re b/src/distPlus/symbolic/Mathjs.re similarity index 100% rename from src/symbolic/Mathjs.re rename to src/distPlus/symbolic/Mathjs.re diff --git a/src/symbolic/MathjsWrapper.js b/src/distPlus/symbolic/MathjsWrapper.js similarity index 100% rename from src/symbolic/MathjsWrapper.js rename to src/distPlus/symbolic/MathjsWrapper.js diff --git a/src/symbolic/SymbolicDist.re b/src/distPlus/symbolic/SymbolicDist.re similarity index 100% rename from src/symbolic/SymbolicDist.re rename to src/distPlus/symbolic/SymbolicDist.re diff --git a/src/utility/E.re b/src/distPlus/utility/E.re similarity index 100% rename from src/utility/E.re rename to src/distPlus/utility/E.re diff --git a/src/symbolic/Jstat.re b/src/distPlus/utility/Jstat.re similarity index 100% rename from src/symbolic/Jstat.re rename to src/distPlus/utility/Jstat.re diff --git a/src/utility/Lodash.re b/src/distPlus/utility/Lodash.re similarity index 100% rename from src/utility/Lodash.re rename to src/distPlus/utility/Lodash.re diff --git a/src/distribution/DistPlusIngredients.re b/src/distribution/DistPlusIngredients.re deleted file mode 100644 index 098128a5..00000000 --- a/src/distribution/DistPlusIngredients.re +++ /dev/null @@ -1,48 +0,0 @@ -open DistTypes; - -let make = - (~guesstimatorString, ~domain=Complete, ~unit=UnspecifiedDistribution, ()) - : distPlusIngredients => { - guesstimatorString, - domain, - unit, -}; - -let applyTruncation = (truncateTo, distPlus) => - switch (truncateTo, distPlus) { - | (Some(t), Some(d)) => Some(d |> Distributions.DistPlus.T.truncate(t)) - | (None, Some(d)) => Some(d) - | _ => None - }; - - // ~samplingInputs=RenderTypes.Sampling.Inputs.empty, - // ~truncateTo: option(int), - // t: distPlusIngredients, -//Make truncation optional -let toDistPlus = - ( - inputs:RenderTypes.DistPlus.inputs - ) - : option(distPlus) => { - let toDist = shape => - Distributions.DistPlus.make( - ~shape, - ~domain=inputs.distPlusIngredients.domain, - ~unit=inputs.distPlusIngredients.unit, - ~guesstimatorString=Some(inputs.distPlusIngredients.guesstimatorString), - (), - ) - |> Distributions.DistPlus.T.scaleToIntegralSum(~intendedSum=1.0); - let shape = - GuesstimatorToShape.run( - ~renderingInputs={ - guesstimatorString: inputs.distPlusIngredients.guesstimatorString, - shapeLength: inputs.recommendedLength, - }, - ~samplingInputs=inputs.samplingInputs, - ) - |> GuesstimatorToShape.getShape; - //TODO: Apply truncation - let foo = shape |> E.O.fmap(toDist); - foo; -}; \ No newline at end of file diff --git a/src/distribution/GuesstimatorToShape.re b/src/distribution/GuesstimatorToShape.re deleted file mode 100644 index 360fb787..00000000 --- a/src/distribution/GuesstimatorToShape.re +++ /dev/null @@ -1,32 +0,0 @@ -let runSymbolic = - (renderingInputs: RenderTypes.primaryInputs) =>{ - let graph = MathJsParser.fromString(renderingInputs.guesstimatorString); - graph |> E.R.fmap(g => RenderTypes.Symbolic.make(g, SymbolicDist.toShape(renderingInputs.shapeLength,g))) - } - -let getShape = (r: RenderTypes.Combined.outputs) => - switch (r.symbolic, r.sampling) { - | (Some(Ok({shape})), _) => Some(shape) - | (_, Some({shape})) => shape - | _ => None - }; - -let run = - ( - ~renderingInputs: RenderTypes.primaryInputs, - ~samplingInputs: RenderTypes.Sampling.inputs, - ) - : RenderTypes.Combined.outputs => { - let symbolic = runSymbolic(renderingInputs); - let sampling = - switch (symbolic) { - | Ok(r) => None - | Error(r) => - Samples.T.fromGuesstimatorString( - ~guesstimatorString=renderingInputs.guesstimatorString, - ~samplingInputs, - (), - ) - }; - {symbolic: Some(symbolic), sampling}; -}; \ No newline at end of file diff --git a/src/distribution/RenderTypes.re b/src/distribution/RenderTypes.re deleted file mode 100644 index 8f4b9882..00000000 --- a/src/distribution/RenderTypes.re +++ /dev/null @@ -1,86 +0,0 @@ -type primaryInputs = { - guesstimatorString: string, - shapeLength: int, -}; - -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(ProbExample.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: ProbExample.SymbolicDist.bigDist, - shape: ProbExample.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), - }; -}; - -module DistPlus = { - let defaultRecommendedLength = 10000; - let defaultShouldTruncate = true; - type inputs = { - distPlusIngredients: DistTypes.distPlusIngredients, - samplingInputs: Sampling.inputs, - recommendedLength: int, - shouldTruncate: bool, - }; - let make = - ( - ~samplingInputs=Sampling.Inputs.empty, - ~recommendedLength=defaultRecommendedLength, - ~shouldTruncate=defaultShouldTruncate, - ~distPlusIngredients, - () - ) - : inputs => { - distPlusIngredients, - samplingInputs, - recommendedLength, - shouldTruncate, - }; -}; \ No newline at end of file diff --git a/src/interface/FormBuilder.re b/src/interface/FormBuilder.re index 7280155b..464aff41 100644 --- a/src/interface/FormBuilder.re +++ b/src/interface/FormBuilder.re @@ -19,7 +19,7 @@ let propValue = (t: Prop.Value.t) => { | ConditionalArray(r) => "Array" |> ReasonReact.string | DistPlusIngredients((r: DistTypes.distPlusIngredients)) => let newDistribution = - RenderTypes.DistPlus.make(~distPlusIngredients=r, ~recommendedLength=1000, ~shouldTruncate=true,()) + RenderTypes.DistPlusRenderer.make(~distPlusIngredients=r, ~recommendedLength=1000, ~shouldTruncate=true,()) |> DistPlusIngredients.toDistPlus switch (newDistribution) { | Some(distribution) => diff --git a/src/models/EAFunds.re b/src/models/EAFunds.re index bd42d96e..ecdd25f2 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.DistPlus.make(~distPlusIngredients=_, ()) + |> RenderTypes.DistPlusRenderer.make(~distPlusIngredients=_, ()) |> DistPlusIngredients.toDistPlus |> E.O.bind(_, Distributions.DistPlusTime.Integral.xToY(Time(dateTime))); }; From 82595ae167b0ff0a50ca7e69ed23c3b2dd956ac7 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Sun, 5 Apr 2020 12:00:25 +0100 Subject: [PATCH 3/5] Cleanup to return more from DistPlusRenderer --- showcase/entries/Continuous.re | 115 +++++++++--------- .../samplesRenderer => }/GuesstimatorDist.re | 0 src/components/DistBuilder.re | 8 +- src/distPlus/distribution/DistTypes.re | 6 - ...PlusIngredients.re => DistPlusRenderer.re} | 34 +++--- src/distPlus/renderers/RenderTypes.re | 36 +++++- ...uesstimatorToShape.re => ShapeRenderer.re} | 11 +- src/interface/FormBuilder.re | 12 +- src/interface/Prop.re | 4 +- src/models/EAFunds.re | 7 +- src/models/GlobalCatastrophe.re | 2 +- src/models/Human.re | 2 +- 12 files changed, 132 insertions(+), 105 deletions(-) rename src/{distPlus/renderers/samplesRenderer => }/GuesstimatorDist.re (100%) rename src/distPlus/renderers/{DistPlusIngredients.re => DistPlusRenderer.re} (50%) rename src/distPlus/renderers/{GuesstimatorToShape.re => ShapeRenderer.re} (74%) diff --git a/showcase/entries/Continuous.re b/showcase/entries/Continuous.re index 23c074d6..a63fb2ab 100644 --- a/showcase/entries/Continuous.re +++ b/showcase/entries/Continuous.re @@ -5,41 +5,44 @@ // floor(3 to 4) // uniform(0,1) > 0.3 ? lognormal(6.652, -0.41): 0 -let timeDist = - DistPlusIngredients.make( +let timeDist ={ + let ingredients = RenderTypes.DistPlusRenderer.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,()) + inputs |> DistPlusRenderer.run +} let setup = dist => RenderTypes.DistPlusRenderer.make(~distPlusIngredients=dist,()) - |> DistPlusIngredients.toDistPlus + |> DistPlusRenderer.run + |> RenderTypes.DistPlusRenderer.Outputs.distplus |> E.O.React.fmapOrNull(distPlus => ); -let simpleExample = (name, guesstimatorString) => - <> -

- {name |> ReasonReact.string} -

- {setup(DistPlusIngredients.make(~guesstimatorString, ()))} - ; +// let simpleExample = (name, guesstimatorString) => +// <> +//

+// {name |> ReasonReact.string} +//

+// {setup(DistPlusIngredients.make(~guesstimatorString, ()))} +// ; -let timeExample = (name, guesstimatorString) => - <> -

- {name |> ReasonReact.string} -

- {setup( - DistPlusIngredients.make( - ~guesstimatorString, - ~unit=TimeDistribution({zero: MomentRe.momentNow(), unit: `years}), - (), - ), - )} - ; +// let timeExample = (name, guesstimatorString) => +// <> +//

+// {name |> ReasonReact.string} +//

+// {setup( +// DistPlusIngredients.make( +// ~guesstimatorString, +// ~unit=TimeDistribution({zero: MomentRe.momentNow(), unit: `years}), +// (), +// ), +// )} +// ; let distributions = () =>
@@ -47,37 +50,37 @@ let distributions = () =>

{"Initial Section" |> ReasonReact.string}

- {simpleExample("Continuous", "5 to 20")} - {simpleExample("Continuous, wide range", "1 to 1000000")} - {simpleExample("Continuous, tiny values", "0.000000001 to 0.00000001")} - {simpleExample( - "Continuous large values", - "50000000000000 to 200000000000000000", - )} - {simpleExample("Discrete", "floor(10 to 20)")} - {simpleExample( - "Discrete and below 0, normal(10,30)", - "floor(normal(10,30))", - )} - {simpleExample("Discrete, wide range", "floor(10 to 200000)")} - {simpleExample("Mixed", "mm(5 to 20, floor(20 to 30), [.5,.5])")} - {simpleExample("Mixed, Early-Discrete Point", "mm(1, 5 to 20, [.5,.5])")} - {simpleExample( - "Mixed, Two-Discrete Points", - "mm(0,10, 5 to 20, [.5,.5,.5])", - )} -

- {"Over Time" |> ReasonReact.string} -

- {timeExample("Continuous", "5 to 20")} - {timeExample("Continuous Over Long Period", "500 to 200000")} - {timeExample("Continuous Over Short Period", "0.0001 to 0.001")} - {timeExample( - "Continuous Over Very Long Period", - "500 to 20000000000000", - )} - {timeExample("Discrete", "floor(5 to 20)")} - {timeExample("Mixed", "mm(5 to 20, floor(5 to 20), [.5,.5])")} + // {simpleExample("Continuous", "5 to 20")} + // {simpleExample("Continuous, wide range", "1 to 1000000")} + // {simpleExample("Continuous, tiny values", "0.000000001 to 0.00000001")} + // {simpleExample( + // "Continuous large values", + // "50000000000000 to 200000000000000000", + // )} + // {simpleExample("Discrete", "floor(10 to 20)")} + // {simpleExample( + // "Discrete and below 0, normal(10,30)", + // "floor(normal(10,30))", + // )} + // {simpleExample("Discrete, wide range", "floor(10 to 200000)")} + // {simpleExample("Mixed", "mm(5 to 20, floor(20 to 30), [.5,.5])")} + // {simpleExample("Mixed, Early-Discrete Point", "mm(1, 5 to 20, [.5,.5])")} + // {simpleExample( + // "Mixed, Two-Discrete Points", + // "mm(0,10, 5 to 20, [.5,.5,.5])", + // )} + //

+ // {"Over Time" |> ReasonReact.string} + //

+ // {timeExample("Continuous", "5 to 20")} + // {timeExample("Continuous Over Long Period", "500 to 200000")} + // {timeExample("Continuous Over Short Period", "0.0001 to 0.001")} + // {timeExample( + // "Continuous Over Very Long Period", + // "500 to 20000000000000", + // )} + // {timeExample("Discrete", "floor(5 to 20)")} + // {timeExample("Mixed", "mm(5 to 20, floor(5 to 20), [.5,.5])")}
; diff --git a/src/distPlus/renderers/samplesRenderer/GuesstimatorDist.re b/src/GuesstimatorDist.re similarity index 100% rename from src/distPlus/renderers/samplesRenderer/GuesstimatorDist.re rename to src/GuesstimatorDist.re diff --git a/src/components/DistBuilder.re b/src/components/DistBuilder.re index 0490e44a..2fc9aa2c 100644 --- a/src/components/DistBuilder.re +++ b/src/components/DistBuilder.re @@ -127,7 +127,7 @@ module DemoDist = { {switch (domain, unit, options) { | (Some(domain), Some(unit), Some(options)) => let distPlusIngredients = - DistPlusIngredients.make( + RenderTypes.DistPlusRenderer.Ingredients.make( ~guesstimatorString, ~domain, ~unit, @@ -141,10 +141,12 @@ module DemoDist = { kernelWidth: options.kernelWidth, }, ~distPlusIngredients, + ~shouldTruncate=options.truncateTo |> E.O.isSome, + ~recommendedLength=options.truncateTo |> E.O.default(10000), (), ); - let distPlus = DistPlusIngredients.toDistPlus(inputs); - switch (distPlus) { + let response = DistPlusRenderer.run(inputs); + switch (RenderTypes.DistPlusRenderer.Outputs.distplus(response)) { | Some(distPlus) => | _ => "Correct Guesstimator string input to show a distribution." diff --git a/src/distPlus/distribution/DistTypes.re b/src/distPlus/distribution/DistTypes.re index 8178c879..7a598c01 100644 --- a/src/distPlus/distribution/DistTypes.re +++ b/src/distPlus/distribution/DistTypes.re @@ -60,12 +60,6 @@ type distPlus = { guesstimatorString: option(string), }; -type distPlusIngredients = { - guesstimatorString: string, - domain, - unit: distributionUnit, -}; - module DistributionUnit = { let toJson = (distributionUnit: distributionUnit) => switch (distributionUnit) { diff --git a/src/distPlus/renderers/DistPlusIngredients.re b/src/distPlus/renderers/DistPlusRenderer.re similarity index 50% rename from src/distPlus/renderers/DistPlusIngredients.re rename to src/distPlus/renderers/DistPlusRenderer.re index 76bdaba9..4e23e73f 100644 --- a/src/distPlus/renderers/DistPlusIngredients.re +++ b/src/distPlus/renderers/DistPlusRenderer.re @@ -1,20 +1,14 @@ -open DistTypes; - -let make = - (~guesstimatorString, ~domain=Complete, ~unit=UnspecifiedDistribution, ()) - : distPlusIngredients => { - guesstimatorString, - domain, - unit, +let truncateIfShould = + ( + {recommendedLength, shouldTruncate}: RenderTypes.DistPlusRenderer.inputs, + {sampling:shape}: RenderTypes.ShapeRenderer.Combined.outputs, + dist, + ) => { + (shouldTruncate && (shape |> E.O.isSome)) + ? dist |> Distributions.DistPlus.T.truncate(recommendedLength) : dist; }; -let truncateIfShould = (inputs: RenderTypes.DistPlusRenderer.inputs, dist) => { - inputs.shouldTruncate - ? dist - : dist |> Distributions.DistPlus.T.truncate(inputs.recommendedLength); -}; - -let toDistPlus = (inputs: RenderTypes.DistPlusRenderer.inputs): option(distPlus) => { +let run = (inputs: RenderTypes.DistPlusRenderer.inputs): RenderTypes.DistPlusRenderer.outputs => { let toDist = shape => Distributions.DistPlus.make( ~shape, @@ -24,16 +18,16 @@ let toDistPlus = (inputs: RenderTypes.DistPlusRenderer.inputs): option(distPlus) (), ) |> Distributions.DistPlus.T.scaleToIntegralSum(~intendedSum=1.0); - let shape = - GuesstimatorToShape.run({ + let outputs = + ShapeRenderer.run({ samplingInputs: inputs.samplingInputs, guesstimatorString: inputs.distPlusIngredients.guesstimatorString, symbolicInputs: { length: inputs.recommendedLength, }, }) - |> GuesstimatorToShape.getShape; + let shape = outputs |> RenderTypes.ShapeRenderer.Combined.getShape let dist = - shape |> E.O.fmap(toDist) |> E.O.fmap(truncateIfShould(inputs)); - dist; + shape |> E.O.fmap(toDist) |> E.O.fmap(truncateIfShould(inputs, outputs)); + RenderTypes.DistPlusRenderer.Outputs.make(outputs, dist) }; \ No newline at end of file diff --git a/src/distPlus/renderers/RenderTypes.re b/src/distPlus/renderers/RenderTypes.re index 9d3d281c..9cf613b2 100644 --- a/src/distPlus/renderers/RenderTypes.re +++ b/src/distPlus/renderers/RenderTypes.re @@ -59,18 +59,43 @@ module ShapeRenderer = { symbolic: option(Belt.Result.t(Symbolic.outputs, string)), sampling: option(Sampling.outputs), }; + let getShape = (r: outputs) => + switch (r.symbolic, r.sampling) { + | (Some(Ok({shape})), _) => Some(shape) + | (_, Some({shape})) => shape + | _ => None + }; }; }; module DistPlusRenderer = { let defaultRecommendedLength = 10000; let defaultShouldTruncate = true; + type ingredients = { + guesstimatorString: string, + domain: DistTypes.domain, + unit: DistTypes.distributionUnit, + }; type inputs = { - distPlusIngredients: DistTypes.distPlusIngredients, + distPlusIngredients: ingredients, samplingInputs: ShapeRenderer.Sampling.inputs, recommendedLength: int, shouldTruncate: bool, }; + module Ingredients = { + let make = + ( + ~guesstimatorString, + ~domain=DistTypes.Complete, + ~unit=DistTypes.UnspecifiedDistribution, + (), + ) + : ingredients => { + guesstimatorString, + domain, + unit, + }; + }; let make = ( ~samplingInputs=ShapeRenderer.Sampling.Inputs.empty, @@ -85,4 +110,13 @@ module DistPlusRenderer = { recommendedLength, shouldTruncate, }; + 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}; + } }; \ No newline at end of file diff --git a/src/distPlus/renderers/GuesstimatorToShape.re b/src/distPlus/renderers/ShapeRenderer.re similarity index 74% rename from src/distPlus/renderers/GuesstimatorToShape.re rename to src/distPlus/renderers/ShapeRenderer.re index cf988330..7c346135 100644 --- a/src/distPlus/renderers/GuesstimatorToShape.re +++ b/src/distPlus/renderers/ShapeRenderer.re @@ -4,13 +4,6 @@ let runSymbolic = graph |> E.R.fmap(g => RenderTypes.ShapeRenderer.Symbolic.make(g, SymbolicDist.toShape(length,g))) } -let getShape = (r: RenderTypes.ShapeRenderer.Combined.outputs) => - switch (r.symbolic, r.sampling) { - | (Some(Ok({shape})), _) => Some(shape) - | (_, Some({shape})) => shape - | _ => None - }; - let run = ( inputs: RenderTypes.ShapeRenderer.Combined.inputs @@ -19,8 +12,8 @@ let run = let symbolic = runSymbolic(inputs.guesstimatorString, inputs.symbolicInputs.length); let sampling = switch (symbolic) { - | Ok(r) => None - | Error(r) => + | Ok(_) => None + | Error(_) => Samples.T.fromGuesstimatorString( ~guesstimatorString=inputs.guesstimatorString, ~samplingInputs=inputs.samplingInputs, diff --git a/src/interface/FormBuilder.re b/src/interface/FormBuilder.re index 464aff41..a143e0bb 100644 --- a/src/interface/FormBuilder.re +++ b/src/interface/FormBuilder.re @@ -17,10 +17,16 @@ let propValue = (t: Prop.Value.t) => { switch (t) { | SelectSingle(r) => r |> ReasonReact.string | ConditionalArray(r) => "Array" |> ReasonReact.string - | DistPlusIngredients((r: DistTypes.distPlusIngredients)) => + | DistPlusIngredients((r: RenderTypes.DistPlusRenderer.ingredients)) => let newDistribution = - RenderTypes.DistPlusRenderer.make(~distPlusIngredients=r, ~recommendedLength=1000, ~shouldTruncate=true,()) - |> DistPlusIngredients.toDistPlus + RenderTypes.DistPlusRenderer.make( + ~distPlusIngredients=r, + ~recommendedLength=10000, + ~shouldTruncate=true, + (), + ) + |> DistPlusRenderer.run + |> RenderTypes.DistPlusRenderer.Outputs.distplus; switch (newDistribution) { | Some(distribution) =>
diff --git a/src/interface/Prop.re b/src/interface/Prop.re index 95a27f38..c956da04 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(DistTypes.distPlusIngredients) + | DistPlusIngredients(RenderTypes.DistPlusRenderer.ingredients) | ConditionalArray(array(conditional)) | FloatCdf(string); @@ -85,7 +85,7 @@ module ValueCluster = { [ | `combination(range(MomentRe.Moment.t)) | `item(string)], ) | Probability([ | `item(string)]) - | DistPlusIngredients([ | `item(DistTypes.distPlusIngredients)]) + | DistPlusIngredients([ | `item(RenderTypes.DistPlusRenderer.ingredients)]) | ConditionalArray([ | `item(array(conditional))]) | FloatCdf([ | `item(string)]); }; diff --git a/src/models/EAFunds.re b/src/models/EAFunds.re index ecdd25f2..b56fd982 100644 --- a/src/models/EAFunds.re +++ b/src/models/EAFunds.re @@ -111,7 +111,8 @@ module Model = { let getGlobalCatastropheChance = dateTime => { GlobalCatastrophe.makeI(MomentRe.momentNow()) |> RenderTypes.DistPlusRenderer.make(~distPlusIngredients=_, ()) - |> DistPlusIngredients.toDistPlus + |> DistPlusRenderer.run + |> RenderTypes.DistPlusRenderer.Outputs.distplus |> E.O.bind(_, Distributions.DistPlusTime.Integral.xToY(Time(dateTime))); }; @@ -152,7 +153,7 @@ module Model = { }; let distPlusIngredients = - DistPlusIngredients.make( + RenderTypes.DistPlusRenderer.Ingredients.make( ~guesstimatorString=str, ~domain=Complete, ~unit=UnspecifiedDistribution, @@ -162,7 +163,7 @@ module Model = { | CHANCE_OF_EXISTENCE => Prop.Value.DistPlusIngredients( - DistPlusIngredients.make( + RenderTypes.DistPlusRenderer.Ingredients.make( ~guesstimatorString= GuesstimatorDist.min( GlobalCatastrophe.guesstimatorString, diff --git a/src/models/GlobalCatastrophe.re b/src/models/GlobalCatastrophe.re index fe507c5e..e01422b3 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) => { - DistPlusIngredients.make( + RenderTypes.DistPlusRenderer.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 7b9bb846..9ff30a77 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) => { - DistPlusIngredients.make( + RenderTypes.DistPlusRenderer.Ingredients.make( ~guesstimatorString=guesstimatorString(age), ~unit=TimeDistribution({zero: MomentRe.momentNow(), unit: `years}), ~domain=RightLimited({xPoint: 300.0, excludingProbabilityMass: 0.3}), From 58c6c165f33bbbfa919d3333cc922282d044394f Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Sun, 5 Apr 2020 12:14:07 +0100 Subject: [PATCH 4/5] Minor fixes --- src/distPlus/renderers/DistPlusRenderer.re | 15 +++++++++------ src/distPlus/renderers/RenderTypes.re | 5 +++++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/distPlus/renderers/DistPlusRenderer.re b/src/distPlus/renderers/DistPlusRenderer.re index 4e23e73f..36d8c857 100644 --- a/src/distPlus/renderers/DistPlusRenderer.re +++ b/src/distPlus/renderers/DistPlusRenderer.re @@ -1,14 +1,17 @@ let truncateIfShould = ( {recommendedLength, shouldTruncate}: RenderTypes.DistPlusRenderer.inputs, - {sampling:shape}: RenderTypes.ShapeRenderer.Combined.outputs, + outputs: RenderTypes.ShapeRenderer.Combined.outputs, dist, ) => { - (shouldTruncate && (shape |> E.O.isSome)) + shouldTruncate + && RenderTypes.ShapeRenderer.Combined.methodUsed(outputs) == `Sampling ? dist |> Distributions.DistPlus.T.truncate(recommendedLength) : dist; }; -let run = (inputs: RenderTypes.DistPlusRenderer.inputs): RenderTypes.DistPlusRenderer.outputs => { +let run = + (inputs: RenderTypes.DistPlusRenderer.inputs) + : RenderTypes.DistPlusRenderer.outputs => { let toDist = shape => Distributions.DistPlus.make( ~shape, @@ -25,9 +28,9 @@ let run = (inputs: RenderTypes.DistPlusRenderer.inputs): RenderTypes.DistPlusRen symbolicInputs: { length: inputs.recommendedLength, }, - }) - let shape = outputs |> RenderTypes.ShapeRenderer.Combined.getShape + }); + let shape = outputs |> RenderTypes.ShapeRenderer.Combined.getShape; let dist = shape |> E.O.fmap(toDist) |> E.O.fmap(truncateIfShould(inputs, outputs)); - RenderTypes.DistPlusRenderer.Outputs.make(outputs, dist) + RenderTypes.DistPlusRenderer.Outputs.make(outputs, dist); }; \ No newline at end of file diff --git a/src/distPlus/renderers/RenderTypes.re b/src/distPlus/renderers/RenderTypes.re index 9cf613b2..a8c2161d 100644 --- a/src/distPlus/renderers/RenderTypes.re +++ b/src/distPlus/renderers/RenderTypes.re @@ -59,6 +59,11 @@ module ShapeRenderer = { symbolic: option(Belt.Result.t(Symbolic.outputs, string)), sampling: option(Sampling.outputs), }; + let methodUsed = ({symbolic, sampling}:outputs) => switch(symbolic, sampling){ + | (Some(Ok(_)), _) => `Symbolic + | (None, Some({shape: Some(_)})) => `Sampling + | _ => `None + } let getShape = (r: outputs) => switch (r.symbolic, r.sampling) { | (Some(Ok({shape})), _) => Some(shape) From 8b5393cd8f1f6ca92adba5d9cff931a57c18b746 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Sun, 5 Apr 2020 12:16:04 +0100 Subject: [PATCH 5/5] Fixed test page --- showcase/entries/Continuous.re | 102 ++++++++++++++++----------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/showcase/entries/Continuous.re b/showcase/entries/Continuous.re index a63fb2ab..7d744480 100644 --- a/showcase/entries/Continuous.re +++ b/showcase/entries/Continuous.re @@ -22,27 +22,27 @@ let setup = dist => |> RenderTypes.DistPlusRenderer.Outputs.distplus |> E.O.React.fmapOrNull(distPlus => ); -// let simpleExample = (name, guesstimatorString) => -// <> -//

-// {name |> ReasonReact.string} -//

-// {setup(DistPlusIngredients.make(~guesstimatorString, ()))} -// ; +let simpleExample = (name, guesstimatorString) => + <> +

+ {name |> ReasonReact.string} +

+ {setup(RenderTypes.DistPlusRenderer.Ingredients.make(~guesstimatorString, ()))} + ; -// let timeExample = (name, guesstimatorString) => -// <> -//

-// {name |> ReasonReact.string} -//

-// {setup( -// DistPlusIngredients.make( -// ~guesstimatorString, -// ~unit=TimeDistribution({zero: MomentRe.momentNow(), unit: `years}), -// (), -// ), -// )} -// ; +let timeExample = (name, guesstimatorString) => + <> +

+ {name |> ReasonReact.string} +

+ {setup( + RenderTypes.DistPlusRenderer.Ingredients.make( + ~guesstimatorString, + ~unit=TimeDistribution({zero: MomentRe.momentNow(), unit: `years}), + (), + ), + )} + ; let distributions = () =>
@@ -50,37 +50,37 @@ let distributions = () =>

{"Initial Section" |> ReasonReact.string}

- // {simpleExample("Continuous", "5 to 20")} - // {simpleExample("Continuous, wide range", "1 to 1000000")} - // {simpleExample("Continuous, tiny values", "0.000000001 to 0.00000001")} - // {simpleExample( - // "Continuous large values", - // "50000000000000 to 200000000000000000", - // )} - // {simpleExample("Discrete", "floor(10 to 20)")} - // {simpleExample( - // "Discrete and below 0, normal(10,30)", - // "floor(normal(10,30))", - // )} - // {simpleExample("Discrete, wide range", "floor(10 to 200000)")} - // {simpleExample("Mixed", "mm(5 to 20, floor(20 to 30), [.5,.5])")} - // {simpleExample("Mixed, Early-Discrete Point", "mm(1, 5 to 20, [.5,.5])")} - // {simpleExample( - // "Mixed, Two-Discrete Points", - // "mm(0,10, 5 to 20, [.5,.5,.5])", - // )} - //

- // {"Over Time" |> ReasonReact.string} - //

- // {timeExample("Continuous", "5 to 20")} - // {timeExample("Continuous Over Long Period", "500 to 200000")} - // {timeExample("Continuous Over Short Period", "0.0001 to 0.001")} - // {timeExample( - // "Continuous Over Very Long Period", - // "500 to 20000000000000", - // )} - // {timeExample("Discrete", "floor(5 to 20)")} - // {timeExample("Mixed", "mm(5 to 20, floor(5 to 20), [.5,.5])")} + {simpleExample("Continuous", "5 to 20")} + {simpleExample("Continuous, wide range", "1 to 1000000")} + {simpleExample("Continuous, tiny values", "0.000000001 to 0.00000001")} + {simpleExample( + "Continuous large values", + "50000000000000 to 200000000000000000", + )} + {simpleExample("Discrete", "floor(10 to 20)")} + {simpleExample( + "Discrete and below 0, normal(10,30)", + "floor(normal(10,30))", + )} + {simpleExample("Discrete, wide range", "floor(10 to 200000)")} + {simpleExample("Mixed", "mm(5 to 20, floor(20 to 30), [.5,.5])")} + {simpleExample("Mixed, Early-Discrete Point", "mm(1, 5 to 20, [.5,.5])")} + {simpleExample( + "Mixed, Two-Discrete Points", + "mm(0,10, 5 to 20, [.5,.5,.5])", + )} +

+ {"Over Time" |> ReasonReact.string} +

+ {timeExample("Continuous", "5 to 20")} + {timeExample("Continuous Over Long Period", "500 to 200000")} + {timeExample("Continuous Over Short Period", "0.0001 to 0.001")} + {timeExample( + "Continuous Over Very Long Period", + "500 to 20000000000000", + )} + {timeExample("Discrete", "floor(5 to 20)")} + {timeExample("Mixed", "mm(5 to 20, floor(5 to 20), [.5,.5])")}
;