diff --git a/showcase/entries/Continuous.re b/showcase/entries/Continuous.re index 2d0d2e4d..7d744480 100644 --- a/showcase/entries/Continuous.re +++ b/showcase/entries/Continuous.re @@ -5,22 +5,21 @@ // 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 => - dist - |> DistPlusIngredients.toDistPlus( - ~sampleCount=2000, - ~outputXYPoints=1000, - ~truncateTo=Some(1000), - ) + RenderTypes.DistPlusRenderer.make(~distPlusIngredients=dist,()) + |> DistPlusRenderer.run + |> RenderTypes.DistPlusRenderer.Outputs.distplus |> E.O.React.fmapOrNull(distPlus => ); let simpleExample = (name, guesstimatorString) => @@ -28,7 +27,7 @@ let simpleExample = (name, guesstimatorString) =>

{name |> ReasonReact.string}

- {setup(DistPlusIngredients.make(~guesstimatorString, ()))} + {setup(RenderTypes.DistPlusRenderer.Ingredients.make(~guesstimatorString, ()))} ; let timeExample = (name, guesstimatorString) => @@ -37,7 +36,7 @@ let timeExample = (name, guesstimatorString) => {name |> ReasonReact.string} {setup( - DistPlusIngredients.make( + RenderTypes.DistPlusRenderer.Ingredients.make( ~guesstimatorString, ~unit=TimeDistribution({zero: MomentRe.momentNow(), unit: `years}), (), @@ -51,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/utility/GuesstimatorDist.re b/src/GuesstimatorDist.re similarity index 100% rename from src/utility/GuesstimatorDist.re rename to src/GuesstimatorDist.re diff --git a/src/components/DistBuilder.re b/src/components/DistBuilder.re index bfcbcb51..2fc9aa2c 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.ShapeRenderer.Sampling.inputs, + guesstimatorString: string, + length: int, + shouldTruncateSampledDistribution: int, +}; + module DemoDist = { [@react.component] let make = (~guesstimatorString, ~domain, ~unit, ~options) => { @@ -119,15 +126,27 @@ 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, - ); - switch (distPlus) { + let distPlusIngredients = + RenderTypes.DistPlusRenderer.Ingredients.make( + ~guesstimatorString, + ~domain, + ~unit, + (), + ); + let inputs = + RenderTypes.DistPlusRenderer.make( + ~samplingInputs={ + sampleCount: Some(options.sampleCount), + outputXYPoints: Some(options.outputXYPoints), + kernelWidth: options.kernelWidth, + }, + ~distPlusIngredients, + ~shouldTruncate=options.truncateTo |> E.O.isSome, + ~recommendedLength=options.truncateTo |> E.O.default(10000), + (), + ); + let response = DistPlusRenderer.run(inputs); + switch (RenderTypes.DistPlusRenderer.Outputs.distplus(response)) { | Some(distPlus) => | _ => "Correct Guesstimator string input to show a distribution." @@ -160,9 +179,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 +265,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/DistTypes.re b/src/distPlus/distribution/DistTypes.re similarity index 97% rename from src/distribution/DistTypes.re rename to src/distPlus/distribution/DistTypes.re index 8178c879..7a598c01 100644 --- a/src/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/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 90% rename from src/distribution/MixedShapeBuilder.re rename to src/distPlus/distribution/MixedShapeBuilder.re index cde9251c..949a6f20 100644 --- a/src/distribution/MixedShapeBuilder.re +++ b/src/distPlus/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 @@ -39,6 +40,8 @@ let buildSimple = (~continuous, ~discrete): option(DistTypes.shape) => { }; }; + +// 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/DistPlusRenderer.re b/src/distPlus/renderers/DistPlusRenderer.re new file mode 100644 index 00000000..36d8c857 --- /dev/null +++ b/src/distPlus/renderers/DistPlusRenderer.re @@ -0,0 +1,36 @@ +let truncateIfShould = + ( + {recommendedLength, shouldTruncate}: RenderTypes.DistPlusRenderer.inputs, + outputs: RenderTypes.ShapeRenderer.Combined.outputs, + dist, + ) => { + shouldTruncate + && RenderTypes.ShapeRenderer.Combined.methodUsed(outputs) == `Sampling + ? dist |> Distributions.DistPlus.T.truncate(recommendedLength) : dist; +}; + +let run = + (inputs: RenderTypes.DistPlusRenderer.inputs) + : RenderTypes.DistPlusRenderer.outputs => { + 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 outputs = + ShapeRenderer.run({ + samplingInputs: inputs.samplingInputs, + guesstimatorString: inputs.distPlusIngredients.guesstimatorString, + symbolicInputs: { + length: inputs.recommendedLength, + }, + }); + 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); +}; \ 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..a8c2161d --- /dev/null +++ b/src/distPlus/renderers/RenderTypes.re @@ -0,0 +1,127 @@ +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), + }; + 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) + | (_, 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: 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, + ~recommendedLength=defaultRecommendedLength, + ~shouldTruncate=defaultShouldTruncate, + ~distPlusIngredients, + (), + ) + : inputs => { + distPlusIngredients, + samplingInputs, + 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/ShapeRenderer.re b/src/distPlus/renderers/ShapeRenderer.re new file mode 100644 index 00000000..7c346135 --- /dev/null +++ b/src/distPlus/renderers/ShapeRenderer.re @@ -0,0 +1,24 @@ +let runSymbolic = + (guesstimatorString, length) =>{ + let graph = MathJsParser.fromString(guesstimatorString); + graph |> E.R.fmap(g => RenderTypes.ShapeRenderer.Symbolic.make(g, SymbolicDist.toShape(length,g))) + } + +let run = + ( + inputs: RenderTypes.ShapeRenderer.Combined.inputs + ) + : RenderTypes.ShapeRenderer.Combined.outputs => { + let symbolic = runSymbolic(inputs.guesstimatorString, inputs.symbolicInputs.length); + let sampling = + switch (symbolic) { + | Ok(_) => None + | Error(_) => + Samples.T.fromGuesstimatorString( + ~guesstimatorString=inputs.guesstimatorString, + ~samplingInputs=inputs.samplingInputs, + (), + ) + }; + {symbolic: Some(symbolic), sampling}; +}; \ 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/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 50% rename from src/Samples.re rename to src/distPlus/renderers/samplesRenderer/Samples.re index 360f8279..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"; }; @@ -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,93 @@ 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; }; - // todo: Figure out some way of doing this without having to integrate so many times. - let toShape = (~samples: t, ~outputXYPoints=3000, ~kernelWidth=10, ()) => { + 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: 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; - 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.ShapeRenderer.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.ShapeRenderer.Sampling.outputs = { + continuousParseParams: pdf |> E.O.fmap(snd), + shape, + }; + samplesParse; + }; + + let fromGuesstimatorString = + ( + ~guesstimatorString, + ~samplingInputs=RenderTypes.ShapeRenderer.Sampling.Inputs.empty, + (), + ) => { + let hasValidSamples = + Guesstimator.stringToSamples(guesstimatorString, 10) |> E.A.length > 0; + let samplingInputs = RenderTypes.ShapeRenderer.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/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 4f6225ff..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) @@ -245,6 +247,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/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 98% rename from src/symbolic/SymbolicDist.re rename to src/distPlus/symbolic/SymbolicDist.re index a4c33baf..cec8e1d6 100644 --- a/src/symbolic/SymbolicDist.re +++ b/src/distPlus/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("") }; 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 36a7e663..00000000 --- a/src/distribution/DistPlusIngredients.re +++ /dev/null @@ -1,55 +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 - }; - -let toDistPlus = - ( - ~sampleCount=2000, - ~outputXYPoints=1500, - ~truncateTo=Some(300), - ~kernelWidth=5, - t: distPlusIngredients, - ) - : option(distPlus) => { - let toDist = shape => - Distributions.DistPlus.make( - ~shape, - ~domain=t.domain, - ~unit=t.unit, - ~guesstimatorString=Some(t.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; -}; \ No newline at end of file diff --git a/src/interface/FormBuilder.re b/src/interface/FormBuilder.re index 94e7b4cb..a143e0bb 100644 --- a/src/interface/FormBuilder.re +++ b/src/interface/FormBuilder.re @@ -17,14 +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 = - DistPlusIngredients.toDistPlus( - ~sampleCount=1000, - ~outputXYPoints=2000, - ~truncateTo=Some(500), - r, - ); + 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 aeacb1b2..b56fd982 100644 --- a/src/models/EAFunds.re +++ b/src/models/EAFunds.re @@ -110,7 +110,9 @@ module Model = { // TODO: Fixe number that integral is calculated for let getGlobalCatastropheChance = dateTime => { GlobalCatastrophe.makeI(MomentRe.momentNow()) - |> DistPlusIngredients.toDistPlus + |> RenderTypes.DistPlusRenderer.make(~distPlusIngredients=_, ()) + |> DistPlusRenderer.run + |> RenderTypes.DistPlusRenderer.Outputs.distplus |> E.O.bind(_, Distributions.DistPlusTime.Integral.xToY(Time(dateTime))); }; @@ -151,7 +153,7 @@ module Model = { }; let distPlusIngredients = - DistPlusIngredients.make( + RenderTypes.DistPlusRenderer.Ingredients.make( ~guesstimatorString=str, ~domain=Complete, ~unit=UnspecifiedDistribution, @@ -161,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}),