Cleanup to return more from DistPlusRenderer

This commit is contained in:
Ozzie Gooen 2020-04-05 12:00:25 +01:00
parent 990f01b8d6
commit 82595ae167
12 changed files with 132 additions and 105 deletions

View File

@ -5,41 +5,44 @@
// floor(3 to 4) // floor(3 to 4)
// uniform(0,1) > 0.3 ? lognormal(6.652, -0.41): 0 // uniform(0,1) > 0.3 ? lognormal(6.652, -0.41): 0
let timeDist = let timeDist ={
DistPlusIngredients.make( let ingredients = RenderTypes.DistPlusRenderer.Ingredients.make(
~guesstimatorString="(floor(10 to 15))", ~guesstimatorString="(floor(10 to 15))",
~domain=RightLimited({xPoint: 50.0, excludingProbabilityMass: 0.3}), ~domain=RightLimited({xPoint: 50.0, excludingProbabilityMass: 0.3}),
~unit= ~unit=
DistTypes.TimeDistribution({zero: MomentRe.momentNow(), unit: `years}), DistTypes.TimeDistribution({zero: MomentRe.momentNow(), unit: `years}),
(), ());
); let inputs = RenderTypes.DistPlusRenderer.make(~distPlusIngredients=ingredients,())
inputs |> DistPlusRenderer.run
}
let setup = dist => let setup = dist =>
RenderTypes.DistPlusRenderer.make(~distPlusIngredients=dist,()) RenderTypes.DistPlusRenderer.make(~distPlusIngredients=dist,())
|> DistPlusIngredients.toDistPlus |> DistPlusRenderer.run
|> RenderTypes.DistPlusRenderer.Outputs.distplus
|> E.O.React.fmapOrNull(distPlus => <DistPlusPlot distPlus />); |> E.O.React.fmapOrNull(distPlus => <DistPlusPlot distPlus />);
let simpleExample = (name, guesstimatorString) => // let simpleExample = (name, guesstimatorString) =>
<> // <>
<h3 className="text-gray-600 text-lg font-bold"> // <h3 className="text-gray-600 text-lg font-bold">
{name |> ReasonReact.string} // {name |> ReasonReact.string}
</h3> // </h3>
{setup(DistPlusIngredients.make(~guesstimatorString, ()))} // {setup(DistPlusIngredients.make(~guesstimatorString, ()))}
</>; // </>;
let timeExample = (name, guesstimatorString) => // let timeExample = (name, guesstimatorString) =>
<> // <>
<h3 className="text-gray-600 text-lg font-bold"> // <h3 className="text-gray-600 text-lg font-bold">
{name |> ReasonReact.string} // {name |> ReasonReact.string}
</h3> // </h3>
{setup( // {setup(
DistPlusIngredients.make( // DistPlusIngredients.make(
~guesstimatorString, // ~guesstimatorString,
~unit=TimeDistribution({zero: MomentRe.momentNow(), unit: `years}), // ~unit=TimeDistribution({zero: MomentRe.momentNow(), unit: `years}),
(), // (),
), // ),
)} // )}
</>; // </>;
let distributions = () => let distributions = () =>
<div> <div>
@ -47,37 +50,37 @@ let distributions = () =>
<h2 className="text-gray-800 text-xl font-bold"> <h2 className="text-gray-800 text-xl font-bold">
{"Initial Section" |> ReasonReact.string} {"Initial Section" |> ReasonReact.string}
</h2> </h2>
{simpleExample("Continuous", "5 to 20")} // {simpleExample("Continuous", "5 to 20")}
{simpleExample("Continuous, wide range", "1 to 1000000")} // {simpleExample("Continuous, wide range", "1 to 1000000")}
{simpleExample("Continuous, tiny values", "0.000000001 to 0.00000001")} // {simpleExample("Continuous, tiny values", "0.000000001 to 0.00000001")}
{simpleExample( // {simpleExample(
"Continuous large values", // "Continuous large values",
"50000000000000 to 200000000000000000", // "50000000000000 to 200000000000000000",
)} // )}
{simpleExample("Discrete", "floor(10 to 20)")} // {simpleExample("Discrete", "floor(10 to 20)")}
{simpleExample( // {simpleExample(
"Discrete and below 0, normal(10,30)", // "Discrete and below 0, normal(10,30)",
"floor(normal(10,30))", // "floor(normal(10,30))",
)} // )}
{simpleExample("Discrete, wide range", "floor(10 to 200000)")} // {simpleExample("Discrete, wide range", "floor(10 to 200000)")}
{simpleExample("Mixed", "mm(5 to 20, floor(20 to 30), [.5,.5])")} // {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, Early-Discrete Point", "mm(1, 5 to 20, [.5,.5])")}
{simpleExample( // {simpleExample(
"Mixed, Two-Discrete Points", // "Mixed, Two-Discrete Points",
"mm(0,10, 5 to 20, [.5,.5,.5])", // "mm(0,10, 5 to 20, [.5,.5,.5])",
)} // )}
<h2 className="text-gray-800 text-xl font-bold"> // <h2 className="text-gray-800 text-xl font-bold">
{"Over Time" |> ReasonReact.string} // {"Over Time" |> ReasonReact.string}
</h2> // </h2>
{timeExample("Continuous", "5 to 20")} // {timeExample("Continuous", "5 to 20")}
{timeExample("Continuous Over Long Period", "500 to 200000")} // {timeExample("Continuous Over Long Period", "500 to 200000")}
{timeExample("Continuous Over Short Period", "0.0001 to 0.001")} // {timeExample("Continuous Over Short Period", "0.0001 to 0.001")}
{timeExample( // {timeExample(
"Continuous Over Very Long Period", // "Continuous Over Very Long Period",
"500 to 20000000000000", // "500 to 20000000000000",
)} // )}
{timeExample("Discrete", "floor(5 to 20)")} // {timeExample("Discrete", "floor(5 to 20)")}
{timeExample("Mixed", "mm(5 to 20, floor(5 to 20), [.5,.5])")} // {timeExample("Mixed", "mm(5 to 20, floor(5 to 20), [.5,.5])")}
</div> </div>
</div>; </div>;

View File

@ -127,7 +127,7 @@ module DemoDist = {
{switch (domain, unit, options) { {switch (domain, unit, options) {
| (Some(domain), Some(unit), Some(options)) => | (Some(domain), Some(unit), Some(options)) =>
let distPlusIngredients = let distPlusIngredients =
DistPlusIngredients.make( RenderTypes.DistPlusRenderer.Ingredients.make(
~guesstimatorString, ~guesstimatorString,
~domain, ~domain,
~unit, ~unit,
@ -141,10 +141,12 @@ module DemoDist = {
kernelWidth: options.kernelWidth, kernelWidth: options.kernelWidth,
}, },
~distPlusIngredients, ~distPlusIngredients,
~shouldTruncate=options.truncateTo |> E.O.isSome,
~recommendedLength=options.truncateTo |> E.O.default(10000),
(), (),
); );
let distPlus = DistPlusIngredients.toDistPlus(inputs); let response = DistPlusRenderer.run(inputs);
switch (distPlus) { switch (RenderTypes.DistPlusRenderer.Outputs.distplus(response)) {
| Some(distPlus) => <DistPlusPlot distPlus /> | Some(distPlus) => <DistPlusPlot distPlus />
| _ => | _ =>
"Correct Guesstimator string input to show a distribution." "Correct Guesstimator string input to show a distribution."

View File

@ -60,12 +60,6 @@ type distPlus = {
guesstimatorString: option(string), guesstimatorString: option(string),
}; };
type distPlusIngredients = {
guesstimatorString: string,
domain,
unit: distributionUnit,
};
module DistributionUnit = { module DistributionUnit = {
let toJson = (distributionUnit: distributionUnit) => let toJson = (distributionUnit: distributionUnit) =>
switch (distributionUnit) { switch (distributionUnit) {

View File

@ -1,20 +1,14 @@
open DistTypes; let truncateIfShould =
(
let make = {recommendedLength, shouldTruncate}: RenderTypes.DistPlusRenderer.inputs,
(~guesstimatorString, ~domain=Complete, ~unit=UnspecifiedDistribution, ()) {sampling:shape}: RenderTypes.ShapeRenderer.Combined.outputs,
: distPlusIngredients => { dist,
guesstimatorString, ) => {
domain, (shouldTruncate && (shape |> E.O.isSome))
unit, ? dist |> Distributions.DistPlus.T.truncate(recommendedLength) : dist;
}; };
let truncateIfShould = (inputs: RenderTypes.DistPlusRenderer.inputs, dist) => { let run = (inputs: RenderTypes.DistPlusRenderer.inputs): RenderTypes.DistPlusRenderer.outputs => {
inputs.shouldTruncate
? dist
: dist |> Distributions.DistPlus.T.truncate(inputs.recommendedLength);
};
let toDistPlus = (inputs: RenderTypes.DistPlusRenderer.inputs): option(distPlus) => {
let toDist = shape => let toDist = shape =>
Distributions.DistPlus.make( Distributions.DistPlus.make(
~shape, ~shape,
@ -24,16 +18,16 @@ let toDistPlus = (inputs: RenderTypes.DistPlusRenderer.inputs): option(distPlus)
(), (),
) )
|> Distributions.DistPlus.T.scaleToIntegralSum(~intendedSum=1.0); |> Distributions.DistPlus.T.scaleToIntegralSum(~intendedSum=1.0);
let shape = let outputs =
GuesstimatorToShape.run({ ShapeRenderer.run({
samplingInputs: inputs.samplingInputs, samplingInputs: inputs.samplingInputs,
guesstimatorString: inputs.distPlusIngredients.guesstimatorString, guesstimatorString: inputs.distPlusIngredients.guesstimatorString,
symbolicInputs: { symbolicInputs: {
length: inputs.recommendedLength, length: inputs.recommendedLength,
}, },
}) })
|> GuesstimatorToShape.getShape; let shape = outputs |> RenderTypes.ShapeRenderer.Combined.getShape
let dist = let dist =
shape |> E.O.fmap(toDist) |> E.O.fmap(truncateIfShould(inputs)); shape |> E.O.fmap(toDist) |> E.O.fmap(truncateIfShould(inputs, outputs));
dist; RenderTypes.DistPlusRenderer.Outputs.make(outputs, dist)
}; };

View File

@ -59,18 +59,43 @@ module ShapeRenderer = {
symbolic: option(Belt.Result.t(Symbolic.outputs, string)), symbolic: option(Belt.Result.t(Symbolic.outputs, string)),
sampling: option(Sampling.outputs), sampling: option(Sampling.outputs),
}; };
let getShape = (r: outputs) =>
switch (r.symbolic, r.sampling) {
| (Some(Ok({shape})), _) => Some(shape)
| (_, Some({shape})) => shape
| _ => None
};
}; };
}; };
module DistPlusRenderer = { module DistPlusRenderer = {
let defaultRecommendedLength = 10000; let defaultRecommendedLength = 10000;
let defaultShouldTruncate = true; let defaultShouldTruncate = true;
type ingredients = {
guesstimatorString: string,
domain: DistTypes.domain,
unit: DistTypes.distributionUnit,
};
type inputs = { type inputs = {
distPlusIngredients: DistTypes.distPlusIngredients, distPlusIngredients: ingredients,
samplingInputs: ShapeRenderer.Sampling.inputs, samplingInputs: ShapeRenderer.Sampling.inputs,
recommendedLength: int, recommendedLength: int,
shouldTruncate: bool, shouldTruncate: bool,
}; };
module Ingredients = {
let make =
(
~guesstimatorString,
~domain=DistTypes.Complete,
~unit=DistTypes.UnspecifiedDistribution,
(),
)
: ingredients => {
guesstimatorString,
domain,
unit,
};
};
let make = let make =
( (
~samplingInputs=ShapeRenderer.Sampling.Inputs.empty, ~samplingInputs=ShapeRenderer.Sampling.Inputs.empty,
@ -85,4 +110,13 @@ module DistPlusRenderer = {
recommendedLength, recommendedLength,
shouldTruncate, 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};
}
}; };

View File

@ -4,13 +4,6 @@ let runSymbolic =
graph |> E.R.fmap(g => RenderTypes.ShapeRenderer.Symbolic.make(g, SymbolicDist.toShape(length,g))) 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 = let run =
( (
inputs: RenderTypes.ShapeRenderer.Combined.inputs inputs: RenderTypes.ShapeRenderer.Combined.inputs
@ -19,8 +12,8 @@ let run =
let symbolic = runSymbolic(inputs.guesstimatorString, inputs.symbolicInputs.length); let symbolic = runSymbolic(inputs.guesstimatorString, inputs.symbolicInputs.length);
let sampling = let sampling =
switch (symbolic) { switch (symbolic) {
| Ok(r) => None | Ok(_) => None
| Error(r) => | Error(_) =>
Samples.T.fromGuesstimatorString( Samples.T.fromGuesstimatorString(
~guesstimatorString=inputs.guesstimatorString, ~guesstimatorString=inputs.guesstimatorString,
~samplingInputs=inputs.samplingInputs, ~samplingInputs=inputs.samplingInputs,

View File

@ -17,10 +17,16 @@ let propValue = (t: Prop.Value.t) => {
switch (t) { switch (t) {
| SelectSingle(r) => r |> ReasonReact.string | SelectSingle(r) => r |> ReasonReact.string
| ConditionalArray(r) => "Array" |> ReasonReact.string | ConditionalArray(r) => "Array" |> ReasonReact.string
| DistPlusIngredients((r: DistTypes.distPlusIngredients)) => | DistPlusIngredients((r: RenderTypes.DistPlusRenderer.ingredients)) =>
let newDistribution = let newDistribution =
RenderTypes.DistPlusRenderer.make(~distPlusIngredients=r, ~recommendedLength=1000, ~shouldTruncate=true,()) RenderTypes.DistPlusRenderer.make(
|> DistPlusIngredients.toDistPlus ~distPlusIngredients=r,
~recommendedLength=10000,
~shouldTruncate=true,
(),
)
|> DistPlusRenderer.run
|> RenderTypes.DistPlusRenderer.Outputs.distplus;
switch (newDistribution) { switch (newDistribution) {
| Some(distribution) => | Some(distribution) =>
<div> <DistPlusPlot distPlus=distribution /> </div> <div> <DistPlusPlot distPlus=distribution /> </div>

View File

@ -9,7 +9,7 @@ module Value = {
| DateTime(MomentRe.Moment.t) | DateTime(MomentRe.Moment.t)
| FloatPoint(float) | FloatPoint(float)
| Probability(float) | Probability(float)
| DistPlusIngredients(DistTypes.distPlusIngredients) | DistPlusIngredients(RenderTypes.DistPlusRenderer.ingredients)
| ConditionalArray(array(conditional)) | ConditionalArray(array(conditional))
| FloatCdf(string); | FloatCdf(string);
@ -85,7 +85,7 @@ module ValueCluster = {
[ | `combination(range(MomentRe.Moment.t)) | `item(string)], [ | `combination(range(MomentRe.Moment.t)) | `item(string)],
) )
| Probability([ | `item(string)]) | Probability([ | `item(string)])
| DistPlusIngredients([ | `item(DistTypes.distPlusIngredients)]) | DistPlusIngredients([ | `item(RenderTypes.DistPlusRenderer.ingredients)])
| ConditionalArray([ | `item(array(conditional))]) | ConditionalArray([ | `item(array(conditional))])
| FloatCdf([ | `item(string)]); | FloatCdf([ | `item(string)]);
}; };

View File

@ -111,7 +111,8 @@ module Model = {
let getGlobalCatastropheChance = dateTime => { let getGlobalCatastropheChance = dateTime => {
GlobalCatastrophe.makeI(MomentRe.momentNow()) GlobalCatastrophe.makeI(MomentRe.momentNow())
|> RenderTypes.DistPlusRenderer.make(~distPlusIngredients=_, ()) |> RenderTypes.DistPlusRenderer.make(~distPlusIngredients=_, ())
|> DistPlusIngredients.toDistPlus |> DistPlusRenderer.run
|> RenderTypes.DistPlusRenderer.Outputs.distplus
|> E.O.bind(_, Distributions.DistPlusTime.Integral.xToY(Time(dateTime))); |> E.O.bind(_, Distributions.DistPlusTime.Integral.xToY(Time(dateTime)));
}; };
@ -152,7 +153,7 @@ module Model = {
}; };
let distPlusIngredients = let distPlusIngredients =
DistPlusIngredients.make( RenderTypes.DistPlusRenderer.Ingredients.make(
~guesstimatorString=str, ~guesstimatorString=str,
~domain=Complete, ~domain=Complete,
~unit=UnspecifiedDistribution, ~unit=UnspecifiedDistribution,
@ -162,7 +163,7 @@ module Model = {
| CHANCE_OF_EXISTENCE => | CHANCE_OF_EXISTENCE =>
Prop.Value.DistPlusIngredients( Prop.Value.DistPlusIngredients(
DistPlusIngredients.make( RenderTypes.DistPlusRenderer.Ingredients.make(
~guesstimatorString= ~guesstimatorString=
GuesstimatorDist.min( GuesstimatorDist.min(
GlobalCatastrophe.guesstimatorString, GlobalCatastrophe.guesstimatorString,

View File

@ -1,7 +1,7 @@
let guesstimatorString = "uniform(1, 100)"; let guesstimatorString = "uniform(1, 100)";
let makeI = (currentDateTime: MomentRe.Moment.t) => { let makeI = (currentDateTime: MomentRe.Moment.t) => {
DistPlusIngredients.make( RenderTypes.DistPlusRenderer.Ingredients.make(
~guesstimatorString, ~guesstimatorString,
~unit=TimeDistribution({zero: currentDateTime, unit: `years}), ~unit=TimeDistribution({zero: currentDateTime, unit: `years}),
~domain=RightLimited({xPoint: 300.0, excludingProbabilityMass: 0.3}), ~domain=RightLimited({xPoint: 300.0, excludingProbabilityMass: 0.3}),

View File

@ -2,7 +2,7 @@ let guesstimatorString = age =>
GuesstimatorDist.normal(72.0 -. age, 5.0 -. age *. 0.01); GuesstimatorDist.normal(72.0 -. age, 5.0 -. age *. 0.01);
let makeI = (age: float) => { let makeI = (age: float) => {
DistPlusIngredients.make( RenderTypes.DistPlusRenderer.Ingredients.make(
~guesstimatorString=guesstimatorString(age), ~guesstimatorString=guesstimatorString(age),
~unit=TimeDistribution({zero: MomentRe.momentNow(), unit: `years}), ~unit=TimeDistribution({zero: MomentRe.momentNow(), unit: `years}),
~domain=RightLimited({xPoint: 300.0, excludingProbabilityMass: 0.3}), ~domain=RightLimited({xPoint: 300.0, excludingProbabilityMass: 0.3}),