Allow function to reach editor

This commit is contained in:
Ozzie Gooen 2020-08-07 13:30:16 +01:00
parent 3147e6d9c3
commit 04d606d9c6
8 changed files with 200 additions and 305 deletions

View File

@ -1 +1 @@
let entries = EntryTypes.[Continuous2.entry,ExpressionTreeExamples.entry]; let entries = EntryTypes.[ExpressionTreeExamples.entry];

View File

@ -5,84 +5,84 @@
// 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 ={
let ingredients = DistPlusRenderer.Inputs.Ingredients.make( // let ingredients = DistPlusRenderer.Inputs.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 = DistPlusRenderer.Inputs.make(~distPlusIngredients=ingredients,()) // let inputs = DistPlusRenderer.Inputs.make(~distPlusIngredients=ingredients,())
inputs |> DistPlusRenderer.run // inputs |> DistPlusRenderer.run
} // }
let setup = dist => // let setup = dist =>
DistPlusRenderer.Inputs.make(~distPlusIngredients=dist,()) // DistPlusRenderer.Inputs.make(~distPlusIngredients=dist,())
|> DistPlusRenderer.run // |> DistPlusRenderer.run
|> E.R.fmap(distPlus => <DistPlusPlot distPlus />) // |> E.R.fmap(distPlus => <DistPlusPlot distPlus />)
|> E.R.toOption // |> E.R.toOption
|> E.O.toExn("") // |> E.O.toExn("")
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(DistPlusRenderer.Inputs.Ingredients.make(~guesstimatorString, ()))} // {setup(DistPlusRenderer.Inputs.Ingredients.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(
DistPlusRenderer.Inputs.Ingredients.make( // DistPlusRenderer.Inputs.Ingredients.make(
~guesstimatorString, // ~guesstimatorString,
~unit=TimeDistribution({zero: MomentRe.momentNow(), unit: `years}), // ~unit=TimeDistribution({zero: MomentRe.momentNow(), unit: `years}),
(), // (),
), // ),
)} // )}
</>; // </>;
let distributions = () => // let distributions = () =>
<div> // <div>
<div> // <div>
<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>;
let entry = EntryTypes.(entry(~title="Mixed Distributions", ~render=distributions)); // let entry = EntryTypes.(entry(~title="Mixed Distributions", ~render=distributions));

View File

@ -1,18 +1,18 @@
let setup = dist => // let setup = dist =>
DistPlusRenderer.Inputs.make(~distPlusIngredients=dist, ()) // DistPlusRenderer.Inputs.make(~distPlusIngredients=dist, ())
|> DistPlusRenderer.run // |> DistPlusRenderer.run
|> E.R.fmap(distPlus => <DistPlusPlot distPlus />) // |> E.R.fmap(distPlus => <DistPlusPlot distPlus />)
|> E.R.toOption // |> E.R.toOption
|> E.O.toExn("") // |> E.O.toExn("")
let simpleExample = (guesstimatorString, ~problem="", ()) => // let simpleExample = (guesstimatorString, ~problem="", ()) =>
<> // <>
<p> {guesstimatorString |> ReasonReact.string} </p> // <p> {guesstimatorString |> ReasonReact.string} </p>
<p> {problem |> (e => "problem: " ++ e) |> ReasonReact.string} </p> // <p> {problem |> (e => "problem: " ++ e) |> ReasonReact.string} </p>
{setup( // {setup(
DistPlusRenderer.Inputs.Ingredients.make(~guesstimatorString, ()), // DistPlusRenderer.Inputs.Ingredients.make(~guesstimatorString, ()),
)} // )}
</>; // </>;
let distributions = () => let distributions = () =>
<div> <div>
@ -20,51 +20,51 @@ 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( // {simpleExample(
"normal(-1, 1) + normal(5, 2)", // "normal(-1, 1) + normal(5, 2)",
~problem="Tails look too flat", // ~problem="Tails look too flat",
(), // (),
)} // )}
{simpleExample( // {simpleExample(
"mm(normal(4,2), normal(10,1))", // "mm(normal(4,2), normal(10,1))",
~problem="Tails look too flat", // ~problem="Tails look too flat",
(), // (),
)} // )}
{simpleExample( // {simpleExample(
"normal(-1, 1) * normal(5, 2)", // "normal(-1, 1) * normal(5, 2)",
~problem="This looks really weird", // ~problem="This looks really weird",
(), // (),
)} // )}
{simpleExample( // {simpleExample(
"normal(1,2) * normal(2,2) * normal(3,1)", // "normal(1,2) * normal(2,2) * normal(3,1)",
~problem="Seems like important parts are cut off", // ~problem="Seems like important parts are cut off",
(), // (),
)} // )}
{simpleExample( // {simpleExample(
"mm(uniform(0, 1) , normal(3,2))", // "mm(uniform(0, 1) , normal(3,2))",
~problem="Uniform distribution seems to break multimodal", // ~problem="Uniform distribution seems to break multimodal",
(), // (),
)} // )}
{simpleExample( // {simpleExample(
"truncate(mm(1 to 10, 10 to 30), 10, 20)", // "truncate(mm(1 to 10, 10 to 30), 10, 20)",
~problem="Truncate seems to have no effect", // ~problem="Truncate seems to have no effect",
(), // (),
)} // )}
{simpleExample( // {simpleExample(
"normal(5,2)*(10^3)", // "normal(5,2)*(10^3)",
~problem="Multiplied items should be evaluated.", // ~problem="Multiplied items should be evaluated.",
(), // (),
)} // )}
{simpleExample( // {simpleExample(
"normal(5,10*3)", // "normal(5,10*3)",
~problem="At least simple operations in the distributions should be evaluated.", // ~problem="At least simple operations in the distributions should be evaluated.",
(), // (),
)} // )}
{simpleExample( // {simpleExample(
"normal(5,10)^3", // "normal(5,10)^3",
~problem="Exponentiation not yet supported", // ~problem="Exponentiation not yet supported",
(), // (),
)} // )}
</div> </div>
</div>; </div>;

View File

@ -158,12 +158,13 @@ module DemoDist = {
(), (),
); );
let response1 = DistPlusRenderer.run(inputs1); let response1 = DistPlusRenderer.run2(inputs1);
switch (response1) { switch (response1) {
| (Ok(distPlus1)) => | (Ok(`DistPlus(distPlus1))) =>
<> <>
<DistPlusPlot distPlus={DistPlus.T.normalize(distPlus1)} /> <DistPlusPlot distPlus={DistPlus.T.normalize(distPlus1)} />
</> </>
| (Ok(`Function(f,a))) => "Function!!!" |> R.ste
| (Error(r)) => r |> R.ste | (Error(r)) => r |> R.ste
}; };
| _ => | _ =>
@ -330,160 +331,10 @@ let make = () => {
<Col span=24> <Col span=24>
<FieldText <FieldText
field=FormConfig.GuesstimatorString field=FormConfig.GuesstimatorString
label="Guesstimator String" label="Program"
/> />
</Col> </Col>
</Row> </Row>
<Row _type=`flex className=Styles.rows>
<Col span=4>
<Form.Field
field=FormConfig.DomainType
render={({handleChange, value}) =>
<Antd.Form.Item label={"Domain Type" |> R.ste}>
<Antd.Select value onChange={e => e |> handleChange}>
<Antd.Select.Option value="Complete">
{"Complete" |> R.ste}
</Antd.Select.Option>
<Antd.Select.Option value="LeftLimited">
{"Left Limited" |> R.ste}
</Antd.Select.Option>
<Antd.Select.Option value="RightLimited">
{"Right Limited" |> R.ste}
</Antd.Select.Option>
<Antd.Select.Option value="LeftAndRightLimited">
{"Left And Right Limited" |> R.ste}
</Antd.Select.Option>
</Antd.Select>
</Antd.Form.Item>
}
/>
</Col>
{<>
<Col span=4>
<FieldFloat
field=FormConfig.XPoint
label="Left X-point"
className=Styles.groupA
/>
</Col>
<Col span=4>
<FieldFloat
field=FormConfig.ExcludingProbabilityMass
label="Left Excluding Probability Mass"
className=Styles.groupA
/>
</Col>
</>
|> R.showIf(
E.L.contains(
reform.state.values.domainType,
["LeftLimited", "LeftAndRightLimited"],
),
)}
{<>
<Col span=4>
<FieldFloat
field=FormConfig.XPoint2
label="Right X-point"
className=Styles.groupB
/>
</Col>
<Col span=4>
<FieldFloat
field=FormConfig.ExcludingProbabilityMass2
label="Right Excluding Probability Mass"
className=Styles.groupB
/>
</Col>
</>
|> R.showIf(
E.L.contains(
reform.state.values.domainType,
["RightLimited", "LeftAndRightLimited"],
),
)}
</Row>
<Row _type=`flex className=Styles.rows>
<Col span=4>
<Form.Field
field=FormConfig.UnitType
render={({handleChange, value}) =>
<Antd.Form.Item label={"Unit Type" |> R.ste}>
<Antd.Select value onChange={e => e |> handleChange}>
<Antd.Select.Option value="UnspecifiedDistribution">
{"Unspecified Distribution" |> R.ste}
</Antd.Select.Option>
<Antd.Select.Option value="TimeDistribution">
{"Time Distribution" |> R.ste}
</Antd.Select.Option>
</Antd.Select>
</Antd.Form.Item>
}
/>
</Col>
{<>
<Col span=4>
<Form.Field
field=FormConfig.Zero
render={({handleChange, value}) =>
<Antd.Form.Item label={"Zero Point" |> R.ste}>
<Antd_DatePicker
value
onChange={e => {
e |> handleChange;
_ => ();
}}
/>
</Antd.Form.Item>
}
/>
</Col>
<Col span=4>
<Form.Field
field=FormConfig.Unit
render={({handleChange, value}) =>
<Antd.Form.Item label={"Unit" |> R.ste}>
<Antd.Select value onChange={e => e |> handleChange}>
<Antd.Select.Option value="days">
{"Days" |> R.ste}
</Antd.Select.Option>
<Antd.Select.Option value="hours">
{"Hours" |> R.ste}
</Antd.Select.Option>
<Antd.Select.Option value="milliseconds">
{"Milliseconds" |> R.ste}
</Antd.Select.Option>
<Antd.Select.Option value="minutes">
{"Minutes" |> R.ste}
</Antd.Select.Option>
<Antd.Select.Option value="months">
{"Months" |> R.ste}
</Antd.Select.Option>
<Antd.Select.Option value="quarters">
{"Quarters" |> R.ste}
</Antd.Select.Option>
<Antd.Select.Option value="seconds">
{"Seconds" |> R.ste}
</Antd.Select.Option>
<Antd.Select.Option value="weeks">
{"Weeks" |> R.ste}
</Antd.Select.Option>
<Antd.Select.Option value="years">
{"Years" |> R.ste}
</Antd.Select.Option>
</Antd.Select>
</Antd.Form.Item>
}
/>
</Col>
</>
|> R.showIf(
E.L.contains(
reform.state.values.unitType,
["TimeDistribution"],
),
)}
</Row>
<Row _type=`flex className=Styles.rows> <Row _type=`flex className=Styles.rows>
<Col span=4> <Col span=4>
<FieldFloat field=FormConfig.SampleCount label="Sample Count" /> <FieldFloat field=FormConfig.SampleCount label="Sample Count" />

View File

@ -9,19 +9,48 @@ let toLeaf = (samplingInputs, environment, node: node) => {
}); });
}; };
let toShape = (samplingInputs, environment, node: node) => { let rec toString: node => string =
let renderResult = fun
`Render(`Normalize(node)) |> toLeaf(samplingInputs, environment); | `SymbolicDist(d) => SymbolicDist.T.toString(d)
| `RenderedDist(_) => "[shape]"
| `AlgebraicCombination(op, t1, t2) =>
Operation.Algebraic.format(op, toString(t1), toString(t2))
| `PointwiseCombination(op, t1, t2) =>
Operation.Pointwise.format(op, toString(t1), toString(t2))
| `VerticalScaling(scaleOp, t, scaleBy) =>
Operation.Scale.format(scaleOp, toString(t), toString(scaleBy))
| `Normalize(t) => "normalize(k" ++ toString(t) ++ ")"
| `FloatFromDist(floatFromDistOp, t) =>
Operation.DistToFloat.format(floatFromDistOp, toString(t))
| `Truncate(lc, rc, t) =>
Operation.T.truncateToString(lc, rc, toString(t))
| `Render(t) => toString(t)
| `Symbol(t) => "Symbol: " ++ t
| `FunctionCall(name, args) =>
"[Fuction call: ("
++ name
++ (args |> E.A.fmap(toString) |> Js.String.concatMany(_, ","))
++ ")]"
| `Function(args, internal) =>
"[Function: ("
++ (args |> Js.String.concatMany(_, ","))
++ toString(internal)
++ ")]";
switch (renderResult) { let toLeaf = (samplingInputs, environment, node: node) => {
switch(toLeaf(samplingInputs, environment, node)){
| Ok(`SymbolicDist(n)) => `Render(`SymbolicDist(n)) |> toLeaf(samplingInputs, environment);
| Ok(`Function(n)) => Ok(`Function(n))
| Ok(`RenderedDist(n)) => `Normalize(`RenderedDist(n))|> toLeaf(samplingInputs, environment)
| Error(e) => Error(e)
| _ => Error("Wrong type returned")
}
}
let toShape = (samplingInputs, environment, node: node) => {
switch (toLeaf(samplingInputs, environment, node)) {
| Ok(`RenderedDist(shape)) => Ok(shape) | Ok(`RenderedDist(shape)) => Ok(shape)
| Ok(_) => Error("Rendering failed.") | Ok(_) => Error("Rendering failed.")
| Error(e) => Error(e) | Error(e) => Error(e)
}; };
}; };
let rec toString =
fun
| `SymbolicDist(d) => SymbolicDist.T.toString(d)
| `RenderedDist(_) => "[shape]"
| op => Operation.T.toString(toString, op);

View File

@ -304,6 +304,7 @@ let toLeaf =
switch (node) { switch (node) {
// Leaf nodes just stay leaf nodes // Leaf nodes just stay leaf nodes
| `SymbolicDist(_) | `SymbolicDist(_)
| `Function(_)
| `RenderedDist(_) => Ok(node) | `RenderedDist(_) => Ok(node)
// Operations nevaluationParamsd to be turned into leaves // Operations nevaluationParamsd to be turned into leaves
| `AlgebraicCombination(algebraicOp, t1, t2) => | `AlgebraicCombination(algebraicOp, t1, t2) =>
@ -328,8 +329,6 @@ let toLeaf =
FloatFromDist.operationToLeaf(evaluationParams, distToFloatOp, t) FloatFromDist.operationToLeaf(evaluationParams, distToFloatOp, t)
| `Normalize(t) => Normalize.operationToLeaf(evaluationParams, t) | `Normalize(t) => Normalize.operationToLeaf(evaluationParams, t)
| `Render(t) => Render.operationToLeaf(evaluationParams, t) | `Render(t) => Render.operationToLeaf(evaluationParams, t)
| `Function(_) => Error("Function must be called with params")
| `Symbol(r) => ExpressionTypes.ExpressionTree.Environment.get(evaluationParams.environment, r) |> E.O.toResult("Undeclared variable " ++ r) | `Symbol(r) => ExpressionTypes.ExpressionTree.Environment.get(evaluationParams.environment, r) |> E.O.toResult("Undeclared variable " ++ r)
| `FunctionCall(name, args) => | `FunctionCall(name, args) =>
callableFunction(evaluationParams, name, args) callableFunction(evaluationParams, name, args)

View File

@ -33,6 +33,7 @@ module Pointwise = {
let toString = let toString =
fun fun
| `Add => "+" | `Add => "+"
| `Exponentiate => "^"
| `Multiply => "*"; | `Multiply => "*";
let format = (a, b, c) => b ++ " " ++ toString(a) ++ " " ++ c; let format = (a, b, c) => b ++ " " ++ toString(a) ++ " " ++ c;

View File

@ -92,7 +92,7 @@ module Internals = {
let makeOutputs = (graph, shape): outputs => {graph, shape}; let makeOutputs = (graph, shape): outputs => {graph, shape};
let runNode = (inputs, node) => { let runNode = (inputs, node) => {
ExpressionTree.toShape( ExpressionTree.toLeaf(
{ {
sampleCount: inputs.samplingInputs.sampleCount |> E.O.default(10000), sampleCount: inputs.samplingInputs.sampleCount |> E.O.default(10000),
outputXYPoints: outputXYPoints:
@ -120,13 +120,12 @@ module Internals = {
|> E.A.R.firstErrorOrOpen; |> E.A.R.firstErrorOrOpen;
}; };
let inputsToShape = (inputs: inputs) => { let inputsToLeaf = (inputs: inputs) => {
MathJsParser.fromString(inputs.guesstimatorString) MathJsParser.fromString(inputs.guesstimatorString)
|> E.R.bind(_, g => runProgram(inputs, g)) |> E.R.bind(_, g => runProgram(inputs, g))
|> E.R.bind(_, r => |> E.R.bind(_, r =>
E.A.last(r) E.A.last(r)
|> E.O.toResult("No rendered lines") |> E.O.toResult("No rendered lines")
|> E.R.fmap(Shape.T.normalize)
); );
}; };
@ -144,6 +143,22 @@ module Internals = {
let run = (inputs: Inputs.inputs) => { let run = (inputs: Inputs.inputs) => {
inputs inputs
|> Internals.distPlusRenderInputsToInputs |> Internals.distPlusRenderInputsToInputs
|> Internals.inputsToShape |> Internals.inputsToLeaf
|> E.R.bind(_,r => switch(r){
| `RenderedDist(n) => Ok(n)
| _ => Error("Didn't output renderedDist")
})
|> E.R.fmap(Internals.outputToDistPlus(inputs)); |> E.R.fmap(Internals.outputToDistPlus(inputs));
}; };
let run2 = (inputs: Inputs.inputs) => {
inputs
|> Internals.distPlusRenderInputsToInputs
|> Internals.inputsToLeaf
|> E.R.bind(_,r => switch(r){
| `RenderedDist(n) => Ok(`DistPlus(Internals.outputToDistPlus(inputs,n)))
| `Function(n) => Ok(`Function(n))
| _ => Error("Didn't output renderedDist")
})
};