Merge pull request #66 from foretold-app/various-enhancements-July-2020

Various enhancements July 2020
This commit is contained in:
Ozzie Gooen 2020-07-20 00:23:13 +01:00 committed by GitHub
commit 029a63853a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 182 additions and 131 deletions

View File

@ -19,8 +19,9 @@ let timeDist ={
let setup = dist => let setup = dist =>
RenderTypes.DistPlusRenderer.make(~distPlusIngredients=dist,()) RenderTypes.DistPlusRenderer.make(~distPlusIngredients=dist,())
|> DistPlusRenderer.run |> DistPlusRenderer.run
|> RenderTypes.DistPlusRenderer.Outputs.distplus |> E.R.fmap(distPlus => <DistPlusPlot distPlus />)
|> R.O.fmapOrNull(distPlus => <DistPlusPlot distPlus />); |> E.R.toOption
|> E.O.toExn("")
let simpleExample = (name, guesstimatorString) => let simpleExample = (name, guesstimatorString) =>
<> <>

View File

@ -1,8 +1,9 @@
let setup = dist => let setup = dist =>
RenderTypes.DistPlusRenderer.make(~distPlusIngredients=dist, ()) RenderTypes.DistPlusRenderer.make(~distPlusIngredients=dist, ())
|> DistPlusRenderer.run |> DistPlusRenderer.run
|> RenderTypes.DistPlusRenderer.Outputs.distplus |> E.R.fmap(distPlus => <DistPlusPlot distPlus />)
|> R.O.fmapOrNull(distPlus => <DistPlusPlot distPlus />); |> E.R.toOption
|> E.O.toExn("")
let simpleExample = (guesstimatorString, ~problem="", ()) => let simpleExample = (guesstimatorString, ~problem="", ()) =>
<> <>

View File

@ -142,18 +142,15 @@ module DemoDist = {
}, },
~distPlusIngredients, ~distPlusIngredients,
~shouldDownsample=options.downsampleTo |> E.O.isSome, ~shouldDownsample=options.downsampleTo |> E.O.isSome,
~recommendedLength=options.downsampleTo |> E.O.default(100), ~recommendedLength=options.downsampleTo |> E.O.default(1000),
(), (),
); );
let response = DistPlusRenderer.run(inputs); let response = DistPlusRenderer.run(inputs);
switch (RenderTypes.DistPlusRenderer.Outputs.distplus(response)) { switch (response) {
| Some(distPlus) => { | Ok(distPlus) =>
let normalizedDistPlus = DistPlus.T.normalize(distPlus); let normalizedDistPlus = DistPlus.T.normalize(distPlus);
<DistPlusPlot distPlus={normalizedDistPlus} />; <DistPlusPlot distPlus=normalizedDistPlus />;
} | Error(r) => r |> R.ste
| _ =>
"Correct Guesstimator string input to show a distribution."
|> R.ste
}; };
| _ => | _ =>
"Nothing to show. Try to change the distribution description." "Nothing to show. Try to change the distribution description."
@ -183,10 +180,10 @@ let make = () => {
unitType: "UnspecifiedDistribution", unitType: "UnspecifiedDistribution",
zero: MomentRe.momentNow(), zero: MomentRe.momentNow(),
unit: "days", unit: "days",
sampleCount: "3000", sampleCount: "30000",
outputXYPoints: "100", outputXYPoints: "1000",
downsampleTo: "100", downsampleTo: "",
kernelWidth: "5", kernelWidth: "",
}, },
(), (),
); );
@ -484,7 +481,10 @@ let make = () => {
/> />
</Col> </Col>
<Col span=4> <Col span=4>
<FieldFloat field=FormConfig.DownsampleTo label="Downsample To" /> <FieldFloat
field=FormConfig.DownsampleTo
label="Downsample To"
/>
</Col> </Col>
<Col span=4> <Col span=4>
<FieldFloat field=FormConfig.KernelWidth label="Kernel Width" /> <FieldFloat field=FormConfig.KernelWidth label="Kernel Width" />

View File

@ -395,7 +395,7 @@ module Draw = {
numSamples, numSamples,
{sampleCount: 10000, outputXYPoints: 10000, kernelWidth: None}, {sampleCount: 10000, outputXYPoints: 10000, kernelWidth: None},
`SymbolicDist(normal), `SymbolicDist(normal),
); ) |> E.R.toExn;
let xyShape: Types.xyShape = let xyShape: Types.xyShape =
switch (normalShape) { switch (normalShape) {
| Mixed(_) => {xs: [||], ys: [||]} | Mixed(_) => {xs: [||], ys: [||]}

View File

@ -78,7 +78,7 @@ let combinePointwise =
make( make(
~integralSumCache=combinedIntegralSum, ~integralSumCache=combinedIntegralSum,
XYShape.PointwiseCombination.combine( XYShape.PointwiseCombination.combine(
(+.), fn,
interpolator, interpolator,
t1.xyShape, t1.xyShape,
t2.xyShape, t2.xyShape,

View File

@ -10,14 +10,9 @@ let toShape = (intendedShapeLength: int, samplingInputs, node: node) => {
}); });
switch (renderResult) { switch (renderResult) {
| Ok(`RenderedDist(rs)) => | Ok(`RenderedDist(shape)) => Ok(shape)
// todo: Why is this here? It converts a mixed shape to a mixed shape. | Ok(_) => Error("Rendering failed.")
let continuous = Shape.T.toContinuous(rs); | Error(e) => Error(e)
let discrete = Shape.T.toDiscrete(rs);
let shape = MixedShapeBuilder.buildSimple(~continuous, ~discrete);
shape |> E.O.toExt("Could not build final shape.");
| Ok(_) => E.O.toExn("Rendering failed.", None)
| Error(message) => E.O.toExn("No shape found, error: " ++ message, None)
}; };
}; };

View File

@ -32,6 +32,47 @@ module AlgebraicCombination = {
); );
}; };
let nodeScore: node => int =
fun
| `SymbolicDist(`Float(_)) => 1
| `SymbolicDist(_) => 1000
| `RenderedDist(Discrete(m)) => m.xyShape |> XYShape.T.length
| `RenderedDist(Mixed(_)) => 1000
| `RenderedDist(Continuous(_)) => 1000
| _ => 1000;
let choose = (t1: node, t2: node) => {
nodeScore(t1) * nodeScore(t2) > 10000 ? `Sampling : `Analytical;
};
let combine =
(evaluationParams, algebraicOp, t1: node, t2: node)
: result(node, string) => {
E.R.merge(
SamplingDistribution.renderIfIsNotSamplingDistribution(
evaluationParams,
t1,
),
SamplingDistribution.renderIfIsNotSamplingDistribution(
evaluationParams,
t2,
),
)
|> E.R.bind(_, ((a, b)) =>
switch (choose(a, b)) {
| `Sampling =>
SamplingDistribution.combineShapesUsingSampling(
evaluationParams,
algebraicOp,
a,
b,
)
| `Analytical =>
combinationByRendering(evaluationParams, algebraicOp, a, b)
}
);
};
let operationToLeaf = let operationToLeaf =
( (
evaluationParams: evaluationParams, evaluationParams: evaluationParams,
@ -45,8 +86,8 @@ module AlgebraicCombination = {
|> E.R.bind( |> E.R.bind(
_, _,
fun fun
| `SymbolicDist(d) as t => Ok(t) | `SymbolicDist(_) as t => Ok(t)
| _ => combinationByRendering(evaluationParams, algebraicOp, t1, t2), | _ => combine(evaluationParams, algebraicOp, t1, t2),
); );
}; };
@ -79,7 +120,10 @@ module VerticalScaling = {
module PointwiseCombination = { module PointwiseCombination = {
let pointwiseAdd = (evaluationParams: evaluationParams, t1: t, t2: t) => { let pointwiseAdd = (evaluationParams: evaluationParams, t1: t, t2: t) => {
switch (Render.render(evaluationParams, t1), Render.render(evaluationParams, t2)) { switch (
Render.render(evaluationParams, t1),
Render.render(evaluationParams, t2),
) {
| (Ok(`RenderedDist(rs1)), Ok(`RenderedDist(rs2))) => | (Ok(`RenderedDist(rs1)), Ok(`RenderedDist(rs2))) =>
Ok( Ok(
`RenderedDist( `RenderedDist(
@ -110,9 +154,17 @@ module PointwiseCombination = {
let pointwiseMultiply = (evaluationParams: evaluationParams, t1: t, t2: t) => { let pointwiseMultiply = (evaluationParams: evaluationParams, t1: t, t2: t) => {
// TODO: construct a function that we can easily sample from, to construct // TODO: construct a function that we can easily sample from, to construct
// a RenderedDist. Use the xMin and xMax of the rendered shapes to tell the sampling function where to look. // a RenderedDist. Use the xMin and xMax of the rendered shapes to tell the sampling function where to look.
Error( // TODO: This should work for symbolic distributions too!
"Pointwise multiplication not yet supported.", switch (
); Render.render(evaluationParams, t1),
Render.render(evaluationParams, t2),
) {
| (Ok(`RenderedDist(rs1)), Ok(`RenderedDist(rs2))) =>
Ok(`RenderedDist(Shape.combinePointwise(( *. ), rs1, rs2)))
| (Error(e1), _) => Error(e1)
| (_, Error(e2)) => Error(e2)
| _ => Error("Pointwise combination: rendering failed.")
};
}; };
let operationToLeaf = let operationToLeaf =
@ -133,8 +185,10 @@ module Truncate = {
let trySimplification = (leftCutoff, rightCutoff, t): simplificationResult => { let trySimplification = (leftCutoff, rightCutoff, t): simplificationResult => {
switch (leftCutoff, rightCutoff, t) { switch (leftCutoff, rightCutoff, t) {
| (None, None, t) => `Solution(t) | (None, None, t) => `Solution(t)
| (Some(lc), Some(rc), t) when lc > rc => | (Some(lc), Some(rc), _) when lc > rc =>
`Error("Left truncation bound must be smaller than right truncation bound.") `Error(
"Left truncation bound must be smaller than right truncation bound.",
)
| (lc, rc, `SymbolicDist(`Uniform(u))) => | (lc, rc, `SymbolicDist(`Uniform(u))) =>
`Solution( `Solution(
`SymbolicDist(`Uniform(SymbolicDist.Uniform.truncate(lc, rc, u))), `SymbolicDist(`Uniform(SymbolicDist.Uniform.truncate(lc, rc, u))),

View File

@ -15,12 +15,8 @@ module MathJsonToMathJsAdt = {
switch (field("mathjs", string, j)) { switch (field("mathjs", string, j)) {
| "FunctionNode" => | "FunctionNode" =>
let args = j |> field("args", array(run)); let args = j |> field("args", array(run));
Some( let name = j |> optional(field("fn", field("name", string)));
Fn({ name |> E.O.fmap(name => Fn({name, args: args |> E.A.O.concatSomes}));
name: j |> field("fn", field("name", string)),
args: args |> E.A.O.concatSomes,
}),
);
| "OperatorNode" => | "OperatorNode" =>
let args = j |> field("args", array(run)); let args = j |> field("args", array(run));
Some( Some(
@ -240,6 +236,7 @@ module MathAdtToDistDst = {
args: array(result(ExpressionTypes.ExpressionTree.node, string)), args: array(result(ExpressionTypes.ExpressionTree.node, string)),
) => { ) => {
let toOkAlgebraic = r => Ok(`AlgebraicCombination(r)); let toOkAlgebraic = r => Ok(`AlgebraicCombination(r));
let toOkPointwise = r => Ok(`PointwiseCombination(r));
let toOkTruncate = r => Ok(`Truncate(r)); let toOkTruncate = r => Ok(`Truncate(r));
let toOkFloatFromDist = r => Ok(`FloatFromDist(r)); let toOkFloatFromDist = r => Ok(`FloatFromDist(r));
switch (name, args) { switch (name, args) {
@ -249,6 +246,11 @@ module MathAdtToDistDst = {
| ("subtract", _) => Error("Subtraction needs two operands") | ("subtract", _) => Error("Subtraction needs two operands")
| ("multiply", [|Ok(l), Ok(r)|]) => toOkAlgebraic((`Multiply, l, r)) | ("multiply", [|Ok(l), Ok(r)|]) => toOkAlgebraic((`Multiply, l, r))
| ("multiply", _) => Error("Multiplication needs two operands") | ("multiply", _) => Error("Multiplication needs two operands")
| ("dotMultiply", [|Ok(l), Ok(r)|]) => toOkPointwise((`Multiply, l, r))
| ("dotMultiply", _) =>
Error("Dotwise multiplication needs two operands")
| ("rightLogShift", [|Ok(l), Ok(r)|]) => toOkPointwise((`Add, l, r))
| ("rightLogShift", _) => Error("Dotwise addition needs two operands")
| ("divide", [|Ok(l), Ok(r)|]) => toOkAlgebraic((`Divide, l, r)) | ("divide", [|Ok(l), Ok(r)|]) => toOkAlgebraic((`Divide, l, r))
| ("divide", _) => Error("Division needs two operands") | ("divide", _) => Error("Division needs two operands")
| ("pow", _) => Error("Exponentiation is not yet supported.") | ("pow", _) => Error("Exponentiation is not yet supported.")
@ -324,6 +326,8 @@ module MathAdtToDistDst = {
| "add" | "add"
| "subtract" | "subtract"
| "multiply" | "multiply"
| "dotMultiply"
| "rightLogShift"
| "divide" | "divide"
| "pow" | "pow"
| "leftTruncate" | "leftTruncate"
@ -358,6 +362,13 @@ module MathAdtToDistDst = {
r |> MathAdtCleaner.run |> topLevel; r |> MathAdtCleaner.run |> topLevel;
}; };
/* The MathJs parser doesn't support '.+' syntax, but we want it because it
would make sense with '.*'. Our workaround is to change this to >>>, which is
logShift in mathJS. We don't expect to use logShift anytime soon, so this tradeoff
seems fine.
*/
let pointwiseToRightLogShift = Js.String.replaceByRe([%re "/\.\+/g"], ">>>");
let fromString = str => { let fromString = str => {
/* We feed the user-typed string into Mathjs.parseMath, /* We feed the user-typed string into Mathjs.parseMath,
which returns a JSON with (hopefully) a single-element array. which returns a JSON with (hopefully) a single-element array.
@ -367,7 +378,7 @@ let fromString = str => {
The function MathJsonToMathJsAdt then recursively unpacks this JSON into a typed data structure we can use. The function MathJsonToMathJsAdt then recursively unpacks this JSON into a typed data structure we can use.
Inside of this function, MathAdtToDistDst is called whenever a distribution function is encountered. Inside of this function, MathAdtToDistDst is called whenever a distribution function is encountered.
*/ */
let mathJsToJson = Mathjs.parseMath(str); let mathJsToJson = str |> pointwiseToRightLogShift |> Mathjs.parseMath;
let mathJsParse = let mathJsParse =
E.R.bind(mathJsToJson, r => { E.R.bind(mathJsToJson, r => {
switch (MathJsonToMathJsAdt.run(r)) { switch (MathJsonToMathJsAdt.run(r)) {

View File

@ -6,7 +6,7 @@ let isSamplingDistribution: node => bool =
| `RenderedDist(_) => true | `RenderedDist(_) => true
| _ => false; | _ => false;
let renderIfIsNotSamplingDistribution = (params, t) => let renderIfIsNotSamplingDistribution = (params, t): result(node, string) =>
!isSamplingDistribution(t) !isSamplingDistribution(t)
? switch (Render.render(params, t)) { ? switch (Render.render(params, t)) {
| Ok(r) => Ok(r) | Ok(r) => Ok(r)
@ -70,9 +70,7 @@ let combineShapesUsingSampling =
}, },
), ),
) )
|> E.O.bind(_, (r: RenderTypes.ShapeRenderer.Sampling.outputs) => |> E.O.bind(_, (r) => r.shape)
r.shape
)
|> E.O.toResult("No response"); |> E.O.toResult("No response");
shape |> E.R.fmap(r => `Normalize(`RenderedDist(r))); shape |> E.R.fmap(r => `Normalize(`RenderedDist(r)));
}, },

View File

@ -1,18 +1,4 @@
let downsampleIfShould = let run = (inputs: RenderTypes.DistPlusRenderer.inputs) => {
(
{recommendedLength, shouldDownsample}: RenderTypes.DistPlusRenderer.inputs,
outputs: RenderTypes.ShapeRenderer.Combined.outputs,
dist,
) => {
let willDownsample =
shouldDownsample
&& RenderTypes.ShapeRenderer.Combined.methodUsed(outputs) == `Sampling;
willDownsample ? dist |> DistPlus.T.downsample(recommendedLength) : dist;
};
let run =
(inputs: RenderTypes.DistPlusRenderer.inputs)
: RenderTypes.DistPlusRenderer.outputs => {
let toDist = shape => let toDist = shape =>
DistPlus.make( DistPlus.make(
~shape, ~shape,
@ -22,7 +8,7 @@ let run =
(), (),
) )
|> DistPlus.T.normalize; |> DistPlus.T.normalize;
let outputs = let output =
ShapeRenderer.run({ ShapeRenderer.run({
samplingInputs: inputs.samplingInputs, samplingInputs: inputs.samplingInputs,
guesstimatorString: inputs.distPlusIngredients.guesstimatorString, guesstimatorString: inputs.distPlusIngredients.guesstimatorString,
@ -30,8 +16,8 @@ let run =
length: inputs.recommendedLength, length: inputs.recommendedLength,
}, },
}); });
let shape = outputs |> RenderTypes.ShapeRenderer.Combined.getShape; output
let dist = |> E.R.fmap((o: RenderTypes.ShapeRenderer.Symbolic.outputs) =>
shape |> E.O.fmap(toDist) |> E.O.fmap(downsampleIfShould(inputs, outputs)); toDist(o.shape)
RenderTypes.DistPlusRenderer.Outputs.make(outputs, dist); );
}; };

View File

@ -18,27 +18,22 @@ let runSymbolic = (inputs: RenderTypes.ShapeRenderer.Combined.inputs) => {
let str = formatString(inputs.guesstimatorString); let str = formatString(inputs.guesstimatorString);
let graph = MathJsParser.fromString(str); let graph = MathJsParser.fromString(str);
graph graph
|> E.R.fmap(g => |> E.R.bind(_, g =>
RenderTypes.ShapeRenderer.Symbolic.make( ExpressionTree.toShape(
inputs.symbolicInputs.length,
{
sampleCount:
inputs.samplingInputs.sampleCount |> E.O.default(10000),
outputXYPoints:
inputs.samplingInputs.outputXYPoints |> E.O.default(10000),
kernelWidth: inputs.samplingInputs.kernelWidth,
},
g, g,
ExpressionTree.toShape(
inputs.symbolicInputs.length,
{
sampleCount:
inputs.samplingInputs.sampleCount |> E.O.default(10000),
outputXYPoints:
inputs.samplingInputs.outputXYPoints |> E.O.default(10000),
kernelWidth: inputs.samplingInputs.kernelWidth,
},
g,
),
) )
|> E.R.fmap(RenderTypes.ShapeRenderer.Symbolic.make(g))
); );
}; };
let run = let run = (inputs: RenderTypes.ShapeRenderer.Combined.inputs) => {
(inputs: RenderTypes.ShapeRenderer.Combined.inputs) runSymbolic(inputs);
: RenderTypes.ShapeRenderer.Combined.outputs => {
let symbolic = runSymbolic(inputs);
{symbolic: Some(symbolic), sampling: None};
}; };

View File

@ -1,3 +1,42 @@
module Types = {
let defaultSampleCount = 5000;
let defaultOutputXYPoints = 10000;
type inputs = {
sampleCount: option(int),
outputXYPoints: option(int),
kernelWidth: option(float),
};
type samplingStats = {
sampleCount: int,
outputXYPoints: int,
bandwidthXSuggested: float,
bandwidthUnitSuggested: float,
bandwidthXImplemented: float,
bandwidthUnitImplemented: float,
};
type outputs = {
continuousParseParams: option(samplingStats),
shape: option(DistTypes.shape),
};
let empty = {sampleCount: None, outputXYPoints: None, kernelWidth: None};
type fInputs = {
sampleCount: int,
outputXYPoints: int,
kernelWidth: option(float),
};
let toF = (i: inputs): fInputs => {
sampleCount: i.sampleCount |> E.O.default(defaultSampleCount),
outputXYPoints: i.outputXYPoints |> E.O.default(defaultOutputXYPoints),
kernelWidth: i.kernelWidth,
};
};
module JS = { module JS = {
[@bs.deriving abstract] [@bs.deriving abstract]
type distJs = { type distJs = {
@ -21,39 +60,6 @@ module KDE = {
|> JS.samplesToContinuousPdf(_, outputXYPoints, kernelWidth) |> JS.samplesToContinuousPdf(_, outputXYPoints, kernelWidth)
|> JS.jsToDist; |> JS.jsToDist;
}; };
// Note: This was an experiment, but it didn't actually work that well.
let inGroups = (samples, outputXYPoints, kernelWidth, ~cuttoff=0.9, ()) => {
let partitionAt =
samples
|> E.A.length
|> float_of_int
|> (e => e *. cuttoff)
|> int_of_float;
let part1XYPoints =
outputXYPoints |> float_of_int |> (e => e *. cuttoff) |> int_of_float;
let part2XYPoints = outputXYPoints - part1XYPoints |> Js.Math.max_int(30);
let part1Data =
samples |> Belt.Array.slice(_, ~offset=0, ~len=partitionAt);
let part2DataLength = (samples |> E.A.length) - partitionAt;
let part2Data =
samples
|> Belt.Array.slice(
_,
~offset=(-1) * part2DataLength,
~len=part2DataLength,
);
let part1 =
part1Data
|> JS.samplesToContinuousPdf(_, part1XYPoints, kernelWidth)
|> JS.jsToDist;
let part2 =
part2Data
|> JS.samplesToContinuousPdf(_, part2XYPoints, 3)
|> JS.jsToDist;
let opp = 1.0 -. cuttoff;
part1;
};
}; };
module T = { module T = {
@ -109,7 +115,7 @@ module T = {
let toShape = let toShape =
( (
~samples: t, ~samples: t,
~samplingInputs: RenderTypes.ShapeRenderer.Sampling.Inputs.fInputs, ~samplingInputs: Types.fInputs,
(), (),
) => { ) => {
Array.fast_sort(compare, samples); Array.fast_sort(compare, samples);
@ -126,6 +132,7 @@ module T = {
continuousPart |> E.A.length > 5 continuousPart |> E.A.length > 5
? { ? {
let _suggestedXWidth = Bandwidth.nrd0(continuousPart); let _suggestedXWidth = Bandwidth.nrd0(continuousPart);
// todo: This does some recalculating from the last step.
let _suggestedUnitWidth = let _suggestedUnitWidth =
suggestedUnitWidth(continuousPart, samplingInputs.outputXYPoints); suggestedUnitWidth(continuousPart, samplingInputs.outputXYPoints);
let usedWidth = let usedWidth =
@ -136,7 +143,7 @@ module T = {
samplingInputs.outputXYPoints, samplingInputs.outputXYPoints,
usedWidth, usedWidth,
); );
let foo: RenderTypes.ShapeRenderer.Sampling.samplingStats = { let foo: Types.samplingStats = {
sampleCount: samplingInputs.sampleCount, sampleCount: samplingInputs.sampleCount,
outputXYPoints: samplingInputs.outputXYPoints, outputXYPoints: samplingInputs.outputXYPoints,
bandwidthXSuggested: _suggestedXWidth, bandwidthXSuggested: _suggestedXWidth,
@ -159,7 +166,7 @@ module T = {
~continuous=pdf |> E.O.fmap(fst), ~continuous=pdf |> E.O.fmap(fst),
~discrete=Some(discrete), ~discrete=Some(discrete),
); );
let samplesParse: RenderTypes.ShapeRenderer.Sampling.outputs = { let samplesParse: Types.outputs = {
continuousParseParams: pdf |> E.O.fmap(snd), continuousParseParams: pdf |> E.O.fmap(snd),
shape, shape,
}; };
@ -168,11 +175,11 @@ module T = {
let fromSamples = let fromSamples =
( (
~samplingInputs=RenderTypes.ShapeRenderer.Sampling.Inputs.empty, ~samplingInputs=Types.empty,
samples, samples,
) => { ) => {
let samplingInputs = let samplingInputs =
RenderTypes.ShapeRenderer.Sampling.Inputs.toF(samplingInputs); Types.toF(samplingInputs);
toShape(~samples, ~samplingInputs, ()); toShape(~samples, ~samplingInputs, ());
}; };
}; };

View File

@ -145,6 +145,7 @@ module R = {
let id = e => e |> result(U.id, U.id); let id = e => e |> result(U.id, U.id);
let fmap = Rationale.Result.fmap; let fmap = Rationale.Result.fmap;
let bind = Rationale.Result.bind; let bind = Rationale.Result.bind;
let toExn = Belt.Result.getExn;
let merge = (a, b) => let merge = (a, b) =>
switch (a, b) { switch (a, b) {
| (Error(e), _) => Error(e) | (Error(e), _) => Error(e)

View File

@ -25,11 +25,9 @@ let propValue = (t: Prop.Value.t) => {
~shouldDownsample=true, ~shouldDownsample=true,
(), (),
) )
|> DistPlusRenderer.run |> DistPlusRenderer.run;
|> RenderTypes.DistPlusRenderer.Outputs.distplus;
switch (newDistribution) { switch (newDistribution) {
| Some(distribution) => | Ok(distribution) => <div> <DistPlusPlot distPlus=distribution /> </div>
<div> <DistPlusPlot distPlus=distribution /> </div>
// <input // <input
// readOnly=true // readOnly=true
// className="shadow appearance-none border w-1/3 rounded py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" // className="shadow appearance-none border w-1/3 rounded py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
@ -45,7 +43,7 @@ let propValue = (t: Prop.Value.t) => {
// className="w-1/3 border w-1/2 rounded py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline bg-white"> // className="w-1/3 border w-1/2 rounded py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline bg-white">
// {"30 to infinity, 80% mass" |> ReasonReact.string} // {"30 to infinity, 80% mass" |> ReasonReact.string}
// </div> // </div>
| None => "Something went wrong" |> ReasonReact.string | Error(e) => e |> ReasonReact.string
}; };
| FloatCdf(_) => <div /> | FloatCdf(_) => <div />
| Probability(r) => | Probability(r) =>

View File

@ -112,8 +112,12 @@ module Model = {
GlobalCatastrophe.makeI(MomentRe.momentNow()) GlobalCatastrophe.makeI(MomentRe.momentNow())
|> RenderTypes.DistPlusRenderer.make(~distPlusIngredients=_, ()) |> RenderTypes.DistPlusRenderer.make(~distPlusIngredients=_, ())
|> DistPlusRenderer.run |> DistPlusRenderer.run
|> RenderTypes.DistPlusRenderer.Outputs.distplus |> E.R.bind(_, r =>
|> E.O.bind(_, DistPlusTime.Integral.xToY(Time(dateTime))); r
|> DistPlusTime.Integral.xToY(Time(dateTime))
|> E.O.toResult("error")
)
|> E.R.toOption;
}; };
let make = let make =