diff --git a/__tests__/Parser__test.re b/__tests__/Parser__test.re
index 79ee6ac2..935ca668 100644
--- a/__tests__/Parser__test.re
+++ b/__tests__/Parser__test.re
@@ -1,21 +1,12 @@
open Jest;
open Expect;
-let json = Mathjs.parseMath("mm(normal(5,2), normal(10))");
-
describe("Shape", () => {
describe("Parser", () => {
test("", () => {
- let parsed1 = MathJsParser.parseMathjs(json);
- let parsed2 =
- (
- switch (parsed1 |> E.O.fmap(MathJsParser.toValue)) {
- | Some(Ok(r)) => Some(r)
- | _ => None
- }
- )
- |> E.O.fmap(Jstat.toString);
- Js.log2("YOYOYYO", parsed2);
+ let parsed1 = MathJsParser.fromString("mm(normal(0,1), normal(10,1))");
+ Js.log(parsed1 |> E.R.fmap(Jstat.toString));
+ Js.log(parsed1 |> E.R.fmap(Jstat.toShape(20)));
expect(1.0) |> toEqual(1.0);
})
})
diff --git a/src/App.re b/src/App.re
index e2526c25..236879d6 100644
--- a/src/App.re
+++ b/src/App.re
@@ -2,6 +2,7 @@ type route =
| Model(string)
| DistBuilder
| DistBuilder2
+ | DistBuilder3
| Home
| NotFound;
@@ -10,6 +11,7 @@ let routeToPath = route =>
| Model(modelId) => "/m/" ++ modelId
| DistBuilder => "/dist-builder"
| DistBuilder2 => "/dist-builder2"
+ | DistBuilder3 => "/dist-builder3"
| Home => "/"
| _ => "/"
};
@@ -81,6 +83,9 @@ module Menu = {
-
{"Dist Builder 2" |> E.ste}
+ -
+ {"Dist Builder 3" |> E.ste}
+
;
};
};
@@ -94,6 +99,7 @@ let make = () => {
| ["m", modelId] => Model(modelId)
| ["dist-builder"] => DistBuilder
| ["dist-builder2"] => DistBuilder2
+ | ["dist-builder3"] => DistBuilder3
| [] => Home
| _ => NotFound
};
@@ -108,8 +114,9 @@ let make = () => {
}
| DistBuilder =>
| DistBuilder2 =>
+ | DistBuilder3 =>
| Home =>
{"Welcome" |> E.ste}
| _ => {"Page is not found" |> E.ste}
}}
;
-};
+};
\ No newline at end of file
diff --git a/src/components/DistBuilder3.re b/src/components/DistBuilder3.re
new file mode 100644
index 00000000..a3f36c1e
--- /dev/null
+++ b/src/components/DistBuilder3.re
@@ -0,0 +1,114 @@
+open BsReform;
+open Antd.Grid;
+
+module FormConfig = [%lenses type state = {guesstimatorString: string}];
+
+module Form = ReForm.Make(FormConfig);
+
+let schema = Form.Validation.Schema([||]);
+
+module FieldString = {
+ [@react.component]
+ let make = (~field, ~label) => {
+
+ E.ste}>
+ validate()}
+ />
+
+ }
+ />;
+ };
+};
+
+module Styles = {
+ open Css;
+ let dist = style([padding(em(1.))]);
+ let spacer = style([marginTop(em(1.))]);
+};
+
+module DemoDist = {
+ [@react.component]
+ let make = (~guesstimatorString: string) => {
+ let parsed1 = MathJsParser.fromString(guesstimatorString);
+ let shape =
+ switch (parsed1) {
+ | Ok(r) => Some(Jstat.toShape(10000, r))
+ | _ => None
+ };
+
+ let str =
+ switch (parsed1) {
+ | Ok(r) => Jstat.toString(r)
+ | Error(e) => e
+ };
+
+ let inside =
+ shape
+ |> E.O.fmap(shape => {
+ let distPlus =
+ Distributions.DistPlus.make(
+ ~shape=Continuous(Distributions.Continuous.fromShape(shape)),
+ ~domain=Complete,
+ ~unit=UnspecifiedDistribution,
+ ~guesstimatorString=None,
+ (),
+ )
+ |> Distributions.DistPlus.T.scaleToIntegralSum(~intendedSum=1.0);
+ ;
+ })
+ |> E.O.default(ReasonReact.null);
+ E.ste}>
+
+ inside
+ {str |> ReasonReact.string}
+ ;
+ };
+};
+
+[@react.component]
+let make = () => {
+ let reform =
+ Form.use(
+ ~validationStrategy=OnDemand,
+ ~schema,
+ ~onSubmit=({state}) => {None},
+ ~initialState={guesstimatorString: "lognormal(6.1, 3)"},
+ (),
+ );
+
+ let demoDist =
+ React.useMemo1(
+ () => {
+
+ },
+ [|reform.state.values.guesstimatorString|],
+ );
+
+
+
+ demoDist
+
+
E.ste}>
+
+
+
+
+
+
+
+
+
+
+
+
;
+};
\ No newline at end of file
diff --git a/src/components/charts/DistPlusPlot.re b/src/components/charts/DistPlusPlot.re
index bf041b89..32886ddb 100644
--- a/src/components/charts/DistPlusPlot.re
+++ b/src/components/charts/DistPlusPlot.re
@@ -216,20 +216,31 @@ module DistPlusChart = {
|> E.O.fmap(Distributions.Continuous.getShape);
let range = T.xTotalRange(distPlus);
- // We subtract a bit from the range to make sure that it fits. Maybe this should be done in d3 instead.
- let minX =
- switch (
- distPlus |> Distributions.DistPlus.T.Integral.yToX(~cache=None, 0.01),
- range,
- ) {
- | (min, Some(range)) => Some(min -. range *. 0.001)
- | _ => None
- };
+ // // We subtract a bit from the range to make sure that it fits. Maybe this should be done in d3 instead.
+ // let minX =
+ // switch (
+ // distPlus
+ // |> Distributions.DistPlus.T.Integral.yToX(~cache=None, 0.0001),
+ // range,
+ // ) {
+ // | (min, Some(range)) => Some(min -. range *. 0.001)
+ // | _ => None
+ // };
+
+ let minX = {
+ distPlus |> Distributions.DistPlus.T.Integral.yToX(~cache=None, 0.00001);
+ };
let maxX = {
distPlus |> Distributions.DistPlus.T.Integral.yToX(~cache=None, 0.99);
};
+ Js.log3(
+ distPlus |> Distributions.DistPlus.T.Integral.yToX(~cache=None, 0.0001),
+ minX,
+ distPlus |> Distributions.DistPlus.T.Integral.yToX(~cache=None, 0.98),
+ );
+
let timeScale = distPlus.unit |> DistTypes.DistributionUnit.toJson;
let toDiscreteProbabilityMass =
distPlus |> Distributions.DistPlus.T.toDiscreteProbabilityMass;
diff --git a/src/symbolic/Jstat.re b/src/symbolic/Jstat.re
index 654c2bfc..3a415ae5 100644
--- a/src/symbolic/Jstat.re
+++ b/src/symbolic/Jstat.re
@@ -102,15 +102,15 @@ module Mixed = {
let min = dist =>
switch (dist) {
- | `Normal(n) => Normal.inv(0.01, n)
- | `Lognormal(n) => Lognormal.inv(0.01, n)
+ | `Normal(n) => Normal.inv(0.0001, n)
+ | `Lognormal(n) => Lognormal.inv(0.0001, n)
| `Uniform({low}) => low
};
let max = dist =>
switch (dist) {
- | `Normal(n) => Normal.inv(0.99, n)
- | `Lognormal(n) => Lognormal.inv(0.99, n)
+ | `Normal(n) => Normal.inv(0.9999, n)
+ | `Lognormal(n) => Lognormal.inv(0.9999, n)
| `Uniform({high}) => high
};
@@ -121,7 +121,7 @@ module Mixed = {
switch (xSelection) {
| `Linear => Functions.range(min(dist), max(dist), sampleCount)
| `ByWeight =>
- Functions.range(0.001, 0.999, sampleCount)
+ Functions.range(0.00001, 0.99999, sampleCount)
|> E.A.fmap(x => inv(x, dist))
};
let ys = xs |> E.A.fmap(r => pdf(r, dist));
@@ -151,7 +151,7 @@ module PointwiseAddDistributionsWeighted = {
dists |> E.A.fmap(d => d |> fst |> Mixed.min) |> Functions.min;
let max = (dists: t) =>
- dists |> E.A.fmap(d => d |> fst |> Mixed.min) |> Functions.min;
+ dists |> E.A.fmap(d => d |> fst |> Mixed.max) |> Functions.max;
let toShape = (dists: t, sampleCount: int) => {
let xs = Functions.range(min(dists), max(dists), sampleCount);
@@ -180,4 +180,10 @@ let toString = (r: bigDist) =>
| `Dist(d) => Mixed.toString(d)
| `PointwiseCombination(d) =>
PointwiseAddDistributionsWeighted.toString(d)
- );
\ No newline at end of file
+ );
+
+let toShape = n =>
+ fun
+ | `Dist(d) => Mixed.toShape(~xSelection=`ByWeight, d, n)
+ | `PointwiseCombination(d) =>
+ PointwiseAddDistributionsWeighted.toShape(d, n);
\ No newline at end of file
diff --git a/src/symbolic/MathJsParser.re b/src/symbolic/MathJsParser.re
index 7270ba5b..2081db79 100644
--- a/src/symbolic/MathJsParser.re
+++ b/src/symbolic/MathJsParser.re
@@ -1,14 +1,16 @@
open Jstat;
type arg =
+ | Symbol(string)
| Value(float)
| Fn(fn)
+ | Array(array(arg))
and fn = {
name: string,
args: array(arg),
};
-let rec parseMathjs = (j: Js.Json.t) => {
+let rec parseMathjs = (j: Js.Json.t) =>
Json.Decode.(
switch (field("mathjs", string, j)) {
| "FunctionNode" =>
@@ -19,19 +21,56 @@ let rec parseMathjs = (j: Js.Json.t) => {
args: args |> E.A.O.concatSomes,
}),
);
- | "ConstantNode" => Some(Value(field("value", float, j)))
- | _ => None
+ | "OperatorNode" =>
+ let args = j |> field("args", array(parseMathjs));
+ Some(
+ Fn({
+ name: j |> field("fn", string),
+ args: args |> E.A.O.concatSomes,
+ }),
+ );
+ | "ConstantNode" => Some(Value(field("value", Json.Decode.float, j)))
+ | "ArrayNode" =>
+ let items = field("items", array(parseMathjs), j);
+ Some(Array(items |> E.A.O.concatSomes));
+ | "SymbolNode" => Some(Symbol(field("name", string, j)))
+ | n =>
+ Js.log2("Couldn't parse mathjs node", j);
+ None;
}
);
-};
-let normal = (r): result(bigDist, string) =>
- r
- |> (
- fun
- | [|Value(mean), Value(stdev)|] => Ok(`Dist(`Normal({mean, stdev})))
- | _ => Error("Wrong number of variables in normal distribution")
- );
+// let logHigh = math.log(high);
+// let logLow = math.log(low);
+
+// let mean = (math.mean(logHigh, logLow)).toFixed(3);
+// let stdev = ((logHigh-logLow) / (2*1.645)).toFixed(3);
+
+let normal: array(arg) => result(bigDist, string) =
+ fun
+ | [|Value(mean), Value(stdev)|] => Ok(`Dist(`Normal({mean, stdev})))
+ | _ => Error("Wrong number of variables in normal distribution");
+
+let lognormal: array(arg) => result(bigDist, string) =
+ fun
+ | [|Value(mu), Value(sigma)|] => Ok(`Dist(`Lognormal({mu, sigma})))
+ | _ => Error("Wrong number of variables in lognormal distribution");
+
+let to_: array(arg) => result(bigDist, string) =
+ fun
+ | [|Value(low), Value(high)|] => {
+ let logLow = Js.Math.log(low);
+ let logHigh = Js.Math.log(high);
+ let mu = Functions.mean([|logLow, logHigh|]);
+ let sigma = (logHigh -. logLow) /. (2.0 *. 1.645);
+ Ok(`Dist(`Lognormal({mu, sigma})));
+ }
+ | _ => Error("Wrong number of variables in lognormal distribution");
+
+let uniform: array(arg) => result(bigDist, string) =
+ fun
+ | [|Value(low), Value(high)|] => Ok(`Dist(`Uniform({low, high})))
+ | _ => Error("Wrong number of variables in lognormal distribution");
let rec toValue = (r): result(bigDist, string) =>
r
@@ -39,14 +78,16 @@ let rec toValue = (r): result(bigDist, string) =>
fun
| Value(_) => Error("Top level can't be value")
| Fn({name: "normal", args}) => normal(args)
+ | Fn({name: "lognormal", args}) => lognormal(args)
+ | Fn({name: "uniform", args}) => uniform(args)
+ | Fn({name: "to", args}) => to_(args)
| Fn({name: "mm", args}) => {
let dists: array(dist) =
args
|> E.A.fmap(toValue)
|> E.A.fmap(
fun
- | Ok(`Dist(`Normal({mean, stdev}))) =>
- Some(`Normal({mean, stdev}))
+ | Ok(`Dist(n)) => Some(n)
| _ => None,
)
|> E.A.O.concatSomes;
@@ -55,4 +96,15 @@ let rec toValue = (r): result(bigDist, string) =>
Ok(`PointwiseCombination(inputs));
}
| Fn({name}) => Error(name ++ ": name not found")
- );
\ No newline at end of file
+ | Array(_) => Error("Array not valid as top level")
+ | Symbol(_) => Error("Symbol not valid as top level")
+ );
+
+let fromString = str =>
+ Mathjs.parseMath(str)
+ |> E.R.bind(_, r =>
+ switch (parseMathjs(r)) {
+ | Some(r) => toValue(r)
+ | None => Error("Second parse failed")
+ }
+ );
\ No newline at end of file
diff --git a/src/symbolic/Mathjs.re b/src/symbolic/Mathjs.re
index 87a7cb01..1b0c7bc7 100644
--- a/src/symbolic/Mathjs.re
+++ b/src/symbolic/Mathjs.re
@@ -1,2 +1,10 @@
[@bs.module "./MathjsWrapper.js"]
-external parseMath: string => Js.Json.t = "parseMath";
\ No newline at end of file
+external parseMathExt: string => Js.Json.t = "parseMath";
+
+let parseMath = (str: string): result(Js.Json.t, string) =>
+ switch (parseMathExt(str)) {
+ | exception (Js.Exn.Error(err)) =>
+ Error(Js.Exn.message(err) |> E.O.default("MathJS Parse Error"))
+ | exception _ => Error("MathJS Parse Error")
+ | j => Ok(j)
+ };
\ No newline at end of file