Cleanup part 1

This commit is contained in:
Ozzie Gooen 2020-11-12 23:35:45 -08:00
parent 1421c6a9b1
commit 255c1763e5
13 changed files with 2 additions and 1219 deletions

View File

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

View File

@ -1,88 +0,0 @@
// Examples:
// mm(floor(uniform(30,35)), normal(50,20), [.25,.5])
// mm(floor(normal(28,4)), normal(32,2), uniform(20,24), [.5,.2,.1])
// mm(5 to 20, floor(normal(20,2)), [.5, .5])"
// floor(3 to 4)
// uniform(0,1) > 0.3 ? lognormal(6.652, -0.41): 0
// let timeDist ={
// let ingredients = DistPlusRenderer.Inputs.Ingredients.make(
// ~guesstimatorString="(floor(10 to 15))",
// ~domain=RightLimited({xPoint: 50.0, excludingProbabilityMass: 0.3}),
// ~unit=
// DistTypes.TimeDistribution({zero: MomentRe.momentNow(), unit: `years}),
// ());
// let inputs = DistPlusRenderer.Inputs.make(~distPlusIngredients=ingredients,())
// inputs |> DistPlusRenderer.run
// }
// let setup = dist =>
// DistPlusRenderer.Inputs.make(~distPlusIngredients=dist,())
// |> DistPlusRenderer.run
// |> E.R.fmap(distPlus => <DistPlusPlot distPlus />)
// |> E.R.toOption
// |> E.O.toExn("")
// let simpleExample = (name, guesstimatorString) =>
// <>
// <h3 className="text-gray-600 text-lg font-bold">
// {name |> ReasonReact.string}
// </h3>
// {setup(DistPlusRenderer.Inputs.Ingredients.make(~guesstimatorString, ()))}
// </>;
// let timeExample = (name, guesstimatorString) =>
// <>
// <h3 className="text-gray-600 text-lg font-bold">
// {name |> ReasonReact.string}
// </h3>
// {setup(
// DistPlusRenderer.Inputs.Ingredients.make(
// ~guesstimatorString,
// ~unit=TimeDistribution({zero: MomentRe.momentNow(), unit: `years}),
// (),
// ),
// )}
// </>;
// let distributions = () =>
// <div>
// <div>
// <h2 className="text-gray-800 text-xl font-bold">
// {"Initial Section" |> ReasonReact.string}
// </h2>
// {simpleExample("Continuous", "5 to 20")}
// {simpleExample("Continuous, wide range", "1 to 1000000")}
// {simpleExample("Continuous, tiny values", "0.000000001 to 0.00000001")}
// {simpleExample(
// "Continuous large values",
// "50000000000000 to 200000000000000000",
// )}
// {simpleExample("Discrete", "floor(10 to 20)")}
// {simpleExample(
// "Discrete and below 0, normal(10,30)",
// "floor(normal(10,30))",
// )}
// {simpleExample("Discrete, wide range", "floor(10 to 200000)")}
// {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, Two-Discrete Points",
// "mm(0,10, 5 to 20, [.5,.5,.5])",
// )}
// <h2 className="text-gray-800 text-xl font-bold">
// {"Over Time" |> ReasonReact.string}
// </h2>
// {timeExample("Continuous", "5 to 20")}
// {timeExample("Continuous Over Long Period", "500 to 200000")}
// {timeExample("Continuous Over Short Period", "0.0001 to 0.001")}
// {timeExample(
// "Continuous Over Very Long Period",
// "500 to 20000000000000",
// )}
// {timeExample("Discrete", "floor(5 to 20)")}
// {timeExample("Mixed", "mm(5 to 20, floor(5 to 20), [.5,.5])")}
// </div>
// </div>;
// let entry = EntryTypes.(entry(~title="Mixed Distributions", ~render=distributions));

View File

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

View File

@ -1,26 +1,15 @@
type route = type route =
| Model(string)
| DistBuilder | DistBuilder
| Home | Home
| NotFound; | NotFound;
let routeToPath = route => let routeToPath = route =>
switch (route) { switch (route) {
| Model(modelId) => "/m/" ++ modelId
| DistBuilder => "/dist-builder" | DistBuilder => "/dist-builder"
| Home => "/" | Home => "/"
| _ => "/" | _ => "/"
}; };
module Models = {
let all = [|
EAFunds.Interface.model,
GlobalCatastrophe.Interface.model,
Human.Interface.model,
|];
let getById = id => E.A.getBy(all, r => r.id == id);
};
module Menu = { module Menu = {
module Styles = { module Styles = {
open Css; open Css;
@ -82,7 +71,6 @@ let make = () => {
let routing = let routing =
switch (url.path) { switch (url.path) {
| ["m", modelId] => Model(modelId)
| ["dist-builder"] => DistBuilder | ["dist-builder"] => DistBuilder
| [] => Home | [] => Home
| _ => NotFound | _ => NotFound
@ -91,14 +79,6 @@ let make = () => {
<> <>
<Menu /> <Menu />
{switch (routing) { {switch (routing) {
| Model(id) =>
(
switch (Models.getById(id)) {
| Some(model) => <FormBuilder.ModelForm model key=id />
| None => <div> {"Page is not found" |> R.ste} </div>
}
)
|> fixedLength
| DistBuilder => <DistBuilder /> | DistBuilder => <DistBuilder />
| Home => <Home /> | Home => <Home />
| _ => fixedLength({"Page is not found" |> R.ste}) | _ => fixedLength({"Page is not found" |> R.ste})

View File

@ -21,7 +21,6 @@ export class CodeEditor extends React.Component {
mode="golang" mode="golang"
height="400px" height="400px"
width="100%" width="100%"
keyboardHandler="vim"
theme="github" theme="github"
showGutter={false} showGutter={false}
highlightActiveLine={false} highlightActiveLine={false}

View File

@ -3,7 +3,7 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>Highly Speculative Forecasts</title> <title>Squiggle Language</title>
<link href="https://fonts.googleapis.com/css?family=Lato:300,400,700,900" rel="stylesheet"> <link href="https://fonts.googleapis.com/css?family=Lato:300,400,700,900" rel="stylesheet">
<link href="./styles/index.css" rel="stylesheet"> <link href="./styles/index.css" rel="stylesheet">
<script src="./Index.re" defer></script> <script src="./Index.re" defer></script>

View File

@ -1,104 +0,0 @@
open Prop;
type formState = {
combo: Combo.t,
setCombo: (Combo.t => Combo.t) => unit,
setInputValue: (Combo.t, string, option(Value.t)) => unit,
};
let makeHelpers = (combo): formState => {
let (combo, setCombo) = React.useState(() => combo);
let setInputValue = (combo, id, newValue) =>
setCombo(_ => Combo.updateInputValue(combo, id, newValue));
{combo, setCombo, setInputValue};
};
let propValue = (t: Prop.Value.t) => {
switch (t) {
| SelectSingle(r) => r |> ReasonReact.string
| ConditionalArray(r) => "Array" |> ReasonReact.string
| DistPlusIngredients((r: DistPlusRenderer.Inputs.ingredients)) =>
let newDistribution =
DistPlusRenderer.Inputs.make(
~distPlusIngredients=r,
(),
)
|> DistPlusRenderer.run;
switch (newDistribution) {
| Ok(distribution) => <div> <DistPlusPlot distPlus=distribution /> </div>
// <input
// 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"
// value={r.guesstimatorString}
// />
// <select
// defaultValue="years"
// readOnly=true
// className="appearance-none w-32 bg-white border border-gray-400 hover:border-gray-500 px-4 py-2 pr-8 rounded shadow leading-tight focus:outline-none focus:shadow-outline">
// <option> {"years" |> ReasonReact.string} </option>
// </select>
// <div
// 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}
// </div>
| Error(e) => e |> ReasonReact.string
};
| FloatCdf(_) => <div />
| Probability(r) =>
(r *. 100. |> Js.Float.toFixed) ++ "%" |> ReasonReact.string
| DateTime(r) => r |> MomentRe.Moment.defaultFormat |> ReasonReact.string
| FloatPoint(r) => r |> Js.Float.toFixed |> ReasonReact.string
};
};
module ModelForm = {
let handleChange = (handleChange, event) =>
handleChange(ReactEvent.Form.target(event)##value);
[@react.component]
let make = (~model: Model.t) => {
let formState = makeHelpers(Combo.fromModel(model));
<div>
<div
className="bg-white rounded px-8 pt-6 pb-8 mb-4 mt-6 border-gray-200 border-solid border-2">
<h1 className="text-gray-800 text-xl font-bold">
{model.name |> ReasonReact.string}
</h1>
<p> {model.description |> ReasonReact.string} </p>
<p> {model.author |> ReasonReact.string} </p>
<ForetoldComponents.Link
href={
"https://github.com/foretold-app/estiband/blob/master/src/models/"
++ model.fileName
}>
{"Model Code" |> ReasonReact.string}
</ForetoldComponents.Link>
{Combo.inputTypeValuePairs(formState.combo)
|> E.A.fmap(((type_: TypeWithMetadata.t, value)) =>
<div className="box-border">
<label
className="block text-gray-700 text-sm font-bold mb-2 w-32 mt-3">
{type_.name |> ReasonReact.string}
</label>
<ValueForm
type_
value
onChange={newValue =>
formState.setInputValue(
formState.combo,
type_.id,
newValue,
)
}
/>
</div>
)
|> ReasonReact.array}
<div className="bg-green-100 p-2 rounded-sm mt-6 text-lg">
{model.run(Prop.Combo.InputValues.toValueArray(formState.combo))
|> R.O.fmapOrNull(propValue)}
</div>
</div>
</div>;
};
};

View File

@ -1,329 +0,0 @@
module Value = {
type conditional = {
name: string,
truthValue: bool,
};
type t =
| SelectSingle(string)
| DateTime(MomentRe.Moment.t)
| FloatPoint(float)
| Probability(float)
| DistPlusIngredients(DistPlusRenderer.Inputs.ingredients)
| ConditionalArray(array(conditional))
| FloatCdf(string);
module ConditionalArray = {
let get = (conditionals: array(conditional), name: string) =>
Belt.Array.getBy(conditionals, (c: conditional) => c.name == name);
};
};
module ValueCombination = {
type pointsToEvenlySample = int;
type dateTimeRange = {
startTime: MomentRe.Moment.t,
endTime: MomentRe.Moment.t,
pointsWithin: int,
};
type floatPointRange = {
startTime: float,
endTime: float,
pointsWithin: int,
};
type range('a) = {
beginning: 'a,
ending: 'a,
pointsToEvenlySample,
};
type t =
| SelectSingle
| DateTime(range(MomentRe.Moment.t))
| FloatPoint(range(MomentRe.Moment.t))
| Probability(pointsToEvenlySample);
};
module ValueCluster = {
type conditional = {
name: string,
truthValue: bool,
};
type pointsToEvenlySample = int;
type dateTimeRange = {
startTime: MomentRe.Moment.t,
endTime: MomentRe.Moment.t,
pointsWithin: int,
};
type floatPointRange = {
startTime: float,
endTime: float,
pointsWithin: int,
};
type range('a) = {
beginning: 'a,
ending: 'a,
pointsToEvenlySample,
};
type t =
| SelectSingle([ | `combination | `item(string)])
| DateTime(
[
| `combination(range(MomentRe.Moment.t))
| `item(MomentRe.Moment.t)
],
)
| FloatPoint(
[ | `combination(range(MomentRe.Moment.t)) | `item(string)],
)
| Probability([ | `item(string)])
| DistPlusIngredients([ | `item(DistPlusRenderer.Inputs.ingredients)])
| ConditionalArray([ | `item(array(conditional))])
| FloatCdf([ | `item(string)]);
};
module Type = {
type selectOption = {
id: string,
name: string,
};
type selectSingle = {
options: list(selectOption),
default: option(string),
};
type conditionals = {
defaults: array(Value.conditional),
options: array(string),
};
let makeConditionals = (defaults, options): conditionals => {
defaults,
options,
};
type floatPoint = {validatations: list(float => bool)};
type withDefaultMinMax('a) = {
default: option('a),
min: option('a),
max: option('a),
};
type withDefault('a) = {default: option('a)};
type t =
| SelectSingle(selectSingle)
| FloatPoint(withDefaultMinMax(float))
| Probability(withDefault(float))
| DateTime(withDefaultMinMax(MomentRe.Moment.t))
| Year(withDefaultMinMax(float))
| Conditionals(conditionals)
| FloatCdf;
let default = (t: t) =>
switch (t) {
| Conditionals(s) => Some(Value.ConditionalArray(s.defaults))
| Year(r) => r.default->Belt.Option.map(p => Value.FloatPoint(p))
| FloatPoint(r) => r.default->Belt.Option.map(p => Value.FloatPoint(p))
| Probability(r) => r.default->Belt.Option.map(p => Value.Probability(p))
| DateTime(r) => r.default->Belt.Option.map(p => Value.DateTime(p))
| SelectSingle(r) =>
r.default->Belt.Option.map(p => Value.SelectSingle(p))
| FloatCdf => None
};
};
module ValueMap = {
module MS = Belt.Map.String;
type t = MS.t(Value.t);
let get = (t: t, s) => MS.get(t, s);
let keys = MS.keysToArray;
let map = MS.map;
let fromArray = (r): t => MS.fromArray(r);
let values = (t: t) => t |> MS.valuesToArray;
let update = (t, k, v) => MS.update(t, k, _ => v);
let toArray = MS.toArray;
let fromOptionalMap = (t: MS.t(option(Value.t))): t =>
MS.keep(t, (_, d) => E.O.isSome(d))
->MS.map(d => E.O.toExn("This should not have happened", d));
let fromOptionalArray = (r): t => MS.fromArray(r) |> fromOptionalMap;
};
module ValueClusterMap = {
module MS = Belt.Map.String;
type t = MS.t(ValueCluster.t);
let get = (t: t, s) => MS.get(t, s);
let keys = MS.keysToArray;
let map = MS.map;
let fromArray = (r): t => MS.fromArray(r);
let values = (t: t) => t |> MS.valuesToArray;
let update = (t, k, v) => MS.update(t, k, _ => v);
let toArray = MS.toArray;
let fromOptionalMap = (t: MS.t(option(ValueCluster.t))): t =>
MS.keep(t, (_, d) => E.O.isSome(d))
->MS.map(d => E.O.toExn("This should not have happened", d));
let fromOptionalArray = (r): t => MS.fromArray(r) |> fromOptionalMap;
};
module TypeWithMetadata = {
// TODO: Figure out a better name for assumptionType
type assumptionType =
| PRIMARY_INPUT
| ASSUMPTION;
type t = {
id: string,
name: string,
description: option(string),
type_: Type.t,
assumptionType,
};
type ts = array(t);
let make =
(
~name,
~type_,
~id=name,
~description=None,
~assumptionType=PRIMARY_INPUT,
(),
) => {
id,
name,
type_,
description,
assumptionType,
};
let currentYear =
make(
~id="currentYear",
~name="Current Day",
~description=None,
~type_=
DateTime({
default: Some(MomentRe.momentNow()),
min: Some(MomentRe.momentNow()),
max: Some(MomentRe.momentNow()),
}),
~assumptionType=ASSUMPTION,
(),
);
let age =
make(
~id="age",
~name="Current Age",
~description=None,
~type_=
FloatPoint({
default: Some(40.0),
min: Some(0.0),
max: Some(100.0),
}),
~assumptionType=PRIMARY_INPUT,
(),
);
let sex =
make(
~id="sex",
~name="Sex",
~description=None,
~type_=
SelectSingle({
options: [
{id: "male", name: "Male"},
{id: "female", name: "Female"},
],
default: Some("female"),
}),
~assumptionType=PRIMARY_INPUT,
(),
);
};
module Model = {
type t = {
name: string,
id: string,
fileName: string,
description: string,
author: string,
version: string,
inputTypes: array(TypeWithMetadata.t),
outputTypes: array(TypeWithMetadata.t),
run: array(option(Value.t)) => option(Value.t),
}
and combo = {
model: t,
inputValues: ValueMap.t,
outputValues: ValueMap.t,
};
module InputTypes = {
let keys = (t: t) =>
t.inputTypes |> E.A.fmap((r: TypeWithMetadata.t) => r.id);
let getBy = (t: t, fn) => t.inputTypes |> E.A.getBy(_, fn);
};
};
module Combo = {
type t = Model.combo;
type valueArray = array(option(Value.t));
module InputValues = {
let defaults = (t: Model.t) =>
t.inputTypes
|> E.A.fmap((o: TypeWithMetadata.t) => (o.id, Type.default(o.type_)))
|> ValueMap.fromOptionalArray;
let isValid = (t: t) =>
t.model
|> Model.InputTypes.keys
|> E.A.fmap(ValueMap.get(t.inputValues))
|> Belt.Array.some(_, E.O.isNone);
let update = (t: t, key: string, onUpdate: option(Value.t)) =>
ValueMap.update(t.inputValues, key, onUpdate);
let toValueArray = (t: t): valueArray => {
t.model.inputTypes
|> E.A.fmap((r: TypeWithMetadata.t) =>
t.inputValues->ValueMap.get(r.id)
);
};
};
let updateInputValue = (t: t, k, u) => {
...t,
inputValues: InputValues.update(t, k, u),
};
let inputTypeValuePairs = (t: t) =>
t.model.inputTypes
|> E.A.fmap((i: TypeWithMetadata.t) =>
(i, ValueMap.get(t.inputValues, i.id))
);
let fromModel = (t: Model.t): t => {
model: t,
inputValues: InputValues.defaults(t),
outputValues: InputValues.defaults(t),
};
let run = (t: t, f): ValueMap.t => f(t.inputValues);
};

View File

@ -1,157 +0,0 @@
open Prop;
let handleChange = (handleChange, event) =>
handleChange(ReactEvent.Form.target(event)##value);
type onChange = option(Value.t) => unit;
module ConditionalReducer = {
type action =
| ADD_OR_UPDATE_CONDITIONAL(Value.conditional)
| REMOVE_CONDITIONAL(Value.conditional);
let reducer = (items: array(Value.conditional), action: action) =>
switch (action) {
| ADD_OR_UPDATE_CONDITIONAL(conditional) =>
items->E.A.hasBy(c => c.name == conditional.name)
? items
|> E.A.fmap((r: Value.conditional) =>
r.name == conditional.name ? conditional : r
)
: E.A.append(items, [|conditional|])
| REMOVE_CONDITIONAL(conditional) =>
items
|> E.A.filter((c: Value.conditional) => c.name != conditional.name)
};
};
[@react.component]
let make =
(
~type_: TypeWithMetadata.t,
~value: option(Value.t),
~onChange: onChange,
) => {
switch (type_.type_, value) {
| (Conditionals(l), Some(ConditionalArray(n))) =>
<div>
{n
|> E.A.fmap((r: Value.conditional) =>
<div
onClick={_ =>
onChange(
Some(
Value.ConditionalArray(
ConditionalReducer.reducer(
n,
REMOVE_CONDITIONAL({name: r.name, truthValue: true}),
),
),
),
)
}>
{r.name |> ReasonReact.string}
{(r.truthValue ? " = True" : " = False") |> ReasonReact.string}
</div>
)
|> ReasonReact.array}
{l.options
|> E.A.fmap(r =>
<div
className="max-w-sm rounded overflow-hidden shadow-sm py-1 px-2 rounded mb-3 bg-gray-200">
{r |> ReasonReact.string}
<button
className="bg-blue-500 hover:bg-blue-700 text-white py-0 px-1 rounded"
onClick={e => {
ReactEvent.Synthetic.preventDefault(e);
onChange(
Some(
Value.ConditionalArray(
ConditionalReducer.reducer(
n,
ADD_OR_UPDATE_CONDITIONAL({
name: r,
truthValue: true,
}),
),
),
),
);
();
}}>
{"=True" |> ReasonReact.string}
</button>
<button
className="hover:bg-red-700 text-white py-0 px-1 rounded bg-red-500"
onClick={e => {
ReactEvent.Synthetic.preventDefault(e);
onChange(
Some(
Value.ConditionalArray(
ConditionalReducer.reducer(
n,
ADD_OR_UPDATE_CONDITIONAL({
name: r,
truthValue: false,
}),
),
),
),
);
}}>
{"=False" |> ReasonReact.string}
</button>
</div>
)
|> ReasonReact.array}
</div>
| (Conditionals(l), _) =>
l.options |> E.A.fmap(r => r |> ReasonReact.string) |> ReasonReact.array
| (Year(_), Some(FloatPoint(r))) =>
<input
className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
type_="number"
value={r |> Js.Float.toString}
onChange={handleChange(r =>
switch (Js.Float.fromString(r)) {
| r => onChange(Some(Value.FloatPoint(r)))
}
)}
/>
| (FloatPoint(_), Some(FloatPoint(r))) =>
<input
className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
type_="number"
value={r |> Js.Float.toString}
onChange={handleChange(r =>
switch (Js.Float.fromString(r)) {
| r => onChange(Some(Value.FloatPoint(r)))
}
)}
/>
| (Year(_), _)
| (FloatPoint(_), _) => <input type_="number" value="" />
| (SelectSingle(t), Some(SelectSingle(r))) =>
<select
defaultValue=r
className="block appearance-none w-full bg-white border border-gray-400 hover:border-gray-500 px-4 py-2 pr-8 rounded shadow leading-tight focus:outline-none focus:shadow-outline"
onChange={handleChange(l => onChange(Some(Value.SelectSingle(l))))}>
{t.options
|> E.A.of_list
|> E.A.fmap((l: Type.selectOption) =>
<option value={l.id} key={l.id}>
{l.name |> ReasonReact.string}
</option>
)
|> ReasonReact.array}
</select>
| (DateTime(_), Some(DateTime((d: MomentRe.Moment.t)))) =>
<input
type_="date"
value={MomentRe.Moment.format("YYYY-MM-DD", d)}
onChange={handleChange(r =>
onChange(
Some(Value.DateTime(MomentRe.momentWithFormat(r, "YYYY-MM-DD"))),
)
)}
/>
};
};

View File

@ -1,306 +0,0 @@
module Data = {
type fund =
| ANIMAL_WELFARE
| GLOBAL_HEALTH
| LONG_TERM_FUTURE
| META;
type group =
| Fund(fund)
| All;
type output =
| DONATIONS
| CHANCE_OF_EXISTENCE
| PAYOUTS;
type conditionals =
| WORLD_CATASTROPHE;
type fundWithInfo = {
group,
name: string,
existingDonations: option(float),
existingPayouts: option(float),
};
let makeFundWithInfo = (name, group, existingDonations, existingPayouts) => {
group,
name,
existingDonations,
existingPayouts,
};
let funds = [|
makeFundWithInfo(
"Animal Welfare Fund",
Fund(ANIMAL_WELFARE),
Some(400000.0),
Some(100000.0),
),
makeFundWithInfo(
"Global Health Fund",
Fund(GLOBAL_HEALTH),
Some(400000.0),
Some(100000.0),
),
makeFundWithInfo(
"Long Term Future Fund",
Fund(LONG_TERM_FUTURE),
Some(400000.0),
Some(100000.0),
),
makeFundWithInfo(
"Meta Fund",
Fund(ANIMAL_WELFARE),
Some(400000.0),
Some(100000.0),
),
makeFundWithInfo("All", All, None, None),
|];
};
module Model = {
open Data;
let currentYear = 2020.;
let firstYearStdDev = 400000.;
type yearlyNumericDiff = {
meanDiff: float,
stdDiff: float,
};
let yearlyMeanGrowthRateIfNotClosed = (group: group): yearlyNumericDiff => {
{meanDiff: 1.1, stdDiff: 1.2};
};
let calculateDifference =
(currentValue, dateTime, currentDateTime, y: yearlyNumericDiff) => {
let yearDiff = MomentRe.diff(dateTime, currentDateTime, `days) /. 365.;
let meanDiff = Js.Math.pow_float(~base=y.meanDiff, ~exp=yearDiff);
let stdDevDiff = Js.Math.pow_float(~base=y.stdDiff, ~exp=yearDiff);
GuesstimatorDist.logNormal(
currentValue *. meanDiff,
firstYearStdDev *. stdDevDiff,
);
};
let rec currentValue = (group: group, output) => {
let sum = (): float =>
currentValue(Fund(ANIMAL_WELFARE), output)
+. currentValue(Fund(GLOBAL_HEALTH), output)
+. currentValue(Fund(LONG_TERM_FUTURE), output)
+. currentValue(Fund(META), output);
switch (group, output) {
| (Fund(ANIMAL_WELFARE), DONATIONS) => 300000.0
| (Fund(ANIMAL_WELFARE), PAYOUTS) => 2300000.0
| (Fund(GLOBAL_HEALTH), DONATIONS) => 1000000.0
| (Fund(GLOBAL_HEALTH), PAYOUTS) => 500000.0
| (Fund(LONG_TERM_FUTURE), DONATIONS) => 600000.0
| (Fund(LONG_TERM_FUTURE), PAYOUTS) => 120000.0
| (Fund(META), DONATIONS) => 9300000.0
| (Fund(META), PAYOUTS) => 830000.0
| (All, _) => sum()
| (_, CHANCE_OF_EXISTENCE) => 0.0
};
};
let xRisk = conditionals =>
Prop.Value.ConditionalArray.get(conditionals, "Global Existential Event");
// TODO: Fixe number that integral is calculated for
let getGlobalCatastropheChance = dateTime => {
GlobalCatastrophe.makeI(MomentRe.momentNow())
|> DistPlusRenderer.Inputs.make(~distPlusIngredients=_, ())
|> DistPlusRenderer.run
|> E.R.bind(_, r =>
r
|> DistPlusTime.Integral.xToY(Time(dateTime))
|> E.O.toResult("error")
)
|> E.R.toOption;
};
let make =
(
group: group,
dateTime: MomentRe.Moment.t,
currentDateTime: MomentRe.Moment.t,
output: output,
conditionals: array(Prop.Value.conditional),
) => {
let xRisk = xRisk(conditionals);
switch (output) {
| DONATIONS
| PAYOUTS =>
let difference =
calculateDifference(
currentValue(group, output),
dateTime,
currentDateTime,
yearlyMeanGrowthRateIfNotClosed(group),
);
let str =
switch (xRisk) {
| Some({truthValue: true}) => "0"
| Some({truthValue: false}) => difference
| None =>
let foo =
getGlobalCatastropheChance(dateTime)
|> E.O.fmap(r => {
let chance = r;
let opposite = 1.0 -. r;
let reg = difference;
{j|mm(0, $reg, [$chance, $opposite])|j};
});
foo |> E.O.default("");
};
let distPlusIngredients =
DistPlusRenderer.Inputs.Ingredients.make(
~guesstimatorString=str,
~domain=Complete,
~unit=UnspecifiedDistribution,
(),
);
Prop.Value.DistPlusIngredients(distPlusIngredients);
| CHANCE_OF_EXISTENCE =>
Prop.Value.DistPlusIngredients(
DistPlusRenderer.Inputs.Ingredients.make(
~guesstimatorString=
GuesstimatorDist.min(
GlobalCatastrophe.guesstimatorString,
GuesstimatorDist.logNormal(20., 2.),
),
~unit=TimeDistribution({zero: currentDateTime, unit: `years}),
~domain=RightLimited({xPoint: 100., excludingProbabilityMass: 0.3}),
(),
),
)
};
};
};
module Interface = {
open Data;
let fundKey = "Fund";
let dayKey = "Day";
let outputKey = "Output";
let choiceFromString = (s: string) =>
funds |> E.A.getBy(_, r => r.name == s);
let outputFromString = (s: string) =>
switch (s) {
| "donations" => DONATIONS
| "exists" => CHANCE_OF_EXISTENCE
| _ => PAYOUTS
};
let run = (p: array(option(Prop.Value.t))) => {
switch (p) {
| [|
Some(SelectSingle(fund)),
Some(DateTime(intendedYear)),
Some(DateTime(currentYear)),
Some(SelectSingle(output)),
Some(ConditionalArray(conditionals)),
|] =>
choiceFromString(fund)
|> E.O.fmap(fund =>
Model.make(
fund.group,
intendedYear,
currentYear,
outputFromString(output),
conditionals,
)
)
| _ => None
};
};
let model: Prop.Model.t =
Prop.{
id: "ea-funds",
name: "EA Funds: Donations & Payouts",
description: "Calculate the payments and payouts of CEA Funds based on existing data.",
version: "1.0.0",
fileName: "EAFunds.re",
author: "Ozzie Gooen",
inputTypes: [|
TypeWithMetadata.make(
~name=fundKey,
~type_=
SelectSingle({
default: Some(Array.unsafe_get(Data.funds, 0).name),
options:
Data.funds
|> E.A.fmap((r) =>
({name: r.name, id: r.name}: Prop.Type.selectOption)
)
|> Array.to_list,
}),
(),
),
TypeWithMetadata.make(
~name=dayKey,
~type_=
DateTime({
default:
Some(
MomentRe.Moment.add(
~duration=MomentRe.duration(5., `years),
MomentRe.momentNow(),
),
),
min:
Some(
MomentRe.Moment.subtract(
~duration=MomentRe.duration(20., `years),
MomentRe.momentNow(),
),
),
max:
Some(
MomentRe.Moment.add(
~duration=MomentRe.duration(20., `years),
MomentRe.momentNow(),
),
),
}),
(),
),
TypeWithMetadata.currentYear,
TypeWithMetadata.make(
~name=outputKey,
~type_=
SelectSingle({
default: Some("Output"),
options: [
{name: "Donations", id: "donations"},
{name: "Funding", id: "funding"},
{name: "Closing", id: "exists"},
],
}),
(),
),
TypeWithMetadata.make(
~name="Conditionals",
~id="conditionals",
~type_=
Conditionals(
Prop.Type.makeConditionals(
[||],
[|"Global Existential Event"|],
),
),
(),
),
|],
outputTypes: [||],
run,
};
};

View File

@ -1,39 +0,0 @@
let guesstimatorString = "uniform(1, 100)";
let makeI = (currentDateTime: MomentRe.Moment.t) => {
DistPlusRenderer.Inputs.Ingredients.make(
~guesstimatorString,
~unit=TimeDistribution({zero: currentDateTime, unit: `years}),
~domain=RightLimited({xPoint: 300.0, excludingProbabilityMass: 0.3}),
(),
);
};
module Model = {
let make = (currentDateTime: MomentRe.Moment.t) => {
Prop.Value.DistPlusIngredients(makeI(currentDateTime));
};
};
module Interface = {
let dayKey = "Day";
let run = (p: array(option(Prop.Value.t))) => {
switch (p) {
| [|Some(DateTime(currentYear))|] => Some(Model.make(currentYear))
| _ => None
};
};
let model: Prop.Model.t =
Prop.{
name: "Global Catastrophe",
id: "global-catastrophe",
fileName: "GlobalCatastrophe.re",
description: "The chances of having at least one catastrophe per year in the future, assuming no other catastrophe until then.",
version: "1.0.0",
author: "Ozzie Gooen",
inputTypes: [|TypeWithMetadata.currentYear|],
outputTypes: [||],
run,
};
};

View File

@ -1,42 +0,0 @@
let guesstimatorString = age =>
GuesstimatorDist.normal(72.0 -. age, 5.0 -. age *. 0.01);
let makeI = (age: float) => {
DistPlusRenderer.Inputs.Ingredients.make(
~guesstimatorString=guesstimatorString(age),
~unit=TimeDistribution({zero: MomentRe.momentNow(), unit: `years}),
~domain=RightLimited({xPoint: 300.0, excludingProbabilityMass: 0.3}),
(),
);
};
module Model = {
let make = (age: float) => {
Prop.Value.DistPlusIngredients(makeI(age));
};
};
module Interface = {
let ageKey = "age";
let run = (p: array(option(Prop.Value.t))) => {
switch (p) {
| [|Some(FloatPoint(age)), Some(SelectSingle(sex))|] =>
Some(Model.make(age))
| _ => None
};
};
let model: Prop.Model.t =
Prop.{
name: "Death Time",
id: "human-lifespan",
fileName: "Human.re",
description: "When will you die?",
version: "1.0.0",
author: "Ozzie Gooen",
inputTypes: [|TypeWithMetadata.age, TypeWithMetadata.sex|],
outputTypes: [||],
run,
};
};

View File

@ -1,20 +1,3 @@
type route =
| Model(string);
let routeToPath = route =>
switch (route) {
| Model(modelId) => "/m/" ++ modelId
};
module Models = {
let all = [|
EAFunds.Interface.model,
GlobalCatastrophe.Interface.model,
Human.Interface.model,
|];
let getById = id => E.A.getBy(all, r => r.id == id);
};
module Styles = { module Styles = {
open Css; open Css;
let h3 = style([fontSize(`em(1.5)), marginBottom(`em(1.5))]); let h3 = style([fontSize(`em(1.5)), marginBottom(`em(1.5))]);
@ -30,51 +13,9 @@ module Table = {
}; };
}; };
module Item = {
[@react.component]
let make = (~model: Prop.Model.t, ~children) => {
<div className=TableStyles.row>
<div className={TableStyles.col()}>
<a
href={routeToPath(Model(model.id))}
onClick={e => {
e->ReactEvent.Synthetic.preventDefault;
ReasonReactRouter.push(routeToPath(Model(model.id)));
}}>
children
</a>
</div>
<div className={TableStyles.col(~f=3.0, ())}>
{model.description |> R.ste}
</div>
<div className={TableStyles.col()}> {model.author |> R.ste} </div>
</div>;
};
};
module ColumnsTitles = {
[@react.component]
let make = () => {
<div className=TableStyles.row>
<div className={TableStyles.col()}> {"Name" |> R.ste} </div>
<div className={TableStyles.col(~f=3.0, ())}>
{"Description" |> R.ste}
</div>
<div className={TableStyles.col()}> {"Author" |> R.ste} </div>
</div>;
};
};
[@react.component] [@react.component]
let make = () => { let make = () => {
<> <>
<h3 className=Styles.h3> {"Probability Models" |> R.ste} </h3>
<ColumnsTitles />
{Models.all
|> E.A.fmap((model: Prop.Model.t) => {
<Item model key={model.id}> {model.name |> R.ste} </Item>
})
|> ReasonReact.array}
</>; </>;
}; };
}; };