Add multiple exported graphs (broken)

This commit is contained in:
Sam Nolan 2022-01-18 11:10:06 +11:00
parent 01990dbe9f
commit afd1481177
8 changed files with 449 additions and 536 deletions

View File

@ -37,7 +37,7 @@
"@glennsl/bs-json",
"@foretold/components",
"bs-ant-design-alt",
"reason-react",
"@rescript/react",
"bs-reform",
"bs-css",
"rationale",

View File

@ -28,6 +28,7 @@
"dependencies": {
"@foretold/components": "./foretold/components",
"@glennsl/bs-json": "^5.0.2",
"@rescript/react": "^0.10.3",
"ace-builds": "^1.4.12",
"antd": "3.17.0",
"autoprefixer": "9.7.4",

View File

@ -1,10 +0,0 @@
'use strict';
var reasonReactBlue = "#48a9dc";
var style = "\n body {\n background-color: rgb(224, 226, 229);\n display: flex;\n flex-direction: column;\n align-items: center;\n }\n button {\n background-color: white;\n color: " + reasonReactBlue + ";\n box-shadow: 0 0 0 1px " + reasonReactBlue + ";\n border: none;\n padding: 8px;\n font-size: 16px;\n }\n button:active {\n background-color: " + reasonReactBlue + ";\n color: white;\n }\n .container {\n margin: 12px 0px;\n box-shadow: 0px 4px 16px rgb(200, 200, 200);\n width: 720px;\n border-radius: 12px;\n font-family: sans-serif;\n }\n .containerTitle {\n background-color: rgb(242, 243, 245);\n border-radius: 12px 12px 0px 0px;\n padding: 12px;\n font-weight: bold;\n }\n .containerContent {\n background-color: white;\n padding: 16px;\n border-radius: 0px 0px 12px 12px;\n }\n";
exports.reasonReactBlue = reasonReactBlue;
exports.style = style;
/* No side effect */

View File

@ -1,345 +0,0 @@
open ReForm;
open Antd.Grid;
module FormConfig = [%lenses
type state = {
squiggleString: string,
sampleCount: string,
outputXYPoints: string,
downsampleTo: string,
kernelWidth: string,
diagramStart: string,
diagramStop: string,
diagramCount: string,
}
];
type options = {
sampleCount: int,
outputXYPoints: int,
downsampleTo: option(int),
kernelWidth: option(float),
diagramStart: float,
diagramStop: float,
diagramCount: int,
};
module Form = ReForm.Make(FormConfig);
module FieldText = {
[@react.component]
let make = (~field, ~label) => {
<>
<Form.Field
field
render={({handleChange, error, value, validate}) =>
<CodeEditor value onChange={r => handleChange(r)} />
}
/>
</>;
};
};
module FieldString = {
[@react.component]
let make = (~field, ~label) => {
<Form.Field
field
render={({handleChange, error, value, validate}) =>
<Antd.Form.Item label={label |> R.ste}>
<Antd.Input
value
onChange={ReForm.Helpers.handleChange(handleChange)}
onBlur={_ => validate()}
/>
</Antd.Form.Item>
}
/>;
};
};
module FieldFloat = {
[@react.component]
let make = (~field, ~label, ~className=Css.style([])) => {
<Form.Field
field
render={({handleChange, error, value, validate}) =>
<Antd.Form.Item label={label |> R.ste}>
<Antd.Input
value
onChange={ReForm.Helpers.handleChange(handleChange)}
onBlur={_ => validate()}
className
/>
</Antd.Form.Item>
}
/>;
};
};
module Styles = {
open Css;
let rows =
style([
selector(
">.ant-col:first-child",
[paddingLeft(em(0.25)), paddingRight(em(0.125))],
),
selector(
">.ant-col:last-child",
[paddingLeft(em(0.125)), paddingRight(em(0.25))],
),
selector(
">.ant-col:not(:first-child):not(:last-child)",
[paddingLeft(em(0.125)), paddingRight(em(0.125))],
),
]);
let parent =
style([
selector(".ant-input-number", [width(`percent(100.))]),
selector(".anticon", [verticalAlign(`zero)]),
]);
let form = style([backgroundColor(hex("eee")), padding(em(1.))]);
let dist = style([padding(em(1.))]);
let spacer = style([marginTop(em(1.))]);
let groupA =
style([
selector(
".ant-input-number-input",
[backgroundColor(hex("fff7db"))],
),
]);
let groupB =
style([
selector(
".ant-input-number-input",
[backgroundColor(hex("eaf4ff"))],
),
]);
};
module DemoDist = {
[@react.component]
let make = (~squiggleString:string, ~options) => {
<Antd.Card title={"Distribution" |> R.ste}>
<div>
{switch (options) {
| Some(options) =>
let inputs1 =
ProgramEvaluator.Inputs.make(
~samplingInputs={
sampleCount: Some(options.sampleCount),
outputXYPoints: Some(options.outputXYPoints),
kernelWidth: options.kernelWidth,
shapeLength:
Some(options.downsampleTo |> E.O.default(1000)),
},
~squiggleString,
~environment=
[|
("K", `SymbolicDist(`Float(1000.0))),
("M", `SymbolicDist(`Float(1000000.0))),
("B", `SymbolicDist(`Float(1000000000.0))),
("T", `SymbolicDist(`Float(1000000000000.0))),
|]
->Belt.Map.String.fromArray,
(),
);
let response1 = ProgramEvaluator.evaluateProgram(inputs1);
switch (response1) {
| Ok(`DistPlus(distPlus1)) =>
<DistPlusPlot distPlus={DistPlus.T.normalize(distPlus1)} />
| Ok(`Float(f)) =>
<ForetoldComponents.NumberShower number=f precision=3 />
| Ok(`Function((f, a), env)) =>
// Problem: When it gets the function, it doesn't save state about previous commands
let foo: ProgramEvaluator.Inputs.inputs = {
squiggleString,
samplingInputs: inputs1.samplingInputs,
environment: env,
};
let results =
E.A.Floats.range(options.diagramStart, options.diagramStop, options.diagramCount)
|> E.A.fmap(r =>
ProgramEvaluator.evaluateFunction(
foo,
(f, a),
[|`SymbolicDist(`Float(r))|],
)
|> E.R.bind(_, a =>
switch (a) {
| `DistPlus(d) => Ok((r, DistPlus.T.normalize(d)))
| n =>
Js.log2("Error here", n);
Error("wrong type");
}
)
)
|> E.A.R.firstErrorOrOpen;
switch (results) {
| Ok(dists) => <PercentilesChart dists />
| Error(r) => r |> R.ste
};
| Error(r) => r |> R.ste
};
| _ =>
"Nothing to show. Try to change the distribution description."
|> R.ste
}}
</div>
</Antd.Card>;
};
};
[@react.component]
let make = () => {
let (reloader, setReloader) = React.useState(() => 1);
let reform =
Form.use(
~validationStrategy=OnDemand,
~schema=Form.Validation.Schema([||]),
~onSubmit=({state}) => {None},
~initialState={
//squiggleString: "mm(normal(-10, 2), uniform(18, 25), lognormal({mean: 10, stdev: 8}), triangular(31,40,50))",
squiggleString: "mm(normal(5,2), normal(10,2))",
sampleCount: "1000",
outputXYPoints: "1000",
downsampleTo: "",
kernelWidth: "",
diagramStart: "0",
diagramStop: "10",
diagramCount: "20",
},
(),
);
let onSubmit = e => {
e->ReactEvent.Synthetic.preventDefault;
reform.submit();
};
let squiggleString = reform.state.values.squiggleString;
let sampleCount = reform.state.values.sampleCount |> Js.Float.fromString;
let outputXYPoints =
reform.state.values.outputXYPoints |> Js.Float.fromString;
let downsampleTo = reform.state.values.downsampleTo |> Js.Float.fromString;
let kernelWidth = reform.state.values.kernelWidth |> Js.Float.fromString;
let diagramStart = reform.state.values.diagramStart |> Js.Float.fromString;
let diagramStop = reform.state.values.diagramStop |> Js.Float.fromString;
let diagramCount = reform.state.values.diagramCount |> Js.Float.fromString;
let options =
switch (sampleCount, outputXYPoints, downsampleTo) {
| (_, _, _)
when
!Js.Float.isNaN(sampleCount)
&& !Js.Float.isNaN(outputXYPoints)
&& !Js.Float.isNaN(downsampleTo)
&& sampleCount > 10.
&& outputXYPoints > 10. =>
Some({
sampleCount: sampleCount |> int_of_float,
outputXYPoints: outputXYPoints |> int_of_float,
downsampleTo:
int_of_float(downsampleTo) > 0
? Some(int_of_float(downsampleTo)) : None,
kernelWidth: kernelWidth == 0.0 ? None : Some(kernelWidth),
diagramStart: diagramStart,
diagramStop: diagramStop,
diagramCount: diagramCount |> int_of_float,
})
| _ => None
};
let demoDist =
React.useMemo1(
() => <DemoDist squiggleString options />,
[|
reform.state.values.squiggleString,
reform.state.values.sampleCount,
reform.state.values.outputXYPoints,
reform.state.values.downsampleTo,
reform.state.values.kernelWidth,
reform.state.values.diagramStart,
reform.state.values.diagramStop,
reform.state.values.diagramCount,
reloader |> string_of_int,
|],
);
let onReload = _ => {
setReloader(_ => reloader + 1);
};
<div className="grid grid-cols-2 gap-4">
<div>
<Antd.Card
title={"Distribution Form" |> R.ste}
extra={
<Antd.Button
icon=Antd.IconName.reload
shape=`circle
onClick=onReload
/>
}>
<Form.Provider value=reform>
<Antd.Form onSubmit>
<Row _type=`flex className=Styles.rows>
<Col span=24>
<FieldText
field=FormConfig.SquiggleString
label="Program"
/>
</Col>
</Row>
<Row _type=`flex className=Styles.rows>
<Col span=12>
<FieldFloat
field=FormConfig.SampleCount
label="Sample Count"
/>
</Col>
<Col span=12>
<FieldFloat
field=FormConfig.OutputXYPoints
label="Output XY-points"
/>
</Col>
<Col span=12>
<FieldFloat
field=FormConfig.DownsampleTo
label="Downsample To"
/>
</Col>
<Col span=12>
<FieldFloat
field=FormConfig.KernelWidth
label="Kernel Width"
/>
</Col>
<Col span=12>
<FieldFloat
field=FormConfig.DiagramStart
label="Diagram Start"
/>
</Col>
<Col span=12>
<FieldFloat
field=FormConfig.DiagramStop
label="Diagram Stop"
/>
</Col>
<Col span=12>
<FieldFloat
field=FormConfig.DiagramCount
label="Diagram Count"
/>
</Col>
</Row>
</Antd.Form>
</Form.Provider>
</Antd.Card>
</div>
<div> demoDist </div>
</div>;
};

View File

@ -0,0 +1,271 @@
open ReForm
open Antd.Grid
module FormConfig = %lenses(
type state = {
squiggleString: string,
sampleCount: string,
outputXYPoints: string,
downsampleTo: string,
kernelWidth: string,
diagramStart: string,
diagramStop: string,
diagramCount: string,
}
)
type options = {
sampleCount: int,
outputXYPoints: int,
downsampleTo: option<int>,
kernelWidth: option<float>,
diagramStart: float,
diagramStop: float,
diagramCount: int,
}
module Form = ReForm.Make(FormConfig)
module FieldText = {
@react.component
let make = (~field, ~label) => <>
<Form.Field
field
render={({handleChange, error, value, validate}) =>
<CodeEditor value onChange={r => handleChange(r)} />}
/>
</>
}
module FieldString = {
@react.component
let make = (~field, ~label) =>
<Form.Field
field
render={({handleChange, error, value, validate}) =>
<Antd.Form.Item label={label |> R.ste}>
<Antd.Input
value onChange={ReForm.Helpers.handleChange(handleChange)} onBlur={_ => validate()}
/>
</Antd.Form.Item>}
/>
}
module FieldFloat = {
@react.component
let make = (~field, ~label, ~className=Css.style(list{})) =>
<Form.Field
field
render={({handleChange, error, value, validate}) =>
<Antd.Form.Item label={label |> R.ste}>
<Antd.Input
value
onChange={ReForm.Helpers.handleChange(handleChange)}
onBlur={_ => validate()}
className
/>
</Antd.Form.Item>}
/>
}
module Styles = {
open Css
let rows = style(list{
selector(">.ant-col:first-child", list{paddingLeft(em(0.25)), paddingRight(em(0.125))}),
selector(">.ant-col:last-child", list{paddingLeft(em(0.125)), paddingRight(em(0.25))}),
selector(
">.ant-col:not(:first-child):not(:last-child)",
list{paddingLeft(em(0.125)), paddingRight(em(0.125))},
),
})
let parent = style(list{
selector(".ant-input-number", list{width(#percent(100.))}),
selector(".anticon", list{verticalAlign(#zero)}),
})
let form = style(list{backgroundColor(hex("eee")), padding(em(1.))})
let dist = style(list{padding(em(1.))})
let spacer = style(list{marginTop(em(1.))})
let groupA = style(list{
selector(".ant-input-number-input", list{backgroundColor(hex("fff7db"))}),
})
let groupB = style(list{
selector(".ant-input-number-input", list{backgroundColor(hex("eaf4ff"))}),
})
}
module DemoDist = {
@react.component
let make = (~squiggleString: string, ~options) =>
<Antd.Card title={"Distribution" |> R.ste}>
<div>
{switch options {
| Some(options) =>
let inputs1 = ProgramEvaluator.Inputs.make(
~samplingInputs={
sampleCount: Some(options.sampleCount),
outputXYPoints: Some(options.outputXYPoints),
kernelWidth: options.kernelWidth,
shapeLength: Some(options.downsampleTo |> E.O.default(1000)),
},
~squiggleString,
~environment=[
("K", #SymbolicDist(#Float(1000.0))),
("M", #SymbolicDist(#Float(1000000.0))),
("B", #SymbolicDist(#Float(1000000000.0))),
("T", #SymbolicDist(#Float(1000000000000.0))),
]->Belt.Map.String.fromArray,
(),
)
let distributionList = ProgramEvaluator.evaluateProgram(inputs1)
let renderExpression = response1 =>
switch response1 {
| #DistPlus(distPlus1) => <DistPlusPlot distPlus={DistPlus.T.normalize(distPlus1)} />
| #Float(f) => <ForetoldComponents.NumberShower number=f precision=3 />
| #Function((f, a), env) =>
// Problem: When it gets the function, it doesn't save state about previous commands
let foo: ProgramEvaluator.Inputs.inputs = {
squiggleString: squiggleString,
samplingInputs: inputs1.samplingInputs,
environment: env,
}
let results =
E.A.Floats.range(options.diagramStart, options.diagramStop, options.diagramCount)
|> E.A.fmap(r =>
ProgramEvaluator.evaluateFunction(
foo,
(f, a),
[#SymbolicDist(#Float(r))],
) |> E.R.bind(_, a =>
switch a {
| #DistPlus(d) => Ok((r, DistPlus.T.normalize(d)))
| n =>
Js.log2("Error here", n)
Error("wrong type")
}
)
)
|> E.A.R.firstErrorOrOpen
switch results {
| Ok(dists) => <PercentilesChart dists />
| Error(r) => r |> R.ste
}
}
// Render the list of distributions given by the
switch distributionList {
| Ok(xs) =>
let childrenElements = List.map(renderExpression, xs)
Js.Console.log(childrenElements)
@JSX div(~children=childrenElements, ())
| Error(r) => r |> R.ste
}
| _ => "Nothing to show. Try to change the distribution description." |> R.ste
}}
</div>
</Antd.Card>
}
@react.component
let make = () => {
let (reloader, setReloader) = React.useState(() => 1)
let reform = Form.use(
~validationStrategy=OnDemand,
~schema=Form.Validation.Schema([]),
~onSubmit=({state}) => None,
~initialState={
//squiggleString: "mm(normal(-10, 2), uniform(18, 25), lognormal({mean: 10, stdev: 8}), triangular(31,40,50))",
squiggleString: "mm(normal(5,2), normal(10,2))",
sampleCount: "1000",
outputXYPoints: "1000",
downsampleTo: "",
kernelWidth: "",
diagramStart: "0",
diagramStop: "10",
diagramCount: "20",
},
(),
)
let onSubmit = e => {
e->ReactEvent.Synthetic.preventDefault
reform.submit()
}
let squiggleString = reform.state.values.squiggleString
let sampleCount = reform.state.values.sampleCount |> Js.Float.fromString
let outputXYPoints = reform.state.values.outputXYPoints |> Js.Float.fromString
let downsampleTo = reform.state.values.downsampleTo |> Js.Float.fromString
let kernelWidth = reform.state.values.kernelWidth |> Js.Float.fromString
let diagramStart = reform.state.values.diagramStart |> Js.Float.fromString
let diagramStop = reform.state.values.diagramStop |> Js.Float.fromString
let diagramCount = reform.state.values.diagramCount |> Js.Float.fromString
let options = switch (sampleCount, outputXYPoints, downsampleTo) {
| (_, _, _)
if !Js.Float.isNaN(sampleCount) &&
(!Js.Float.isNaN(outputXYPoints) &&
(!Js.Float.isNaN(downsampleTo) && (sampleCount > 10. && outputXYPoints > 10.))) =>
Some({
sampleCount: sampleCount |> int_of_float,
outputXYPoints: outputXYPoints |> int_of_float,
downsampleTo: int_of_float(downsampleTo) > 0 ? Some(int_of_float(downsampleTo)) : None,
kernelWidth: kernelWidth == 0.0 ? None : Some(kernelWidth),
diagramStart: diagramStart,
diagramStop: diagramStop,
diagramCount: diagramCount |> int_of_float,
})
| _ => None
}
let demoDist = React.useMemo1(
() => <DemoDist squiggleString options />,
[
reform.state.values.squiggleString,
reform.state.values.sampleCount,
reform.state.values.outputXYPoints,
reform.state.values.downsampleTo,
reform.state.values.kernelWidth,
reform.state.values.diagramStart,
reform.state.values.diagramStop,
reform.state.values.diagramCount,
reloader |> string_of_int,
],
)
let onReload = _ => setReloader(_ => reloader + 1)
<div className="grid grid-cols-2 gap-4">
<div>
<Antd.Card
title={"Distribution Form" |> R.ste}
extra={<Antd.Button icon=Antd.IconName.reload shape=#circle onClick=onReload />}>
<Form.Provider value=reform>
<Antd.Form onSubmit>
<Row _type=#flex className=Styles.rows>
<Col span=24> <FieldText field=FormConfig.SquiggleString label="Program" /> </Col>
</Row>
<Row _type=#flex className=Styles.rows>
<Col span=12> <FieldFloat field=FormConfig.SampleCount label="Sample Count" /> </Col>
<Col span=12>
<FieldFloat field=FormConfig.OutputXYPoints label="Output XY-points" />
</Col>
<Col span=12>
<FieldFloat field=FormConfig.DownsampleTo label="Downsample To" />
</Col>
<Col span=12> <FieldFloat field=FormConfig.KernelWidth label="Kernel Width" /> </Col>
<Col span=12>
<FieldFloat field=FormConfig.DiagramStart label="Diagram Start" />
</Col>
<Col span=12> <FieldFloat field=FormConfig.DiagramStop label="Diagram Stop" /> </Col>
<Col span=12>
<FieldFloat field=FormConfig.DiagramCount label="Diagram Count" />
</Col>
</Row>
</Antd.Form>
</Form.Provider>
</Antd.Card>
</div>
<div> demoDist </div>
</div>
}

View File

@ -1,180 +0,0 @@
// TODO: This setup is more confusing than it should be, there's more work to do in cleanup here.
module Inputs = {
module SamplingInputs = {
type t = {
sampleCount: option(int),
outputXYPoints: option(int),
kernelWidth: option(float),
shapeLength: option(int),
};
};
let defaultRecommendedLength = 100;
let defaultShouldDownsample = true;
type inputs = {
squiggleString: string,
samplingInputs: SamplingInputs.t,
environment: ExpressionTypes.ExpressionTree.environment,
};
let empty: SamplingInputs.t = {
sampleCount: None,
outputXYPoints: None,
kernelWidth: None,
shapeLength: None,
};
let make =
(
~samplingInputs=empty,
~squiggleString,
~environment=ExpressionTypes.ExpressionTree.Environment.empty,
(),
)
: inputs => {
samplingInputs,
squiggleString,
environment,
};
};
type export = [
| `DistPlus(ProbExample.DistPlus.t)
| `Float(float)
| `Function(
(array(string), ProbExample.ExpressionTypes.ExpressionTree.node),
ProbExample.ExpressionTypes.ExpressionTree.environment,
)
];
module Internals = {
let addVariable =
(
{samplingInputs, squiggleString, environment}: Inputs.inputs,
str,
node,
)
: Inputs.inputs => {
samplingInputs,
squiggleString,
environment:
ExpressionTypes.ExpressionTree.Environment.update(environment, str, _ =>
Some(node)
),
};
type outputs = {
graph: ExpressionTypes.ExpressionTree.node,
shape: DistTypes.shape,
};
let makeOutputs = (graph, shape): outputs => {graph, shape};
let makeInputs =
(inputs: Inputs.inputs): ExpressionTypes.ExpressionTree.samplingInputs => {
sampleCount: inputs.samplingInputs.sampleCount |> E.O.default(10000),
outputXYPoints:
inputs.samplingInputs.outputXYPoints |> E.O.default(10000),
kernelWidth: inputs.samplingInputs.kernelWidth,
shapeLength: inputs.samplingInputs.shapeLength |> E.O.default(10000),
};
let runNode = (inputs, node) => {
ExpressionTree.toLeaf(makeInputs(inputs), inputs.environment, node);
};
let runProgram = (inputs: Inputs.inputs, p: ExpressionTypes.Program.program) => {
let ins = ref(inputs);
p
|> E.A.fmap(
fun
| `Assignment(name, node) => {
ins := addVariable(ins^, name, node);
None;
}
| `Expression(node) =>
Some(
runNode(ins^, node) |> E.R.fmap(r => (ins^.environment, r)),
),
)
|> E.A.O.concatSomes
|> E.A.R.firstErrorOrOpen;
};
let inputsToLeaf = (inputs: Inputs.inputs) => {
MathJsParser.fromString(inputs.squiggleString)
|> E.R.bind(_, g => runProgram(inputs, g))
|> E.R.bind(_, r => E.A.last(r) |> E.O.toResult("No rendered lines"));
};
let outputToDistPlus = (inputs: Inputs.inputs, shape: DistTypes.shape) => {
DistPlus.make(~shape, ~squiggleString=Some(inputs.squiggleString), ());
};
};
let renderIfNeeded =
(inputs: Inputs.inputs, node: ExpressionTypes.ExpressionTree.node)
: result(ExpressionTypes.ExpressionTree.node, string) =>
node
|> (
fun
| `Normalize(_) as n
| `SymbolicDist(_) as n => {
`Render(n)
|> Internals.runNode(inputs)
|> (
fun
| Ok(`RenderedDist(_)) as r => r
| Error(r) => Error(r)
| _ => Error("Didn't render, but intended to")
);
}
| n => Ok(n)
);
// TODO: Consider using ExpressionTypes.ExpressionTree.getFloat or similar in this function
let coersionToExportedTypes =
(
inputs,
env: ProbExample.ExpressionTypes.ExpressionTree.environment,
node: ExpressionTypes.ExpressionTree.node,
)
: result(export, string) =>
node
|> renderIfNeeded(inputs)
|> E.R.bind(
_,
fun
| `RenderedDist(Discrete({xyShape: {xs: [|x|], ys: [|1.0|]}})) =>
Ok(`Float(x))
| `SymbolicDist(`Float(x)) => Ok(`Float(x))
| `RenderedDist(n) =>
Ok(`DistPlus(Internals.outputToDistPlus(inputs, n)))
| `Function(n) => Ok(`Function((n, env)))
| n =>
Error(
"Didn't output a rendered distribution. Format:"
++ ExpressionTree.toString(n),
),
);
let evaluateProgram = (inputs: Inputs.inputs) => {
inputs
|> Internals.inputsToLeaf
|> E.R.bind(_, ((a, b)) => coersionToExportedTypes(inputs, a, b));
};
let evaluateFunction =
(
inputs: Inputs.inputs,
fn: (array(string), ExpressionTypes.ExpressionTree.node),
fnInputs,
) => {
let output =
ExpressionTree.runFunction(
Internals.makeInputs(inputs),
inputs.environment,
fnInputs,
fn,
);
output |> E.R.bind(_, coersionToExportedTypes(inputs, inputs.environment));
};

View File

@ -0,0 +1,171 @@
// TODO: This setup is more confusing than it should be, there's more work to do in cleanup here.
module Inputs = {
module SamplingInputs = {
type t = {
sampleCount: option<int>,
outputXYPoints: option<int>,
kernelWidth: option<float>,
shapeLength: option<int>,
}
}
let defaultRecommendedLength = 100
let defaultShouldDownsample = true
type inputs = {
squiggleString: string,
samplingInputs: SamplingInputs.t,
environment: ExpressionTypes.ExpressionTree.environment,
}
let empty: SamplingInputs.t = {
sampleCount: None,
outputXYPoints: None,
kernelWidth: None,
shapeLength: None,
}
let make = (
~samplingInputs=empty,
~squiggleString,
~environment=ExpressionTypes.ExpressionTree.Environment.empty,
(),
): inputs => {
samplingInputs: samplingInputs,
squiggleString: squiggleString,
environment: environment,
}
}
type \"export" = [
| #DistPlus(ProbExample.DistPlus.t)
| #Float(float)
| #Function(
(array<string>, ProbExample.ExpressionTypes.ExpressionTree.node),
ProbExample.ExpressionTypes.ExpressionTree.environment,
)
]
module Internals = {
let addVariable = (
{samplingInputs, squiggleString, environment}: Inputs.inputs,
str,
node,
): Inputs.inputs => {
samplingInputs: samplingInputs,
squiggleString: squiggleString,
environment: ExpressionTypes.ExpressionTree.Environment.update(environment, str, _ => Some(
node,
)),
}
type outputs = {
graph: ExpressionTypes.ExpressionTree.node,
shape: DistTypes.shape,
}
let makeOutputs = (graph, shape): outputs => {graph: graph, shape: shape}
let makeInputs = (inputs: Inputs.inputs): ExpressionTypes.ExpressionTree.samplingInputs => {
sampleCount: inputs.samplingInputs.sampleCount |> E.O.default(10000),
outputXYPoints: inputs.samplingInputs.outputXYPoints |> E.O.default(10000),
kernelWidth: inputs.samplingInputs.kernelWidth,
shapeLength: inputs.samplingInputs.shapeLength |> E.O.default(10000),
}
let runNode = (inputs, node) =>
ExpressionTree.toLeaf(makeInputs(inputs), inputs.environment, node)
let runProgram = (inputs: Inputs.inputs, p: ExpressionTypes.Program.program) => {
let ins = ref(inputs)
p
|> E.A.fmap(x =>
switch x {
| #Assignment(name, node) =>
ins := addVariable(ins.contents, name, node)
None
| #Expression(node) =>
Some(runNode(ins.contents, node) |> E.R.fmap(r => (ins.contents.environment, r)))
}
)
|> E.A.O.concatSomes
|> E.A.R.firstErrorOrOpen
}
let inputsToLeaf = (inputs: Inputs.inputs) =>
MathJsParser.fromString(inputs.squiggleString)
|> E.R.bind(_, g => runProgram(inputs, g))
let outputToDistPlus = (inputs: Inputs.inputs, shape: DistTypes.shape) =>
DistPlus.make(~shape, ~squiggleString=Some(inputs.squiggleString), ())
}
let renderIfNeeded = (inputs: Inputs.inputs, node: ExpressionTypes.ExpressionTree.node): result<
ExpressionTypes.ExpressionTree.node,
string,
> =>
node |> (
x =>
switch x {
| #Normalize(_) as n
| #SymbolicDist(_) as n =>
#Render(n)
|> Internals.runNode(inputs)
|> (
x =>
switch x {
| Ok(#RenderedDist(_)) as r => r
| Error(r) => Error(r)
| _ => Error("Didn't render, but intended to")
}
)
| n => Ok(n)
}
)
// TODO: Consider using ExpressionTypes.ExpressionTree.getFloat or similar in this function
let coersionToExportedTypes = (
inputs,
env: ProbExample.ExpressionTypes.ExpressionTree.environment,
node: ExpressionTypes.ExpressionTree.node,
): result<\"export", string> =>
node
|> renderIfNeeded(inputs)
|> E.R.bind(_, x =>
switch x {
| #RenderedDist(Discrete({xyShape: {xs: [x], ys: [1.0]}})) => Ok(#Float(x))
| #SymbolicDist(#Float(x)) => Ok(#Float(x))
| #RenderedDist(n) => Ok(#DistPlus(Internals.outputToDistPlus(inputs, n)))
| #Function(n) => Ok(#Function(n, env))
| n => Error("Didn't output a rendered distribution. Format:" ++ ExpressionTree.toString(n))
}
)
let rec mapM = (f, xs) =>
switch xs {
| list{} => Ok(list{})
| list{x, ...rest} =>
switch f(x) {
| Error(err) => Error(err)
| Ok(val) =>
switch mapM(f, rest) {
| Error(err) => Error(err)
| Ok(restList) => Ok(list{val, ...restList})
}
}
}
let evaluateProgram = (inputs: Inputs.inputs) =>
inputs |> Internals.inputsToLeaf |> E.R.bind(_, xs => mapM(((a, b)) => coersionToExportedTypes(inputs, a, b), (Array.to_list(xs))))
let evaluateFunction = (
inputs: Inputs.inputs,
fn: (array<string>, ExpressionTypes.ExpressionTree.node),
fnInputs,
) => {
let output = ExpressionTree.runFunction(
Internals.makeInputs(inputs),
inputs.environment,
fnInputs,
fn,
)
output |> E.R.bind(_, coersionToExportedTypes(inputs, inputs.environment))
}

View File

@ -1382,6 +1382,11 @@
"@parcel/utils" "^1.11.0"
physical-cpu-count "^2.0.0"
"@rescript/react@^0.10.3":
version "0.10.3"
resolved "https://registry.yarnpkg.com/@rescript/react/-/react-0.10.3.tgz#a2a8bed6b017940ec26c2154764b350f50348889"
integrity sha512-Lf9rzrR3bQPKJjOK3PBRa/B3xrJ7CqQ1HYr9VHPVxJidarIJJFZBhj0Dg1uZURX+Wg/xiP0PHFxXmdj2bK8Vxw==
"@sinonjs/commons@^1.7.0":
version "1.7.2"
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.7.2.tgz#505f55c74e0272b43f6c52d81946bed7058fc0e2"