Add multiple exported graphs (broken)
This commit is contained in:
parent
01990dbe9f
commit
afd1481177
|
@ -37,7 +37,7 @@
|
|||
"@glennsl/bs-json",
|
||||
"@foretold/components",
|
||||
"bs-ant-design-alt",
|
||||
"reason-react",
|
||||
"@rescript/react",
|
||||
"bs-reform",
|
||||
"bs-css",
|
||||
"rationale",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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 */
|
|
@ -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>;
|
||||
};
|
271
src/components/DistBuilder.res
Normal file
271
src/components/DistBuilder.res
Normal 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>
|
||||
}
|
|
@ -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));
|
||||
};
|
171
src/distPlus/ProgramEvaluator.res
Normal file
171
src/distPlus/ProgramEvaluator.res
Normal 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))
|
||||
}
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue
Block a user