diff --git a/__tests__/Samples__test.re b/__tests__/Samples__test.re
index bc61cb99..9afbaf86 100644
--- a/__tests__/Samples__test.re
+++ b/__tests__/Samples__test.re
@@ -14,12 +14,12 @@ describe("Lodash", () => {
describe("Lodash", () => {
makeTest(
"split",
- Samples.T.splitContinuousAndDiscrete([|1.432, 1.33455, 2.0|]),
+ SamplesToShape.Internals.T.splitContinuousAndDiscrete([|1.432, 1.33455, 2.0|]),
([|1.432, 1.33455, 2.0|], E.FloatFloatMap.empty()),
);
makeTest(
"split",
- Samples.T.splitContinuousAndDiscrete([|
+ SamplesToShape.Internals.T.splitContinuousAndDiscrete([|
1.432,
1.33455,
2.0,
@@ -39,12 +39,12 @@ describe("Lodash", () => {
};
let (_, discrete) =
- Samples.T.splitContinuousAndDiscrete(makeDuplicatedArray(10));
+ SamplesToShape.Internals.T.splitContinuousAndDiscrete(makeDuplicatedArray(10));
let toArr = discrete |> E.FloatFloatMap.toArray;
makeTest("splitMedium", toArr |> Belt.Array.length, 10);
let (c, discrete) =
- Samples.T.splitContinuousAndDiscrete(makeDuplicatedArray(500));
+ SamplesToShape.Internals.T.splitContinuousAndDiscrete(makeDuplicatedArray(500));
let toArr = discrete |> E.FloatFloatMap.toArray;
makeTest("splitMedium", toArr |> Belt.Array.length, 500);
})
diff --git a/showcase/entries/Continuous2.re b/showcase/entries/Continuous2.re
index f6cb9a53..e46218ef 100644
--- a/showcase/entries/Continuous2.re
+++ b/showcase/entries/Continuous2.re
@@ -6,18 +6,18 @@
// uniform(0,1) > 0.3 ? lognormal(6.652, -0.41): 0
let timeDist ={
- let ingredients = RenderTypes.DistPlusRenderer.Ingredients.make(
+ 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 = RenderTypes.DistPlusRenderer.make(~distPlusIngredients=ingredients,())
+ let inputs = DistPlusRenderer.Inputs.make(~distPlusIngredients=ingredients,())
inputs |> DistPlusRenderer.run
}
let setup = dist =>
- RenderTypes.DistPlusRenderer.make(~distPlusIngredients=dist,())
+ DistPlusRenderer.Inputs.make(~distPlusIngredients=dist,())
|> DistPlusRenderer.run
|> E.R.fmap(distPlus => )
|> E.R.toOption
@@ -28,7 +28,7 @@ let simpleExample = (name, guesstimatorString) =>
{name |> ReasonReact.string}
- {setup(RenderTypes.DistPlusRenderer.Ingredients.make(~guesstimatorString, ()))}
+ {setup(DistPlusRenderer.Inputs.Ingredients.make(~guesstimatorString, ()))}
>;
let timeExample = (name, guesstimatorString) =>
@@ -37,7 +37,7 @@ let timeExample = (name, guesstimatorString) =>
{name |> ReasonReact.string}
{setup(
- RenderTypes.DistPlusRenderer.Ingredients.make(
+ DistPlusRenderer.Inputs.Ingredients.make(
~guesstimatorString,
~unit=TimeDistribution({zero: MomentRe.momentNow(), unit: `years}),
(),
diff --git a/showcase/entries/ExpressionTreeExamples.re b/showcase/entries/ExpressionTreeExamples.re
index 2ca91990..a93f61d1 100644
--- a/showcase/entries/ExpressionTreeExamples.re
+++ b/showcase/entries/ExpressionTreeExamples.re
@@ -1,5 +1,5 @@
let setup = dist =>
- RenderTypes.DistPlusRenderer.make(~distPlusIngredients=dist, ())
+ DistPlusRenderer.Inputs.make(~distPlusIngredients=dist, ())
|> DistPlusRenderer.run
|> E.R.fmap(distPlus => )
|> E.R.toOption
@@ -10,7 +10,7 @@ let simpleExample = (guesstimatorString, ~problem="", ()) =>
{guesstimatorString |> ReasonReact.string}
{problem |> (e => "problem: " ++ e) |> ReasonReact.string}
{setup(
- RenderTypes.DistPlusRenderer.Ingredients.make(~guesstimatorString, ()),
+ DistPlusRenderer.Inputs.Ingredients.make(~guesstimatorString, ()),
)}
>;
diff --git a/src/components/DistBuilder.re b/src/components/DistBuilder.re
index 2a28a5a6..04e197bb 100644
--- a/src/components/DistBuilder.re
+++ b/src/components/DistBuilder.re
@@ -33,6 +33,23 @@ module Form = ReForm.Make(FormConfig);
let schema = Form.Validation.Schema([||]);
+module FieldText = {
+ [@react.component]
+ let make = (~field, ~label) => {
+
+ R.ste}>
+ validate()}
+ />
+
+ }
+ />;
+ };
+};
module FieldString = {
[@react.component]
let make = (~field, ~label) => {
@@ -111,13 +128,6 @@ module Styles = {
]);
};
-type inputs = {
- samplingInputs: RenderTypes.ShapeRenderer.Sampling.inputs,
- guesstimatorString: string,
- length: int,
- shouldDownsampleSampledDistribution: int,
-};
-
module DemoDist = {
[@react.component]
let make = (~guesstimatorString, ~domain, ~unit, ~options) => {
@@ -127,30 +137,34 @@ module DemoDist = {
{switch (domain, unit, options) {
| (Some(domain), Some(unit), Some(options)) =>
let distPlusIngredients =
- RenderTypes.DistPlusRenderer.Ingredients.make(
+ DistPlusRenderer.Inputs.Ingredients.make(
~guesstimatorString,
~domain,
~unit,
(),
);
- let inputs =
- RenderTypes.DistPlusRenderer.make(
+ let inputs1 =
+ DistPlusRenderer.Inputs.make(
~samplingInputs={
sampleCount: Some(options.sampleCount),
outputXYPoints: Some(options.outputXYPoints),
kernelWidth: options.kernelWidth,
+ shapeLength: Some(options.downsampleTo |> E.O.default(1000))
},
~distPlusIngredients,
- ~shouldDownsample=options.downsampleTo |> E.O.isSome,
- ~recommendedLength=options.downsampleTo |> E.O.default(1000),
+ ~environment=
+ [|("p", `SymbolicDist(`Float(1.0)))|]
+ ->Belt.Map.String.fromArray,
(),
);
- let response = DistPlusRenderer.run(inputs);
- switch (response) {
- | Ok(distPlus) =>
- let normalizedDistPlus = DistPlus.T.normalize(distPlus);
- ;
- | Error(r) => r |> R.ste
+
+ let response1 = DistPlusRenderer.run(inputs1);
+ switch (response1) {
+ | (Ok(distPlus1)) =>
+ <>
+
+ >
+ | (Error(r)) => r |> R.ste
};
| _ =>
"Nothing to show. Try to change the distribution description."
@@ -314,7 +328,7 @@ let make = () => {
-
diff --git a/src/components/Drawer.re b/src/components/Drawer.re
index c8777b54..7b9be5e7 100644
--- a/src/components/Drawer.re
+++ b/src/components/Drawer.re
@@ -392,8 +392,8 @@ module Draw = {
let normal: SymbolicTypes.symbolicDist = `Normal({mean, stdev});
let normalShape =
ExpressionTree.toShape(
- numSamples,
- {sampleCount: 10000, outputXYPoints: 10000, kernelWidth: None},
+ {sampleCount: 10000, outputXYPoints: 10000, kernelWidth: None, shapeLength:numSamples},
+ ExpressionTypes.ExpressionTree.Environment.empty,
`SymbolicDist(normal),
) |> E.R.toExn;
let xyShape: Types.xyShape =
diff --git a/src/components/charts/DistPlusPlotReducer.re b/src/components/charts/DistPlusPlotReducer.re
index 7eaa48f1..0c4ec0ad 100644
--- a/src/components/charts/DistPlusPlotReducer.re
+++ b/src/components/charts/DistPlusPlotReducer.re
@@ -106,7 +106,6 @@ let init = {
showParams: false,
showPercentiles: true,
distributions: [
- {yLog: false, xLog: false, isCumulative: false, height: 4},
- {yLog: false, xLog: false, isCumulative: true, height: 1},
+ {yLog: false, xLog: false, isCumulative: false, height: 1},
],
};
\ No newline at end of file
diff --git a/src/distPlus/distribution/AlgebraicShapeCombination.re b/src/distPlus/distribution/AlgebraicShapeCombination.re
index fd6f7453..631fb3a5 100644
--- a/src/distPlus/distribution/AlgebraicShapeCombination.re
+++ b/src/distPlus/distribution/AlgebraicShapeCombination.re
@@ -27,15 +27,15 @@ let toDiscretePointMassesFromTriangulars =
let xsProdN1: array(float) = Belt.Array.makeUninitializedUnsafe(n - 1);
let xsProdN2: array(float) = Belt.Array.makeUninitializedUnsafe(n - 2);
for (i in 0 to n - 1) {
- Belt.Array.set(xsSq, i, xs[i] *. xs[i]) |> ignore;
+ Belt.Array.set(xsSq, i, xs[i] *. xs[i]) |> ignore;
();
};
for (i in 0 to n - 2) {
- Belt.Array.set(xsProdN1, i, xs[i] *. xs[i + 1]) |> ignore;
+ Belt.Array.set(xsProdN1, i, xs[i] *. xs[i + 1]) |> ignore;
();
};
for (i in 0 to n - 3) {
- Belt.Array.set(xsProdN2, i, xs[i] *. xs[i + 2]) |> ignore;
+ Belt.Array.set(xsProdN2, i, xs[i] *. xs[i + 2]) |> ignore;
();
};
// means and variances
@@ -45,11 +45,8 @@ let toDiscretePointMassesFromTriangulars =
if (inverse) {
for (i in 1 to n - 2) {
- Belt.Array.set(
- masses,
- i - 1,
- (xs[i + 1] -. xs[i - 1]) *. ys[i] /. 2.,
- ) |> ignore;
+ Belt.Array.set(masses, i - 1, (xs[i + 1] -. xs[i - 1]) *. ys[i] /. 2.)
+ |> ignore;
// this only works when the whole triange is either on the left or on the right of zero
let a = xs[i - 1];
@@ -70,9 +67,9 @@ let toDiscretePointMassesFromTriangulars =
-. inverseMean
** 2.;
- Belt.Array.set(means, i - 1, inverseMean) |> ignore;
+ Belt.Array.set(means, i - 1, inverseMean) |> ignore;
- Belt.Array.set(variances, i - 1, inverseVar) |> ignore;
+ Belt.Array.set(variances, i - 1, inverseVar) |> ignore;
();
};
@@ -80,14 +77,12 @@ let toDiscretePointMassesFromTriangulars =
} else {
for (i in 1 to n - 2) {
// area of triangle = width * height / 2
- Belt.Array.set(
- masses,
- i - 1,
- (xs[i + 1] -. xs[i - 1]) *. ys[i] /. 2.,
- ) |> ignore;
+ Belt.Array.set(masses, i - 1, (xs[i + 1] -. xs[i - 1]) *. ys[i] /. 2.)
+ |> ignore;
// means of triangle = (a + b + c) / 3
- Belt.Array.set(means, i - 1, (xs[i - 1] +. xs[i] +. xs[i + 1]) /. 3.) |> ignore;
+ Belt.Array.set(means, i - 1, (xs[i - 1] +. xs[i] +. xs[i + 1]) /. 3.)
+ |> ignore;
// variance of triangle = (a^2 + b^2 + c^2 - ab - ac - bc) / 18
Belt.Array.set(
@@ -102,7 +97,8 @@ let toDiscretePointMassesFromTriangulars =
-. xsProdN2[i - 1]
)
/. 18.,
- ) |> ignore;
+ )
+ |> ignore;
();
};
{n: n - 2, masses, means, variances};
@@ -134,8 +130,10 @@ let combineShapesContinuousContinuous =
| `Subtract => ((m1, m2) => m1 -. m2)
| `Multiply => ((m1, m2) => m1 *. m2)
| `Divide => ((m1, mInv2) => m1 *. mInv2)
+ | `Exponentiate => ((m1, mInv2) => m1 ** mInv2)
}; // note: here, mInv2 = mean(1 / t2) ~= 1 / mean(t2)
+ // TODO: I don't know what the variances are for exponentatiation
// converts the variances and means of the two inputs into the variance of the output
let combineVariancesFn =
switch (op) {
@@ -144,6 +142,8 @@ let combineShapesContinuousContinuous =
| `Multiply => (
(v1, v2, m1, m2) => v1 *. v2 +. v1 *. m2 ** 2. +. v2 *. m1 ** 2.
)
+ | `Exponentiate =>
+ ((v1, v2, m1, m2) => v1 *. v2 +. v1 *. m2 ** 2. +. v2 *. m1 ** 2.);
| `Divide => (
(v1, vInv2, m1, mInv2) =>
v1 *. vInv2 +. v1 *. mInv2 ** 2. +. vInv2 *. m1 ** 2.
@@ -225,9 +225,12 @@ let toDiscretePointMassesFromDiscrete =
};
let combineShapesContinuousDiscrete =
- (op: ExpressionTypes.algebraicOperation, continuousShape: DistTypes.xyShape, discreteShape: DistTypes.xyShape)
+ (
+ op: ExpressionTypes.algebraicOperation,
+ continuousShape: DistTypes.xyShape,
+ discreteShape: DistTypes.xyShape,
+ )
: DistTypes.xyShape => {
-
let t1n = continuousShape |> XYShape.T.length;
let t2n = discreteShape |> XYShape.T.length;
@@ -248,15 +251,19 @@ let combineShapesContinuousDiscrete =
Belt.Array.set(
dxyShape,
i,
- (fn(continuousShape.xs[i], discreteShape.xs[j]),
- continuousShape.ys[i] *. discreteShape.ys[j]),
- ) |> ignore;
+ (
+ fn(continuousShape.xs[i], discreteShape.xs[j]),
+ continuousShape.ys[i] *. discreteShape.ys[j],
+ ),
+ )
+ |> ignore;
();
};
Belt.Array.set(outXYShapes, j, dxyShape) |> ignore;
();
}
| `Multiply
+ | `Exponentiate
| `Divide =>
for (j in 0 to t2n - 1) {
// creates a new continuous shape for each one of the discrete points, and collects them in outXYShapes.
@@ -266,8 +273,12 @@ let combineShapesContinuousDiscrete =
Belt.Array.set(
dxyShape,
i,
- (fn(continuousShape.xs[i], discreteShape.xs[j]), continuousShape.ys[i] *. discreteShape.ys[j] /. discreteShape.xs[j]),
- ) |> ignore;
+ (
+ fn(continuousShape.xs[i], discreteShape.xs[j]),
+ {continuousShape.ys[i] *. discreteShape.ys[j] /. discreteShape.xs[j]}
+ ),
+ )
+ |> ignore;
();
};
Belt.Array.set(outXYShapes, j, dxyShape) |> ignore;
@@ -278,7 +289,10 @@ let combineShapesContinuousDiscrete =
outXYShapes
|> E.A.fmap(XYShape.T.fromZippedArray)
|> E.A.fold_left(
- XYShape.PointwiseCombination.combine((+.),
- XYShape.XtoY.continuousInterpolator(`Linear, `UseZero)),
- XYShape.T.empty);
+ XYShape.PointwiseCombination.combine(
+ (+.),
+ XYShape.XtoY.continuousInterpolator(`Linear, `UseZero),
+ ),
+ XYShape.T.empty,
+ );
};
diff --git a/src/distPlus/distribution/Continuous.re b/src/distPlus/distribution/Continuous.re
index 7c5451e7..276cec88 100644
--- a/src/distPlus/distribution/Continuous.re
+++ b/src/distPlus/distribution/Continuous.re
@@ -37,6 +37,7 @@ let empty: DistTypes.continuousShape = {
let stepwiseToLinear = (t: t): t =>
make(~integralSumCache=t.integralSumCache, ~integralCache=t.integralCache, XYShape.Range.stepwiseToLinear(t.xyShape));
+// Note: This results in a distribution with as many points as the sum of those in t1 and t2.
let combinePointwise =
(
~integralSumCachesFn=(_, _) => None,
diff --git a/src/distPlus/distribution/Shape.re b/src/distPlus/distribution/Shape.re
index 01685c3c..fa0be2b8 100644
--- a/src/distPlus/distribution/Shape.re
+++ b/src/distPlus/distribution/Shape.re
@@ -216,6 +216,11 @@ let sample = (t: t): float => {
bar;
};
+let isFloat = (t:t) => switch(t){
+| Discrete({xyShape: {xs: [|_|], ys: [|1.0|]}}) => true
+| _ => false
+}
+
let sampleNRendered = (n, dist) => {
let integralCache = T.Integral.get(dist);
let distWithUpdatedIntegralCache = T.updateIntegralCache(Some(integralCache), dist);
diff --git a/src/distPlus/expressionTree/ExpressionTree.re b/src/distPlus/expressionTree/ExpressionTree.re
index 66ccf3a6..797aa14e 100644
--- a/src/distPlus/expressionTree/ExpressionTree.re
+++ b/src/distPlus/expressionTree/ExpressionTree.re
@@ -1,13 +1,17 @@
open ExpressionTypes.ExpressionTree;
-let toShape = (intendedShapeLength: int, samplingInputs, node: node) => {
+let toLeaf = (samplingInputs, environment, node: node) => {
+ node
+ |> ExpressionTreeEvaluator.toLeaf({
+ samplingInputs,
+ environment,
+ evaluateNode: ExpressionTreeEvaluator.toLeaf,
+ });
+};
+
+let toShape = (samplingInputs, environment, node: node) => {
let renderResult =
- `Render(`Normalize(node))
- |> ExpressionTreeEvaluator.toLeaf({
- samplingInputs,
- intendedShapeLength,
- evaluateNode: ExpressionTreeEvaluator.toLeaf,
- });
+ `Render(`Normalize(node)) |> toLeaf(samplingInputs, environment);
switch (renderResult) {
| Ok(`RenderedDist(shape)) => Ok(shape)
diff --git a/src/distPlus/expressionTree/ExpressionTreeEvaluator.re b/src/distPlus/expressionTree/ExpressionTreeEvaluator.re
index 2ad84adc..27570b59 100644
--- a/src/distPlus/expressionTree/ExpressionTreeEvaluator.re
+++ b/src/distPlus/expressionTree/ExpressionTreeEvaluator.re
@@ -151,7 +151,7 @@ module PointwiseCombination = {
};
};
- let pointwiseMultiply = (evaluationParams: evaluationParams, t1: t, t2: t) => {
+ let pointwiseCombine = (fn, evaluationParams: evaluationParams, t1: t, t2: t) => {
// TODO: construct a function that we can easily sample from, to construct
// a RenderedDist. Use the xMin and xMax of the rendered shapes to tell the sampling function where to look.
// TODO: This should work for symbolic distributions too!
@@ -176,7 +176,8 @@ module PointwiseCombination = {
) => {
switch (pointwiseOp) {
| `Add => pointwiseAdd(evaluationParams, t1, t2)
- | `Multiply => pointwiseMultiply(evaluationParams, t1, t2)
+ | `Multiply => pointwiseCombine(( *. ),evaluationParams, t1, t2)
+ | `Exponentiate => pointwiseCombine(( *. ),evaluationParams, t1, t2)
};
};
};
@@ -259,14 +260,27 @@ module FloatFromDist = {
};
};
+// TODO: This forces things to be floats
+let callableFunction = (evaluationParams, name, args) => {
+ let b =
+ args
+ |> E.A.fmap(a =>
+ Render.render(evaluationParams, a)
+ |> E.R.bind(_, Render.toFloat)
+ )
+ |> E.A.R.firstErrorOrOpen;
+ b |> E.R.bind(_, Functions.fnn(evaluationParams, name));
+};
+
module Render = {
let rec operationToLeaf =
(evaluationParams: evaluationParams, t: node): result(t, string) => {
switch (t) {
+ | `Function(_) => Error("Cannot render a function")
| `SymbolicDist(d) =>
Ok(
`RenderedDist(
- SymbolicDist.T.toShape(evaluationParams.intendedShapeLength, d),
+ SymbolicDist.T.toShape(evaluationParams.samplingInputs.shapeLength, d),
),
)
| `RenderedDist(_) as t => Ok(t) // already a rendered shape, we're done here
@@ -275,6 +289,13 @@ module Render = {
};
};
+let run = (node, fnNode) => {
+ switch (fnNode) {
+ | `Function(r) => Ok(r(node))
+ | _ => Error("Not a function")
+ };
+};
+
/* This function recursively goes through the nodes of the parse tree,
replacing each Operation node and its subtree with a Data node.
Whenever possible, the replacement produces a new Symbolic Data node,
@@ -314,5 +335,9 @@ let toLeaf =
FloatFromDist.operationToLeaf(evaluationParams, distToFloatOp, t)
| `Normalize(t) => Normalize.operationToLeaf(evaluationParams, t)
| `Render(t) => Render.operationToLeaf(evaluationParams, t)
+ | `Function(t) => Ok(`Function(t))
+ | `Symbol(r) => ExpressionTypes.ExpressionTree.Environment.get(evaluationParams.environment, r) |> E.O.toResult("Undeclared variable " ++ r)
+ | `FunctionCall(name, args) =>
+ callableFunction(evaluationParams, name, args)
};
};
diff --git a/src/distPlus/expressionTree/ExpressionTypes.re b/src/distPlus/expressionTree/ExpressionTypes.re
index a76aee74..6453667a 100644
--- a/src/distPlus/expressionTree/ExpressionTypes.re
+++ b/src/distPlus/expressionTree/ExpressionTypes.re
@@ -1,5 +1,5 @@
-type algebraicOperation = [ | `Add | `Multiply | `Subtract | `Divide];
-type pointwiseOperation = [ | `Add | `Multiply];
+type algebraicOperation = [ | `Add | `Multiply | `Subtract | `Divide | `Exponentiate];
+type pointwiseOperation = [ | `Add | `Multiply | `Exponentiate];
type scaleOperation = [ | `Multiply | `Exponentiate | `Log];
type distToFloatOperation = [
| `Pdf(float)
@@ -20,17 +20,37 @@ module ExpressionTree = {
| `Truncate(option(float), option(float), node)
| `Normalize(node)
| `FloatFromDist(distToFloatOperation, node)
+ | `Function(array(string), node)
+ | `FunctionCall(string, array(node))
+ | `Symbol(string)
];
type samplingInputs = {
sampleCount: int,
outputXYPoints: int,
kernelWidth: option(float),
+ shapeLength: int
};
+ type environment = Belt.Map.String.t(node);
+
+ module Environment = {
+ type t = environment
+ module MS = Belt.Map.String;
+ let fromArray = MS.fromArray
+ let empty:t = [||]->fromArray;
+ let mergeKeepSecond = (a:t,b:t) => MS.merge(a,b, (_,a,b) =>switch(a,b){
+ | (_, Some(b)) => Some(b)
+ | (Some(a), _) => Some(a)
+ | _ => None
+ })
+ let update = (t,str, fn) => MS.update(t, str, fn)
+ let get = (t:t,str) => MS.get(t, str)
+ }
+
type evaluationParams = {
samplingInputs,
- intendedShapeLength: int,
+ environment,
evaluateNode: (evaluationParams, node) => Belt.Result.t(node, string),
};
@@ -71,8 +91,20 @@ module ExpressionTree = {
| `RenderedDist(r) => Some(r)
| _ => None
};
- };
+ let _toFloat = (t: DistTypes.shape) =>
+ switch (t) {
+ | Discrete({xyShape: {xs: [|x|], ys: [|1.0|]}}) =>
+ Some(`SymbolicDist(`Float(x)))
+ | _ => None
+ };
+
+ let toFloat = (item: node): result(node, string) =>
+ item
+ |> getShape
+ |> E.O.bind(_, _toFloat)
+ |> E.O.toResult("Not valid shape");
+ };
};
type simplificationResult = [
@@ -80,3 +112,8 @@ type simplificationResult = [
| `Error(string)
| `NoSolution
];
+
+module Program = {
+ type statement = [ | `Assignment(string, ExpressionTree.node) | `Expression(ExpressionTree.node)];
+ type program = array(statement);
+}
\ No newline at end of file
diff --git a/src/distPlus/expressionTree/Functions.re b/src/distPlus/expressionTree/Functions.re
new file mode 100644
index 00000000..f32c570c
--- /dev/null
+++ b/src/distPlus/expressionTree/Functions.re
@@ -0,0 +1,122 @@
+type node = ExpressionTypes.ExpressionTree.node;
+
+let toOkSym = r => Ok(`SymbolicDist(r));
+
+let twoFloats = (fn, n1: node, n2: node): result(node, string) =>
+ switch (n1, n2) {
+ | (`SymbolicDist(`Float(a)), `SymbolicDist(`Float(b))) => fn(a, b)
+ | _ => Error("Variables have wrong type")
+ };
+
+let threeFloats = (fn, n1: node, n2: node, n3: node): result(node, string) =>
+ switch (n1, n2, n3) {
+ | (
+ `SymbolicDist(`Float(a)),
+ `SymbolicDist(`Float(b)),
+ `SymbolicDist(`Float(c)),
+ ) =>
+ fn(a, b, c)
+ | _ => Error("Variables have wrong type")
+ };
+
+let twoFloatsToOkSym = fn => twoFloats((f1, f2) => fn(f1, f2) |> toOkSym);
+
+let threeFloats = fn => threeFloats((f1, f2, f3) => fn(f1, f2, f3));
+
+let apply2 = (fn, args): result(node, string) =>
+ switch (args) {
+ | [|a, b|] => fn(a, b)
+ | _ => Error("Needs 2 args")
+ };
+
+let apply3 = (fn, args: array(node)): result(node, string) =>
+ switch (args) {
+ | [|a, b, c|] => fn(a, b, c)
+ | _ => Error("Needs 3 args")
+ };
+
+let to_: array(node) => result(node, string) =
+ fun
+ | [|`SymbolicDist(`Float(low)), `SymbolicDist(`Float(high))|]
+ when low <= 0.0 && low < high => {
+ Ok(`SymbolicDist(SymbolicDist.Normal.from90PercentCI(low, high)));
+ }
+ | [|`SymbolicDist(`Float(low)), `SymbolicDist(`Float(high))|]
+ when low < high => {
+ Ok(`SymbolicDist(SymbolicDist.Lognormal.from90PercentCI(low, high)));
+ }
+ | [|`SymbolicDist(`Float(_)), `SymbolicDist(_)|] =>
+ Error("Low value must be less than high value.")
+ | _ => Error("Requires 2 variables");
+
+let processCustomFn =
+ (
+ evaluationParams: ExpressionTypes.ExpressionTree.evaluationParams,
+ args: array(node),
+ argNames: array(string),
+ fnResult: node,
+ ) =>
+ if (E.A.length(args) == E.A.length(argNames)) {
+ let newEnvironment =
+ Belt.Array.zip(argNames, args)
+ |> ExpressionTypes.ExpressionTree.Environment.fromArray;
+ let newEvaluationParams: ExpressionTypes.ExpressionTree.evaluationParams = {
+ samplingInputs: evaluationParams.samplingInputs,
+ environment:
+ ExpressionTypes.ExpressionTree.Environment.mergeKeepSecond(
+ evaluationParams.environment,
+ newEnvironment,
+ ),
+ evaluateNode: evaluationParams.evaluateNode,
+ };
+ evaluationParams.evaluateNode(newEvaluationParams, fnResult);
+ } else {
+ Error("Failure");
+ };
+
+let fnn =
+ (
+ evaluationParams: ExpressionTypes.ExpressionTree.evaluationParams,
+ name,
+ args: array(node),
+ ) =>
+ switch (
+ name,
+ ExpressionTypes.ExpressionTree.Environment.get(
+ evaluationParams.environment,
+ name,
+ ),
+ ) {
+ | (_, Some(`Function(argNames, tt))) =>
+ processCustomFn(evaluationParams, args, argNames, tt)
+ | ("normal", _) =>
+ apply2(twoFloatsToOkSym(SymbolicDist.Normal.make), args)
+ | ("uniform", _) =>
+ apply2(twoFloatsToOkSym(SymbolicDist.Uniform.make), args)
+ | ("beta", _) => apply2(twoFloatsToOkSym(SymbolicDist.Beta.make), args)
+ | ("cauchy", _) =>
+ apply2(twoFloatsToOkSym(SymbolicDist.Cauchy.make), args)
+ | ("lognormal", _) =>
+ apply2(twoFloatsToOkSym(SymbolicDist.Lognormal.make), args)
+ | ("lognormalFromMeanAndStdDev", _) =>
+ apply2(twoFloatsToOkSym(SymbolicDist.Lognormal.fromMeanAndStdev), args)
+ | ("exponential", _) =>
+ switch (args) {
+ | [|`SymbolicDist(`Float(a))|] =>
+ Ok(`SymbolicDist(SymbolicDist.Exponential.make(a)))
+ | _ => Error("Needs 3 valid arguments")
+ }
+ | ("triangular", _) =>
+ switch (args) {
+ | [|
+ `SymbolicDist(`Float(a)),
+ `SymbolicDist(`Float(b)),
+ `SymbolicDist(`Float(c)),
+ |] =>
+ SymbolicDist.Triangular.make(a, b, c)
+ |> E.R.fmap(r => `SymbolicDist(r))
+ | _ => Error("Needs 3 valid arguments")
+ }
+ | ("to", _) => to_(args)
+ | _ => Error("Function not found")
+ };
diff --git a/src/distPlus/expressionTree/MathJsParser.re b/src/distPlus/expressionTree/MathJsParser.re
index 1704c2de..52fb90b2 100644
--- a/src/distPlus/expressionTree/MathJsParser.re
+++ b/src/distPlus/expressionTree/MathJsParser.re
@@ -4,10 +4,18 @@ module MathJsonToMathJsAdt = {
| Value(float)
| Fn(fn)
| Array(array(arg))
+ | Blocks(array(arg))
| Object(Js.Dict.t(arg))
+ | Assignment(arg, arg)
+ | FunctionAssignment(fnAssignment)
and fn = {
name: string,
args: array(arg),
+ }
+ and fnAssignment = {
+ name: string,
+ args: array(string),
+ expression: arg,
};
let rec run = (j: Js.Json.t) =>
@@ -40,6 +48,25 @@ module MathJsonToMathJsAdt = {
let items = field("items", array(run), j);
Some(Array(items |> E.A.O.concatSomes));
| "SymbolNode" => Some(Symbol(field("name", string, j)))
+ | "AssignmentNode" =>
+ let object_ = j |> field("object", run);
+ let value_ = j |> field("value", run);
+ switch (object_, value_) {
+ | (Some(o), Some(v)) => Some(Assignment(o, v))
+ | _ => None
+ };
+ | "BlockNode" =>
+ let block = r => r |> field("node", run);
+ let args = j |> field("blocks", array(block)) |> E.A.O.concatSomes;
+ Some(Blocks(args));
+ | "FunctionAssignmentNode" =>
+ let name = j |> field("name", string);
+ let args = j |> field("params", array(field("name", string)));
+ let expression = j |> field("expr", run);
+ expression
+ |> E.O.fmap(expression =>
+ FunctionAssignment({name, args, expression})
+ );
| n =>
Js.log3("Couldn't parse mathjs node", j, n);
None;
@@ -50,29 +77,37 @@ module MathJsonToMathJsAdt = {
module MathAdtToDistDst = {
open MathJsonToMathJsAdt;
+ let handleSymbol = sym => {
+ Ok(`Symbol(sym));
+ };
+
module MathAdtCleaner = {
let transformWithSymbol = (f: float, s: string) =>
switch (s) {
| "K"
- | "k" => f *. 1000.
+ | "k" => Some(f *. 1000.)
| "M"
- | "m" => f *. 1000000.
+ | "m" => Some(f *. 1000000.)
| "B"
- | "b" => f *. 1000000000.
+ | "b" => Some(f *. 1000000000.)
| "T"
- | "t" => f *. 1000000000000.
- | _ => f
+ | "t" => Some(f *. 1000000000000.)
+ | _ => None
};
-
let rec run =
fun
- | Fn({name: "multiply", args: [|Value(f), Symbol(s)|]}) =>
- Value(transformWithSymbol(f, s))
+ | Fn({name: "multiply", args: [|Value(f), Symbol(s)|]}) as doNothing =>
+ transformWithSymbol(f, s)
+ |> E.O.fmap(r => Value(r))
+ |> E.O.default(doNothing)
| Fn({name: "unaryMinus", args: [|Value(f)|]}) => Value((-1.0) *. f)
| Fn({name, args}) => Fn({name, args: args |> E.A.fmap(run)})
| Array(args) => Array(args |> E.A.fmap(run))
| Symbol(s) => Symbol(s)
| Value(v) => Value(v)
+ | Blocks(args) => Blocks(args |> E.A.fmap(run))
+ | Assignment(a, b) => Assignment(a, run(b))
+ | FunctionAssignment(a) => FunctionAssignment(a)
| Object(v) =>
Object(
v
@@ -82,83 +117,31 @@ module MathAdtToDistDst = {
);
};
- let normal:
- array(arg) => result(ExpressionTypes.ExpressionTree.node, string) =
- fun
- | [|Value(mean), Value(stdev)|] =>
- Ok(`SymbolicDist(`Normal({mean, stdev})))
- | _ => Error("Wrong number of variables in normal distribution");
-
- let lognormal:
- array(arg) => result(ExpressionTypes.ExpressionTree.node, string) =
- fun
- | [|Value(mu), Value(sigma)|] =>
- Ok(`SymbolicDist(`Lognormal({mu, sigma})))
- | [|Object(o)|] => {
- let g = Js.Dict.get(o);
- switch (g("mean"), g("stdev"), g("mu"), g("sigma")) {
- | (Some(Value(mean)), Some(Value(stdev)), _, _) =>
- Ok(
- `SymbolicDist(
- SymbolicDist.Lognormal.fromMeanAndStdev(mean, stdev),
- ),
- )
- | (_, _, Some(Value(mu)), Some(Value(sigma))) =>
- Ok(`SymbolicDist(`Lognormal({mu, sigma})))
- | _ => Error("Lognormal distribution would need mean and stdev")
- };
- }
- | _ => Error("Wrong number of variables in lognormal distribution");
-
- let to_: array(arg) => result(ExpressionTypes.ExpressionTree.node, string) =
- fun
- | [|Value(low), Value(high)|] when low <= 0.0 && low < high => {
- Ok(`SymbolicDist(SymbolicDist.Normal.from90PercentCI(low, high)));
- }
- | [|Value(low), Value(high)|] when low < high => {
+ let lognormal = (args, parseArgs, nodeParser) =>
+ switch (args) {
+ | [|Object(o)|] =>
+ let g = s =>
+ Js.Dict.get(o, s)
+ |> E.O.toResult("Variable was empty")
+ |> E.R.bind(_, nodeParser);
+ switch (g("mean"), g("stdev"), g("mu"), g("sigma")) {
+ | (Ok(mean), Ok(stdev), _, _) =>
Ok(
- `SymbolicDist(SymbolicDist.Lognormal.from90PercentCI(low, high)),
- );
- }
- | [|Value(_), Value(_)|] =>
- Error("Low value must be less than high value.")
- | _ => Error("Wrong number of variables in lognormal distribution");
-
- let uniform:
- array(arg) => result(ExpressionTypes.ExpressionTree.node, string) =
- fun
- | [|Value(low), Value(high)|] =>
- Ok(`SymbolicDist(`Uniform({low, high})))
- | _ => Error("Wrong number of variables in lognormal distribution");
-
- let beta: array(arg) => result(ExpressionTypes.ExpressionTree.node, string) =
- fun
- | [|Value(alpha), Value(beta)|] =>
- Ok(`SymbolicDist(`Beta({alpha, beta})))
- | _ => Error("Wrong number of variables in lognormal distribution");
-
- let exponential:
- array(arg) => result(ExpressionTypes.ExpressionTree.node, string) =
- fun
- | [|Value(rate)|] => Ok(`SymbolicDist(`Exponential({rate: rate})))
- | _ => Error("Wrong number of variables in Exponential distribution");
-
- let cauchy:
- array(arg) => result(ExpressionTypes.ExpressionTree.node, string) =
- fun
- | [|Value(local), Value(scale)|] =>
- Ok(`SymbolicDist(`Cauchy({local, scale})))
- | _ => Error("Wrong number of variables in cauchy distribution");
-
- let triangular:
- array(arg) => result(ExpressionTypes.ExpressionTree.node, string) =
- fun
- | [|Value(low), Value(medium), Value(high)|]
- when low < medium && medium < high =>
- Ok(`SymbolicDist(`Triangular({low, medium, high})))
- | [|Value(_), Value(_), Value(_)|] =>
- Error("Triangular values must be increasing order")
- | _ => Error("Wrong number of variables in triangle distribution");
+ `FunctionCall(("lognormalFromMeanAndStdDev", [|mean, stdev|])),
+ )
+ | (_, _, Ok(mu), Ok(sigma)) =>
+ Ok(`FunctionCall(("lognormal", [|mu, sigma|])))
+ | _ =>
+ Error(
+ "Lognormal distribution needs either mean and stdev or mu and sigma",
+ )
+ };
+ | _ =>
+ parseArgs()
+ |> E.R.fmap((args: array(ExpressionTypes.ExpressionTree.node)) =>
+ `FunctionCall(("lognormal", args))
+ )
+ };
let multiModal =
(
@@ -166,15 +149,6 @@ module MathAdtToDistDst = {
weights: option(array(float)),
) => {
let weights = weights |> E.O.default([||]);
-
- /*let dists: =
- args
- |> E.A.fmap(
- fun
- | Ok(a) => a
- | Error(e) => Error(e)
- );*/
-
let firstWithError = args |> Belt.Array.getBy(_, Belt.Result.isError);
let withoutErrors = args |> E.A.fmap(E.R.toOption) |> E.A.O.concatSomes;
@@ -203,101 +177,75 @@ module MathAdtToDistDst = {
};
};
- // let arrayParser =
- // (args: array(arg))
- // : result(ExpressionTypes.ExpressionTree.node, string) => {
- // let samples =
- // args
- // |> E.A.fmap(
- // fun
- // | Value(n) => Some(n)
- // | _ => None,
- // )
- // |> E.A.O.concatSomes;
- // let outputs = Samples.T.fromSamples(samples);
- // let pdf =
- // outputs.shape |> E.O.bind(_, Shape.T.toContinuous);
- // let shape =
- // pdf
- // |> E.O.fmap(pdf => {
- // let _pdf = Continuous.T.normalize(pdf);
- // let cdf = Continuous.T.integral(~cache=None, _pdf);
- // SymbolicDist.ContinuousShape.make(_pdf, cdf);
- // });
- // switch (shape) {
- // | Some(s) => Ok(`SymbolicDist(`ContinuousShape(s)))
- // | None => Error("Rendering did not work")
- // };
- // };
-
+ // Error("Dotwise exponentiation needs two operands")
let operationParser =
(
name: string,
- args: array(result(ExpressionTypes.ExpressionTree.node, string)),
- ) => {
+ args: result(array(ExpressionTypes.ExpressionTree.node), string),
+ ):result(ExpressionTypes.ExpressionTree.node,string) => {
let toOkAlgebraic = r => Ok(`AlgebraicCombination(r));
let toOkPointwise = r => Ok(`PointwiseCombination(r));
let toOkTruncate = r => Ok(`Truncate(r));
let toOkFloatFromDist = r => Ok(`FloatFromDist(r));
- switch (name, args) {
- | ("add", [|Ok(l), Ok(r)|]) => toOkAlgebraic((`Add, l, r))
- | ("add", _) => Error("Addition needs two operands")
- | ("subtract", [|Ok(l), Ok(r)|]) => toOkAlgebraic((`Subtract, l, r))
- | ("subtract", _) => Error("Subtraction needs two operands")
- | ("multiply", [|Ok(l), Ok(r)|]) => toOkAlgebraic((`Multiply, l, r))
- | ("multiply", _) => Error("Multiplication needs two operands")
- | ("dotMultiply", [|Ok(l), Ok(r)|]) => toOkPointwise((`Multiply, l, r))
- | ("dotMultiply", _) =>
- Error("Dotwise multiplication needs two operands")
- | ("rightLogShift", [|Ok(l), Ok(r)|]) => toOkPointwise((`Add, l, r))
- | ("rightLogShift", _) => Error("Dotwise addition needs two operands")
- | ("divide", [|Ok(l), Ok(r)|]) => toOkAlgebraic((`Divide, l, r))
- | ("divide", _) => Error("Division needs two operands")
- | ("pow", _) => Error("Exponentiation is not yet supported.")
- | ("leftTruncate", [|Ok(d), Ok(`SymbolicDist(`Float(lc)))|]) =>
- toOkTruncate((Some(lc), None, d))
- | ("leftTruncate", _) =>
- Error("leftTruncate needs two arguments: the expression and the cutoff")
- | ("rightTruncate", [|Ok(d), Ok(`SymbolicDist(`Float(rc)))|]) =>
- toOkTruncate((None, Some(rc), d))
- | ("rightTruncate", _) =>
- Error(
- "rightTruncate needs two arguments: the expression and the cutoff",
- )
- | (
- "truncate",
- [|
- Ok(d),
- Ok(`SymbolicDist(`Float(lc))),
- Ok(`SymbolicDist(`Float(rc))),
- |],
- ) =>
- toOkTruncate((Some(lc), Some(rc), d))
- | ("truncate", _) =>
- Error("truncate needs three arguments: the expression and both cutoffs")
- | ("pdf", [|Ok(d), Ok(`SymbolicDist(`Float(v)))|]) =>
- toOkFloatFromDist((`Pdf(v), d))
- | ("cdf", [|Ok(d), Ok(`SymbolicDist(`Float(v)))|]) =>
- toOkFloatFromDist((`Cdf(v), d))
- | ("inv", [|Ok(d), Ok(`SymbolicDist(`Float(v)))|]) =>
- toOkFloatFromDist((`Inv(v), d))
- | ("mean", [|Ok(d)|]) => toOkFloatFromDist((`Mean, d))
- | ("sample", [|Ok(d)|]) => toOkFloatFromDist((`Sample, d))
- | _ => Error("This type not currently supported")
- };
+ args
+ |> E.R.bind(_, args => {
+ switch (name, args) {
+ | ("add", [|l, r|]) => toOkAlgebraic((`Add, l, r))
+ | ("add", _) => Error("Addition needs two operands")
+ | ("subtract", [|l, r|]) => toOkAlgebraic((`Subtract, l, r))
+ | ("subtract", _) => Error("Subtraction needs two operands")
+ | ("multiply", [|l, r|]) => toOkAlgebraic((`Multiply, l, r))
+ | ("multiply", _) => Error("Multiplication needs two operands")
+ | ("pow", [|l,r|]) => toOkAlgebraic((`Exponentiate, l, r))
+ | ("pow", _) => Error("Exponentiation needs two operands")
+ | ("dotMultiply", [|l, r|]) => toOkPointwise((`Multiply, l, r))
+ | ("dotMultiply", _) =>
+ Error("Dotwise multiplication needs two operands")
+ | ("rightLogShift", [|l, r|]) => toOkPointwise((`Add, l, r))
+ | ("rightLogShift", _) =>
+ Error("Dotwise addition needs two operands")
+ | ("divide", [|l, r|]) => toOkAlgebraic((`Divide, l, r))
+ | ("divide", _) => Error("Division needs two operands")
+ | ("leftTruncate", [|d, `SymbolicDist(`Float(lc))|]) =>
+ toOkTruncate((Some(lc), None, d))
+ | ("leftTruncate", _) =>
+ Error(
+ "leftTruncate needs two arguments: the expression and the cutoff",
+ )
+ | ("rightTruncate", [|d, `SymbolicDist(`Float(rc))|]) =>
+ toOkTruncate((None, Some(rc), d))
+ | ("rightTruncate", _) =>
+ Error(
+ "rightTruncate needs two arguments: the expression and the cutoff",
+ )
+ | (
+ "truncate",
+ [|d, `SymbolicDist(`Float(lc)), `SymbolicDist(`Float(rc))|],
+ ) =>
+ toOkTruncate((Some(lc), Some(rc), d))
+ | ("truncate", _) =>
+ Error(
+ "truncate needs three arguments: the expression and both cutoffs",
+ )
+ | ("pdf", [|d, `SymbolicDist(`Float(v))|]) =>
+ toOkFloatFromDist((`Pdf(v), d))
+ | ("cdf", [|d, `SymbolicDist(`Float(v))|]) =>
+ toOkFloatFromDist((`Cdf(v), d))
+ | ("inv", [|d, `SymbolicDist(`Float(v))|]) =>
+ toOkFloatFromDist((`Inv(v), d))
+ | ("mean", [|d|]) => toOkFloatFromDist((`Mean, d))
+ | ("sample", [|d|]) => toOkFloatFromDist((`Sample, d))
+ | _ => Error("This type not currently supported")
+ }
+ });
};
let functionParser = (nodeParser, name, args) => {
- let parseArgs = () => args |> E.A.fmap(nodeParser);
+ let parseArray = ags =>
+ ags |> E.A.fmap(nodeParser) |> E.A.R.firstErrorOrOpen;
+ let parseArgs = () => parseArray(args);
switch (name) {
- | "normal" => normal(args)
- | "lognormal" => lognormal(args)
- | "uniform" => uniform(args)
- | "beta" => beta(args)
- | "to" => to_(args)
- | "exponential" => exponential(args)
- | "cauchy" => cauchy(args)
- | "triangular" => triangular(args)
+ | "lognormal" => lognormal(args, parseArgs, nodeParser)
| "mm" =>
let weights =
args
@@ -338,27 +286,55 @@ module MathAdtToDistDst = {
| "sample"
| "cdf"
| "pdf" => operationParser(name, parseArgs())
- | n => Error(n ++ "(...) is not currently supported")
+ | name =>
+ parseArgs()
+ |> E.R.fmap((args: array(ExpressionTypes.ExpressionTree.node)) =>
+ `FunctionCall((name, args))
+ )
};
};
- let rec nodeParser =
+ let rec nodeParser:
+ MathJsonToMathJsAdt.arg =>
+ result(ExpressionTypes.ExpressionTree.node, string) =
fun
| Value(f) => Ok(`SymbolicDist(`Float(f)))
+ | Symbol(sym) => Ok(`Symbol(sym))
| Fn({name, args}) => functionParser(nodeParser, name, args)
| _ => {
- Error("This type not currently supported");
+ Error("This type not currently supported")
};
- let topLevel =
- fun
- | Value(_) as r => nodeParser(r)
- | Fn(_) as r => nodeParser(r)
+ // | FunctionAssignment({name, args, expression}) => {
+ // let evaluatedExpression = run(expression);
+ // `Function(_ => Ok(evaluatedExpression));
+ // }
+ let rec topLevel = (r): result(ExpressionTypes.Program.program, string) =>
+ switch (r) {
+ | FunctionAssignment({name, args, expression}) =>
+ switch (nodeParser(expression)) {
+ | Ok(r) => Ok([|`Assignment((name, `Function((args, r))))|])
+ | Error(r) => Error(r)
+ }
+ | Value(_) as r => nodeParser(r) |> E.R.fmap(r => [|`Expression(r)|])
+ | Fn(_) as r => nodeParser(r) |> E.R.fmap(r => [|`Expression(r)|])
| Array(_) => Error("Array not valid as top level")
- | Symbol(_) => Error("Symbol not valid as top level")
- | Object(_) => Error("Object not valid as top level");
+ | Symbol(s) => handleSymbol(s) |> E.R.fmap(r => [|`Expression(r)|])
+ | Object(_) => Error("Object not valid as top level")
+ | Assignment(name, value) =>
+ switch (name) {
+ | Symbol(symbol) =>
+ nodeParser(value) |> E.R.fmap(r => [|`Assignment((symbol, r))|])
+ | _ => Error("Symbol not a string")
+ }
+ | Blocks(blocks) =>
+ blocks
+ |> E.A.fmap(b => topLevel(b))
+ |> E.A.R.firstErrorOrOpen
+ |> E.R.fmap(E.A.concatMany)
+ };
- let run = (r): result(ExpressionTypes.ExpressionTree.node, string) =>
+ let run = (r): result(ExpressionTypes.Program.program, string) =>
r |> MathAdtCleaner.run |> topLevel;
};
@@ -369,7 +345,7 @@ module MathAdtToDistDst = {
*/
let pointwiseToRightLogShift = Js.String.replaceByRe([%re "/\.\+/g"], ">>>");
-let fromString = str => {
+let fromString2 = str => {
/* We feed the user-typed string into Mathjs.parseMath,
which returns a JSON with (hopefully) a single-element array.
This array element is the top-level node of a nested-object tree
@@ -390,3 +366,7 @@ let fromString = str => {
let value = E.R.bind(mathJsParse, MathAdtToDistDst.run);
value;
};
+
+let fromString = str => {
+ fromString2(str);
+};
diff --git a/src/distPlus/expressionTree/Operation.re b/src/distPlus/expressionTree/Operation.re
index 26826f3f..e415b5de 100644
--- a/src/distPlus/expressionTree/Operation.re
+++ b/src/distPlus/expressionTree/Operation.re
@@ -7,6 +7,7 @@ module Algebraic = {
| `Add => (+.)
| `Subtract => (-.)
| `Multiply => ( *. )
+ | `Exponentiate => ( ** )
| `Divide => (/.);
let applyFn = (t, f1, f2) => {
@@ -21,6 +22,7 @@ module Algebraic = {
| `Add => "+"
| `Subtract => "-"
| `Multiply => "*"
+ | `Exponentiate => ( "**" )
| `Divide => "/";
let format = (a, b, c) => b ++ " " ++ toString(a) ++ " " ++ c;
diff --git a/src/distPlus/expressionTree/Program.re b/src/distPlus/expressionTree/Program.re
new file mode 100644
index 00000000..7adfb3d8
--- /dev/null
+++ b/src/distPlus/expressionTree/Program.re
@@ -0,0 +1,5 @@
+type t = ExpressionTypes.Program.program;
+
+let last = (r:t) => E.A.last(r) |> E.O.toResult("No rendered lines");
+// let run = (p:program) => p |> E.A.last |> E.O.fmap(r =>
+// )
\ No newline at end of file
diff --git a/src/distPlus/expressionTree/SamplingDistribution.re b/src/distPlus/expressionTree/SamplingDistribution.re
index 2f95be1e..de9ef58b 100644
--- a/src/distPlus/expressionTree/SamplingDistribution.re
+++ b/src/distPlus/expressionTree/SamplingDistribution.re
@@ -60,17 +60,11 @@ let combineShapesUsingSampling =
let shape =
samples
|> E.O.fmap(
- Samples.T.fromSamples(
- ~samplingInputs={
- sampleCount:
- Some(evaluationParams.samplingInputs.sampleCount),
- outputXYPoints:
- Some(evaluationParams.samplingInputs.outputXYPoints),
- kernelWidth: evaluationParams.samplingInputs.kernelWidth,
- },
+ SamplesToShape.fromSamples(
+ ~samplingInputs=evaluationParams.samplingInputs,
),
)
- |> E.O.bind(_, (r) => r.shape)
+ |> E.O.bind(_, r => r.shape)
|> E.O.toResult("No response");
shape |> E.R.fmap(r => `Normalize(`RenderedDist(r)));
},
diff --git a/src/distPlus/renderers/DistPlusRenderer.re b/src/distPlus/renderers/DistPlusRenderer.re
index 1d06eb4a..7c39e17c 100644
--- a/src/distPlus/renderers/DistPlusRenderer.re
+++ b/src/distPlus/renderers/DistPlusRenderer.re
@@ -1,23 +1,147 @@
-let run = (inputs: RenderTypes.DistPlusRenderer.inputs) => {
- let toDist = shape =>
+// 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 = 10000;
+ let defaultShouldDownsample = true;
+
+ type ingredients = {
+ guesstimatorString: string,
+ domain: DistTypes.domain,
+ unit: DistTypes.distributionUnit,
+ };
+ module Ingredients = {
+ type t = ingredients;
+ let make =
+ (
+ ~guesstimatorString,
+ ~domain=DistTypes.Complete,
+ ~unit=DistTypes.UnspecifiedDistribution,
+ (),
+ )
+ : t => {
+ guesstimatorString,
+ domain,
+ unit,
+ };
+ };
+
+ type inputs = {
+ distPlusIngredients: ingredients,
+ samplingInputs: SamplingInputs.t,
+ environment: ExpressionTypes.ExpressionTree.environment
+ };
+
+ let empty: SamplingInputs.t = {
+ sampleCount: None,
+ outputXYPoints: None,
+ kernelWidth: None,
+ shapeLength: None,
+ };
+
+ let make =
+ (
+ ~samplingInputs=empty,
+ ~distPlusIngredients,
+ ~environment=ExpressionTypes.ExpressionTree.Environment.empty,
+ (),
+ )
+ : inputs => {
+ distPlusIngredients,
+ samplingInputs,
+ environment,
+ };
+};
+
+module Internals = {
+ type inputs = {
+ samplingInputs: Inputs.SamplingInputs.t,
+ guesstimatorString: string,
+ environment: ExpressionTypes.ExpressionTree.environment,
+ };
+
+ let addVariable =
+ (
+ {samplingInputs, guesstimatorString, environment}: inputs,
+ str,
+ node,
+ )
+ : inputs => {
+ samplingInputs,
+ guesstimatorString,
+ environment:
+ ExpressionTypes.ExpressionTree.Environment.update(environment, str, _ => Some(node))
+ };
+
+ let distPlusRenderInputsToInputs = (inputs: Inputs.inputs): inputs => {
+ {
+ samplingInputs: inputs.samplingInputs,
+ guesstimatorString: inputs.distPlusIngredients.guesstimatorString,
+ environment: inputs.environment,
+ };
+ };
+
+ type outputs = {
+ graph: ExpressionTypes.ExpressionTree.node,
+ shape: DistTypes.shape,
+ };
+ let makeOutputs = (graph, shape): outputs => {graph, shape};
+
+ let runNode = (inputs, node) => {
+ ExpressionTree.toShape(
+ {
+ 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),
+ },
+ inputs.environment,
+ node,
+ );
+ };
+
+ let runProgram = (inputs: inputs, p: ExpressionTypes.Program.program) => {
+ let ins = ref(inputs);
+ p
+ |> E.A.fmap(statement =>
+ switch (statement) {
+ | `Assignment(name, node) =>
+ ins := addVariable(ins^, name, node);
+ None;
+ | `Expression(node) => Some(runNode(ins^, node))
+ }
+ )
+ |> E.A.O.concatSomes
+ |> E.A.R.firstErrorOrOpen;
+ };
+
+ let inputsToShape = (inputs: inputs) => {
+ MathJsParser.fromString(inputs.guesstimatorString)
+ |> E.R.bind(_, g => runProgram(inputs, g))
+ |> E.R.bind(_, r => E.A.last(r) |> E.O.toResult("No rendered lines") |> E.R.fmap(Shape.T.normalize));
+ };
+
+ let outputToDistPlus = (inputs: Inputs.inputs, shape: DistTypes.shape) => {
DistPlus.make(
~shape,
~domain=inputs.distPlusIngredients.domain,
~unit=inputs.distPlusIngredients.unit,
~guesstimatorString=Some(inputs.distPlusIngredients.guesstimatorString),
(),
- )
- |> DistPlus.T.normalize;
- let output =
- ShapeRenderer.run({
- samplingInputs: inputs.samplingInputs,
- guesstimatorString: inputs.distPlusIngredients.guesstimatorString,
- symbolicInputs: {
- length: inputs.recommendedLength,
- },
- });
- output
- |> E.R.fmap((o: RenderTypes.ShapeRenderer.Symbolic.outputs) =>
- toDist(o.shape)
- );
+ );
+ };
+};
+
+let run = (inputs: Inputs.inputs) => {
+ inputs
+ |> Internals.distPlusRenderInputsToInputs
+ |> Internals.inputsToShape
+ |> E.R.fmap(Internals.outputToDistPlus(inputs));
};
diff --git a/src/distPlus/renderers/ProgramRunner.re b/src/distPlus/renderers/ProgramRunner.re
new file mode 100644
index 00000000..e69de29b
diff --git a/src/distPlus/renderers/RenderTypes.re b/src/distPlus/renderers/RenderTypes.re
deleted file mode 100644
index 9b37503f..00000000
--- a/src/distPlus/renderers/RenderTypes.re
+++ /dev/null
@@ -1,127 +0,0 @@
-module ShapeRenderer = {
- module Sampling = {
- type inputs = {
- sampleCount: option(int),
- outputXYPoints: option(int),
- kernelWidth: option(float),
- };
- type samplingStats = {
- sampleCount: int,
- outputXYPoints: int,
- bandwidthXSuggested: float,
- bandwidthUnitSuggested: float,
- bandwidthXImplemented: float,
- bandwidthUnitImplemented: float,
- };
- type outputs = {
- continuousParseParams: option(samplingStats),
- shape: option(DistTypes.shape),
- };
- module Inputs = {
- let defaultSampleCount = 5000;
- let defaultOutputXYPoints = 10000;
- let empty = {
- sampleCount: None,
- outputXYPoints: None,
- kernelWidth: None,
- };
-
- type fInputs = {
- sampleCount: int,
- outputXYPoints: int,
- kernelWidth: option(float),
- };
- let toF = (i: inputs): fInputs => {
- sampleCount: i.sampleCount |> E.O.default(defaultSampleCount),
- outputXYPoints:
- i.outputXYPoints |> E.O.default(defaultOutputXYPoints),
- kernelWidth: i.kernelWidth,
- };
- };
- };
-
- module Symbolic = {
- type inputs = {length: int};
- type outputs = {
- graph: ExpressionTypes.ExpressionTree.node,
- shape: DistTypes.shape,
- };
- let make = (graph, shape) => {graph, shape};
- };
-
- module Combined = {
- type inputs = {
- samplingInputs: Sampling.inputs,
- symbolicInputs: Symbolic.inputs,
- guesstimatorString: string,
- };
- type outputs = {
- symbolic: option(Belt.Result.t(Symbolic.outputs, string)),
- sampling: option(Sampling.outputs),
- };
- let methodUsed = ({symbolic, sampling}:outputs) => switch(symbolic, sampling){
- | (Some(Ok(_)), _) => `Symbolic
- | (_, Some({shape: Some(_)})) => `Sampling
- | _ => `None
- }
- let getShape = (r: outputs) =>
- switch (r.symbolic, r.sampling) {
- | (Some(Ok({shape})), _) => Some(shape)
- | (_, Some({shape})) => shape
- | _ => None
- };
- };
-};
-
-module DistPlusRenderer = {
- let defaultRecommendedLength = 10000;
- let defaultShouldDownsample = true;
- type ingredients = {
- guesstimatorString: string,
- domain: DistTypes.domain,
- unit: DistTypes.distributionUnit,
- };
- type inputs = {
- distPlusIngredients: ingredients,
- samplingInputs: ShapeRenderer.Sampling.inputs,
- recommendedLength: int,
- shouldDownsample: bool,
- };
- module Ingredients = {
- let make =
- (
- ~guesstimatorString,
- ~domain=DistTypes.Complete,
- ~unit=DistTypes.UnspecifiedDistribution,
- (),
- )
- : ingredients => {
- guesstimatorString,
- domain,
- unit,
- };
- };
- let make =
- (
- ~samplingInputs=ShapeRenderer.Sampling.Inputs.empty,
- ~recommendedLength=defaultRecommendedLength,
- ~shouldDownsample=defaultShouldDownsample,
- ~distPlusIngredients,
- (),
- )
- : inputs => {
- distPlusIngredients,
- samplingInputs,
- recommendedLength,
- shouldDownsample,
- };
- type outputs = {
- shapeRenderOutputs: ShapeRenderer.Combined.outputs,
- distPlus: option(DistTypes.distPlus)
- }
- module Outputs = {
- let distplus = (t:outputs) => t.distPlus
- let shapeRenderOutputs = (t:outputs) => t.shapeRenderOutputs
- let make = (shapeRenderOutputs, distPlus) => {shapeRenderOutputs, distPlus};
- }
-};
diff --git a/src/distPlus/renderers/ShapeRenderer.re b/src/distPlus/renderers/ShapeRenderer.re
deleted file mode 100644
index c9ac37c7..00000000
--- a/src/distPlus/renderers/ShapeRenderer.re
+++ /dev/null
@@ -1,39 +0,0 @@
-// This transforms an array intersperced with spaces or newlines with a normally formatted one.
-// "3 4 5 3 2 1 " -> "[3,4,5,3,2,1]""
-let formatMessyArray = str => {
- let split = Js.String.splitByRe([%re "/\\n|\\r|\\s/"], str);
- if (E.A.length(split) > 20) {
- let inner = split |> Js.Array.joinWith(",");
- {j|[$inner]|j};
- } else {
- str;
- };
-};
-
-let formatString = str => {
- str |> formatMessyArray;
-};
-
-let runSymbolic = (inputs: RenderTypes.ShapeRenderer.Combined.inputs) => {
- let str = formatString(inputs.guesstimatorString);
- let graph = MathJsParser.fromString(str);
- graph
- |> E.R.bind(_, g =>
- ExpressionTree.toShape(
- inputs.symbolicInputs.length,
- {
- sampleCount:
- inputs.samplingInputs.sampleCount |> E.O.default(10000),
- outputXYPoints:
- inputs.samplingInputs.outputXYPoints |> E.O.default(10000),
- kernelWidth: inputs.samplingInputs.kernelWidth,
- },
- g,
- )
- |> E.R.fmap(RenderTypes.ShapeRenderer.Symbolic.make(g))
- );
-};
-
-let run = (inputs: RenderTypes.ShapeRenderer.Combined.inputs) => {
- runSymbolic(inputs);
-};
diff --git a/src/distPlus/renderers/samplesRenderer/Samples.re b/src/distPlus/renderers/samplesRenderer/Samples.re
deleted file mode 100644
index 7f9b10aa..00000000
--- a/src/distPlus/renderers/samplesRenderer/Samples.re
+++ /dev/null
@@ -1,185 +0,0 @@
-module Types = {
- let defaultSampleCount = 5000;
- let defaultOutputXYPoints = 10000;
-
- type inputs = {
- sampleCount: option(int),
- outputXYPoints: option(int),
- kernelWidth: option(float),
- };
-
- type samplingStats = {
- sampleCount: int,
- outputXYPoints: int,
- bandwidthXSuggested: float,
- bandwidthUnitSuggested: float,
- bandwidthXImplemented: float,
- bandwidthUnitImplemented: float,
- };
-
- type outputs = {
- continuousParseParams: option(samplingStats),
- shape: option(DistTypes.shape),
- };
-
- let empty = {sampleCount: None, outputXYPoints: None, kernelWidth: None};
-
- type fInputs = {
- sampleCount: int,
- outputXYPoints: int,
- kernelWidth: option(float),
- };
-
- let toF = (i: inputs): fInputs => {
- sampleCount: i.sampleCount |> E.O.default(defaultSampleCount),
- outputXYPoints: i.outputXYPoints |> E.O.default(defaultOutputXYPoints),
- kernelWidth: i.kernelWidth,
- };
-};
-
-module JS = {
- [@bs.deriving abstract]
- type distJs = {
- xs: array(float),
- ys: array(float),
- };
-
- let jsToDist = (d: distJs): DistTypes.xyShape => {
- xs: xsGet(d),
- ys: ysGet(d),
- };
-
- [@bs.module "./KdeLibrary.js"]
- external samplesToContinuousPdf: (array(float), int, int) => distJs =
- "samplesToContinuousPdf";
-};
-
-module KDE = {
- let normalSampling = (samples, outputXYPoints, kernelWidth) => {
- samples
- |> JS.samplesToContinuousPdf(_, outputXYPoints, kernelWidth)
- |> JS.jsToDist;
- };
-};
-
-module T = {
- type t = array(float);
-
- let splitContinuousAndDiscrete = (sortedArray: t) => {
- let continuous = [||];
- let discrete = E.FloatFloatMap.empty();
- Belt.Array.forEachWithIndex(
- sortedArray,
- (index, element) => {
- let maxIndex = (sortedArray |> Array.length) - 1;
- let possiblySimilarElements =
- (
- switch (index) {
- | 0 => [|index + 1|]
- | n when n == maxIndex => [|index - 1|]
- | _ => [|index - 1, index + 1|]
- }
- )
- |> Belt.Array.map(_, r => sortedArray[r]);
- let hasSimilarElement =
- Belt.Array.some(possiblySimilarElements, r => r == element);
- hasSimilarElement
- ? E.FloatFloatMap.increment(element, discrete)
- : {
- let _ = Js.Array.push(element, continuous);
- ();
- };
- ();
- },
- );
- (continuous, discrete);
- };
-
- let xWidthToUnitWidth = (samples, outputXYPoints, xWidth) => {
- let xyPointRange = E.A.Sorted.range(samples) |> E.O.default(0.0);
- let xyPointWidth = xyPointRange /. float_of_int(outputXYPoints);
- xWidth /. xyPointWidth;
- };
-
- let formatUnitWidth = w => Jstat.max([|w, 1.0|]) |> int_of_float;
-
- let suggestedUnitWidth = (samples, outputXYPoints) => {
- let suggestedXWidth = Bandwidth.nrd0(samples);
- xWidthToUnitWidth(samples, outputXYPoints, suggestedXWidth);
- };
-
- let kde = (~samples, ~outputXYPoints, width) => {
- KDE.normalSampling(samples, outputXYPoints, width);
- };
-
- let toShape =
- (
- ~samples: t,
- ~samplingInputs: Types.fInputs,
- (),
- ) => {
- Array.fast_sort(compare, samples);
- let (continuousPart, discretePart) = E.A.Sorted.Floats.split(samples);
- let length = samples |> E.A.length |> float_of_int;
- let discrete: DistTypes.discreteShape =
- discretePart
- |> E.FloatFloatMap.fmap(r => r /. length)
- |> E.FloatFloatMap.toArray
- |> XYShape.T.fromZippedArray
- |> Discrete.make;
-
- let pdf =
- continuousPart |> E.A.length > 5
- ? {
- let _suggestedXWidth = Bandwidth.nrd0(continuousPart);
- // todo: This does some recalculating from the last step.
- let _suggestedUnitWidth =
- suggestedUnitWidth(continuousPart, samplingInputs.outputXYPoints);
- let usedWidth =
- samplingInputs.kernelWidth |> E.O.default(_suggestedXWidth);
- let usedUnitWidth =
- xWidthToUnitWidth(
- samples,
- samplingInputs.outputXYPoints,
- usedWidth,
- );
- let foo: Types.samplingStats = {
- sampleCount: samplingInputs.sampleCount,
- outputXYPoints: samplingInputs.outputXYPoints,
- bandwidthXSuggested: _suggestedXWidth,
- bandwidthUnitSuggested: _suggestedUnitWidth,
- bandwidthXImplemented: usedWidth,
- bandwidthUnitImplemented: usedUnitWidth,
- };
- continuousPart
- |> kde(
- ~samples=_,
- ~outputXYPoints=samplingInputs.outputXYPoints,
- formatUnitWidth(usedUnitWidth),
- )
- |> Continuous.make
- |> (r => Some((r, foo)));
- }
- : None;
- let shape =
- MixedShapeBuilder.buildSimple(
- ~continuous=pdf |> E.O.fmap(fst),
- ~discrete=Some(discrete),
- );
- let samplesParse: Types.outputs = {
- continuousParseParams: pdf |> E.O.fmap(snd),
- shape,
- };
- samplesParse;
- };
-
- let fromSamples =
- (
- ~samplingInputs=Types.empty,
- samples,
- ) => {
- let samplingInputs =
- Types.toF(samplingInputs);
- toShape(~samples, ~samplingInputs, ());
- };
-};
diff --git a/src/distPlus/renderers/samplesRenderer/SamplesToShape.re b/src/distPlus/renderers/samplesRenderer/SamplesToShape.re
new file mode 100644
index 00000000..7b3c231f
--- /dev/null
+++ b/src/distPlus/renderers/samplesRenderer/SamplesToShape.re
@@ -0,0 +1,164 @@
+module Internals = {
+ module Types = {
+ type samplingStats = {
+ sampleCount: int,
+ outputXYPoints: int,
+ bandwidthXSuggested: float,
+ bandwidthUnitSuggested: float,
+ bandwidthXImplemented: float,
+ bandwidthUnitImplemented: float,
+ };
+
+ type outputs = {
+ continuousParseParams: option(samplingStats),
+ shape: option(DistTypes.shape),
+ };
+ };
+
+ module JS = {
+ [@bs.deriving abstract]
+ type distJs = {
+ xs: array(float),
+ ys: array(float),
+ };
+
+ let jsToDist = (d: distJs): DistTypes.xyShape => {
+ xs: xsGet(d),
+ ys: ysGet(d),
+ };
+
+ [@bs.module "./KdeLibrary.js"]
+ external samplesToContinuousPdf: (array(float), int, int) => distJs =
+ "samplesToContinuousPdf";
+ };
+
+ module KDE = {
+ let normalSampling = (samples, outputXYPoints, kernelWidth) => {
+ samples
+ |> JS.samplesToContinuousPdf(_, outputXYPoints, kernelWidth)
+ |> JS.jsToDist;
+ };
+ };
+
+ module T = {
+ type t = array(float);
+
+ let splitContinuousAndDiscrete = (sortedArray: t) => {
+ let continuous = [||];
+ let discrete = E.FloatFloatMap.empty();
+ Belt.Array.forEachWithIndex(
+ sortedArray,
+ (index, element) => {
+ let maxIndex = (sortedArray |> Array.length) - 1;
+ let possiblySimilarElements =
+ (
+ switch (index) {
+ | 0 => [|index + 1|]
+ | n when n == maxIndex => [|index - 1|]
+ | _ => [|index - 1, index + 1|]
+ }
+ )
+ |> Belt.Array.map(_, r => sortedArray[r]);
+ let hasSimilarElement =
+ Belt.Array.some(possiblySimilarElements, r => r == element);
+ hasSimilarElement
+ ? E.FloatFloatMap.increment(element, discrete)
+ : {
+ let _ = Js.Array.push(element, continuous);
+ ();
+ };
+ ();
+ },
+ );
+ (continuous, discrete);
+ };
+
+ let xWidthToUnitWidth = (samples, outputXYPoints, xWidth) => {
+ let xyPointRange = E.A.Sorted.range(samples) |> E.O.default(0.0);
+ let xyPointWidth = xyPointRange /. float_of_int(outputXYPoints);
+ xWidth /. xyPointWidth;
+ };
+
+ let formatUnitWidth = w => Jstat.max([|w, 1.0|]) |> int_of_float;
+
+ let suggestedUnitWidth = (samples, outputXYPoints) => {
+ let suggestedXWidth = Bandwidth.nrd0(samples);
+ xWidthToUnitWidth(samples, outputXYPoints, suggestedXWidth);
+ };
+
+ let kde = (~samples, ~outputXYPoints, width) => {
+ KDE.normalSampling(samples, outputXYPoints, width);
+ };
+ };
+};
+
+let toShape =
+ (
+ ~samples: Internals.T.t,
+ ~samplingInputs: ExpressionTypes.ExpressionTree.samplingInputs,
+ (),
+ ) => {
+ Array.fast_sort(compare, samples);
+ let (continuousPart, discretePart) = E.A.Sorted.Floats.split(samples);
+ let length = samples |> E.A.length |> float_of_int;
+ let discrete: DistTypes.discreteShape =
+ discretePart
+ |> E.FloatFloatMap.fmap(r => r /. length)
+ |> E.FloatFloatMap.toArray
+ |> XYShape.T.fromZippedArray
+ |> Discrete.make;
+
+ let pdf =
+ continuousPart |> E.A.length > 5
+ ? {
+ let _suggestedXWidth = Bandwidth.nrd0(continuousPart);
+ // todo: This does some recalculating from the last step.
+ let _suggestedUnitWidth =
+ Internals.T.suggestedUnitWidth(
+ continuousPart,
+ samplingInputs.outputXYPoints,
+ );
+ let usedWidth =
+ samplingInputs.kernelWidth |> E.O.default(_suggestedXWidth);
+ let usedUnitWidth =
+ Internals.T.xWidthToUnitWidth(
+ samples,
+ samplingInputs.outputXYPoints,
+ usedWidth,
+ );
+ let samplingStats: Internals.Types.samplingStats = {
+ sampleCount: samplingInputs.sampleCount,
+ outputXYPoints: samplingInputs.outputXYPoints,
+ bandwidthXSuggested: _suggestedXWidth,
+ bandwidthUnitSuggested: _suggestedUnitWidth,
+ bandwidthXImplemented: usedWidth,
+ bandwidthUnitImplemented: usedUnitWidth,
+ };
+ continuousPart
+ |> Internals.T.kde(
+ ~samples=_,
+ ~outputXYPoints=samplingInputs.outputXYPoints,
+ Internals.T.formatUnitWidth(usedUnitWidth),
+ )
+ |> Continuous.make
+ |> (r => Some((r, samplingStats)));
+ }
+ : None;
+
+ let shape =
+ MixedShapeBuilder.buildSimple(
+ ~continuous=pdf |> E.O.fmap(fst),
+ ~discrete=Some(discrete),
+ );
+
+ let samplesParse: Internals.Types.outputs = {
+ continuousParseParams: pdf |> E.O.fmap(snd),
+ shape,
+ };
+
+ samplesParse;
+};
+
+let fromSamples = (~samplingInputs, samples) => {
+ toShape(~samples, ~samplingInputs, ());
+};
diff --git a/src/distPlus/symbolic/SymbolicDist.re b/src/distPlus/symbolic/SymbolicDist.re
index efa4e49b..33942187 100644
--- a/src/distPlus/symbolic/SymbolicDist.re
+++ b/src/distPlus/symbolic/SymbolicDist.re
@@ -2,6 +2,12 @@ open SymbolicTypes;
module Exponential = {
type t = exponential;
+ let make = (rate:float): symbolicDist =>
+ `Exponential(
+ {
+ rate:rate
+ },
+ );
let pdf = (x, t: t) => Jstat.exponential##pdf(x, t.rate);
let cdf = (x, t: t) => Jstat.exponential##cdf(x, t.rate);
let inv = (p, t: t) => Jstat.exponential##inv(p, t.rate);
@@ -12,6 +18,7 @@ module Exponential = {
module Cauchy = {
type t = cauchy;
+ let make = (local, scale): symbolicDist => `Cauchy({local, scale});
let pdf = (x, t: t) => Jstat.cauchy##pdf(x, t.local, t.scale);
let cdf = (x, t: t) => Jstat.cauchy##cdf(x, t.local, t.scale);
let inv = (p, t: t) => Jstat.cauchy##inv(p, t.local, t.scale);
@@ -22,6 +29,10 @@ module Cauchy = {
module Triangular = {
type t = triangular;
+ let make = (low, medium, high): result(symbolicDist, string) =>
+ low < medium && medium < high
+ ? Ok(`Triangular({low, medium, high}))
+ : Error("Triangular values must be increasing order");
let pdf = (x, t: t) => Jstat.triangular##pdf(x, t.low, t.high, t.medium);
let cdf = (x, t: t) => Jstat.triangular##cdf(x, t.low, t.high, t.medium);
let inv = (p, t: t) => Jstat.triangular##inv(p, t.low, t.high, t.medium);
@@ -32,6 +43,7 @@ module Triangular = {
module Normal = {
type t = normal;
+ let make = (mean, stdev): symbolicDist => `Normal({mean, stdev});
let pdf = (x, t: t) => Jstat.normal##pdf(x, t.mean, t.stdev);
let cdf = (x, t: t) => Jstat.normal##cdf(x, t.mean, t.stdev);
@@ -75,6 +87,7 @@ module Normal = {
module Beta = {
type t = beta;
+ let make = (alpha, beta) => `Beta({alpha, beta});
let pdf = (x, t: t) => Jstat.beta##pdf(x, t.alpha, t.beta);
let cdf = (x, t: t) => Jstat.beta##cdf(x, t.alpha, t.beta);
let inv = (p, t: t) => Jstat.beta##inv(p, t.alpha, t.beta);
@@ -85,6 +98,7 @@ module Beta = {
module Lognormal = {
type t = lognormal;
+ let make = (mu, sigma) => `Lognormal({mu, sigma});
let pdf = (x, t: t) => Jstat.lognormal##pdf(x, t.mu, t.sigma);
let cdf = (x, t: t) => Jstat.lognormal##cdf(x, t.mu, t.sigma);
let inv = (p, t: t) => Jstat.lognormal##inv(p, t.mu, t.sigma);
@@ -131,6 +145,7 @@ module Lognormal = {
module Uniform = {
type t = uniform;
+ let make = (low, high) => `Uniform({low, high});
let pdf = (x, t: t) => Jstat.uniform##pdf(x, t.low, t.high);
let cdf = (x, t: t) => Jstat.uniform##cdf(x, t.low, t.high);
let inv = (p, t: t) => Jstat.uniform##inv(p, t.low, t.high);
@@ -146,6 +161,7 @@ module Uniform = {
module Float = {
type t = float;
+ let make = t => `Float(t);
let pdf = (x, t: t) => x == t ? 1.0 : 0.0;
let cdf = (x, t: t) => x >= t ? 1.0 : 0.0;
let inv = (p, t: t) => p < t ? 0.0 : 1.0;
@@ -317,13 +333,14 @@ module T = {
switch (d) {
| `Float(v) =>
Discrete(
- Discrete.make(~integralSumCache=Some(1.0), {xs: [|v|], ys: [|1.0|]}),
+ Discrete.make(
+ ~integralSumCache=Some(1.0),
+ {xs: [|v|], ys: [|1.0|]},
+ ),
)
| _ =>
let xs = interpolateXs(~xSelection=`ByWeight, d, sampleCount);
let ys = xs |> E.A.fmap(x => pdf(x, d));
- Continuous(
- Continuous.make(~integralSumCache=Some(1.0), {xs, ys}),
- );
+ Continuous(Continuous.make(~integralSumCache=Some(1.0), {xs, ys}));
};
};
diff --git a/src/interface/FormBuilder.re b/src/interface/FormBuilder.re
index ea06e318..428d5556 100644
--- a/src/interface/FormBuilder.re
+++ b/src/interface/FormBuilder.re
@@ -17,12 +17,10 @@ let propValue = (t: Prop.Value.t) => {
switch (t) {
| SelectSingle(r) => r |> ReasonReact.string
| ConditionalArray(r) => "Array" |> ReasonReact.string
- | DistPlusIngredients((r: RenderTypes.DistPlusRenderer.ingredients)) =>
+ | DistPlusIngredients((r: DistPlusRenderer.Inputs.ingredients)) =>
let newDistribution =
- RenderTypes.DistPlusRenderer.make(
+ DistPlusRenderer.Inputs.make(
~distPlusIngredients=r,
- ~recommendedLength=10000,
- ~shouldDownsample=true,
(),
)
|> DistPlusRenderer.run;
diff --git a/src/interface/Prop.re b/src/interface/Prop.re
index c956da04..e20a1f8c 100644
--- a/src/interface/Prop.re
+++ b/src/interface/Prop.re
@@ -9,7 +9,7 @@ module Value = {
| DateTime(MomentRe.Moment.t)
| FloatPoint(float)
| Probability(float)
- | DistPlusIngredients(RenderTypes.DistPlusRenderer.ingredients)
+ | DistPlusIngredients(DistPlusRenderer.Inputs.ingredients)
| ConditionalArray(array(conditional))
| FloatCdf(string);
@@ -85,7 +85,7 @@ module ValueCluster = {
[ | `combination(range(MomentRe.Moment.t)) | `item(string)],
)
| Probability([ | `item(string)])
- | DistPlusIngredients([ | `item(RenderTypes.DistPlusRenderer.ingredients)])
+ | DistPlusIngredients([ | `item(DistPlusRenderer.Inputs.ingredients)])
| ConditionalArray([ | `item(array(conditional))])
| FloatCdf([ | `item(string)]);
};
diff --git a/src/models/EAFunds.re b/src/models/EAFunds.re
index 215bfe2a..c4dd6dd1 100644
--- a/src/models/EAFunds.re
+++ b/src/models/EAFunds.re
@@ -110,7 +110,7 @@ module Model = {
// TODO: Fixe number that integral is calculated for
let getGlobalCatastropheChance = dateTime => {
GlobalCatastrophe.makeI(MomentRe.momentNow())
- |> RenderTypes.DistPlusRenderer.make(~distPlusIngredients=_, ())
+ |> DistPlusRenderer.Inputs.make(~distPlusIngredients=_, ())
|> DistPlusRenderer.run
|> E.R.bind(_, r =>
r
@@ -157,7 +157,7 @@ module Model = {
};
let distPlusIngredients =
- RenderTypes.DistPlusRenderer.Ingredients.make(
+ DistPlusRenderer.Inputs.Ingredients.make(
~guesstimatorString=str,
~domain=Complete,
~unit=UnspecifiedDistribution,
@@ -167,7 +167,7 @@ module Model = {
| CHANCE_OF_EXISTENCE =>
Prop.Value.DistPlusIngredients(
- RenderTypes.DistPlusRenderer.Ingredients.make(
+ DistPlusRenderer.Inputs.Ingredients.make(
~guesstimatorString=
GuesstimatorDist.min(
GlobalCatastrophe.guesstimatorString,
diff --git a/src/models/GlobalCatastrophe.re b/src/models/GlobalCatastrophe.re
index e01422b3..9f675267 100644
--- a/src/models/GlobalCatastrophe.re
+++ b/src/models/GlobalCatastrophe.re
@@ -1,7 +1,7 @@
let guesstimatorString = "uniform(1, 100)";
let makeI = (currentDateTime: MomentRe.Moment.t) => {
- RenderTypes.DistPlusRenderer.Ingredients.make(
+ DistPlusRenderer.Inputs.Ingredients.make(
~guesstimatorString,
~unit=TimeDistribution({zero: currentDateTime, unit: `years}),
~domain=RightLimited({xPoint: 300.0, excludingProbabilityMass: 0.3}),
diff --git a/src/models/Human.re b/src/models/Human.re
index 9ff30a77..2439adcc 100644
--- a/src/models/Human.re
+++ b/src/models/Human.re
@@ -2,7 +2,7 @@ let guesstimatorString = age =>
GuesstimatorDist.normal(72.0 -. age, 5.0 -. age *. 0.01);
let makeI = (age: float) => {
- RenderTypes.DistPlusRenderer.Ingredients.make(
+ DistPlusRenderer.Inputs.Ingredients.make(
~guesstimatorString=guesstimatorString(age),
~unit=TimeDistribution({zero: MomentRe.momentNow(), unit: `years}),
~domain=RightLimited({xPoint: 300.0, excludingProbabilityMass: 0.3}),