Merge pull request #68 from foretold-app/variable-test
Adds simple variables and functions
This commit is contained in:
commit
65f1485a55
|
@ -14,12 +14,12 @@ describe("Lodash", () => {
|
||||||
describe("Lodash", () => {
|
describe("Lodash", () => {
|
||||||
makeTest(
|
makeTest(
|
||||||
"split",
|
"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()),
|
([|1.432, 1.33455, 2.0|], E.FloatFloatMap.empty()),
|
||||||
);
|
);
|
||||||
makeTest(
|
makeTest(
|
||||||
"split",
|
"split",
|
||||||
Samples.T.splitContinuousAndDiscrete([|
|
SamplesToShape.Internals.T.splitContinuousAndDiscrete([|
|
||||||
1.432,
|
1.432,
|
||||||
1.33455,
|
1.33455,
|
||||||
2.0,
|
2.0,
|
||||||
|
@ -39,12 +39,12 @@ describe("Lodash", () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
let (_, discrete) =
|
let (_, discrete) =
|
||||||
Samples.T.splitContinuousAndDiscrete(makeDuplicatedArray(10));
|
SamplesToShape.Internals.T.splitContinuousAndDiscrete(makeDuplicatedArray(10));
|
||||||
let toArr = discrete |> E.FloatFloatMap.toArray;
|
let toArr = discrete |> E.FloatFloatMap.toArray;
|
||||||
makeTest("splitMedium", toArr |> Belt.Array.length, 10);
|
makeTest("splitMedium", toArr |> Belt.Array.length, 10);
|
||||||
|
|
||||||
let (c, discrete) =
|
let (c, discrete) =
|
||||||
Samples.T.splitContinuousAndDiscrete(makeDuplicatedArray(500));
|
SamplesToShape.Internals.T.splitContinuousAndDiscrete(makeDuplicatedArray(500));
|
||||||
let toArr = discrete |> E.FloatFloatMap.toArray;
|
let toArr = discrete |> E.FloatFloatMap.toArray;
|
||||||
makeTest("splitMedium", toArr |> Belt.Array.length, 500);
|
makeTest("splitMedium", toArr |> Belt.Array.length, 500);
|
||||||
})
|
})
|
||||||
|
|
|
@ -6,18 +6,18 @@
|
||||||
// uniform(0,1) > 0.3 ? lognormal(6.652, -0.41): 0
|
// uniform(0,1) > 0.3 ? lognormal(6.652, -0.41): 0
|
||||||
|
|
||||||
let timeDist ={
|
let timeDist ={
|
||||||
let ingredients = RenderTypes.DistPlusRenderer.Ingredients.make(
|
let ingredients = DistPlusRenderer.Inputs.Ingredients.make(
|
||||||
~guesstimatorString="(floor(10 to 15))",
|
~guesstimatorString="(floor(10 to 15))",
|
||||||
~domain=RightLimited({xPoint: 50.0, excludingProbabilityMass: 0.3}),
|
~domain=RightLimited({xPoint: 50.0, excludingProbabilityMass: 0.3}),
|
||||||
~unit=
|
~unit=
|
||||||
DistTypes.TimeDistribution({zero: MomentRe.momentNow(), unit: `years}),
|
DistTypes.TimeDistribution({zero: MomentRe.momentNow(), unit: `years}),
|
||||||
());
|
());
|
||||||
let inputs = RenderTypes.DistPlusRenderer.make(~distPlusIngredients=ingredients,())
|
let inputs = DistPlusRenderer.Inputs.make(~distPlusIngredients=ingredients,())
|
||||||
inputs |> DistPlusRenderer.run
|
inputs |> DistPlusRenderer.run
|
||||||
}
|
}
|
||||||
|
|
||||||
let setup = dist =>
|
let setup = dist =>
|
||||||
RenderTypes.DistPlusRenderer.make(~distPlusIngredients=dist,())
|
DistPlusRenderer.Inputs.make(~distPlusIngredients=dist,())
|
||||||
|> DistPlusRenderer.run
|
|> DistPlusRenderer.run
|
||||||
|> E.R.fmap(distPlus => <DistPlusPlot distPlus />)
|
|> E.R.fmap(distPlus => <DistPlusPlot distPlus />)
|
||||||
|> E.R.toOption
|
|> E.R.toOption
|
||||||
|
@ -28,7 +28,7 @@ let simpleExample = (name, guesstimatorString) =>
|
||||||
<h3 className="text-gray-600 text-lg font-bold">
|
<h3 className="text-gray-600 text-lg font-bold">
|
||||||
{name |> ReasonReact.string}
|
{name |> ReasonReact.string}
|
||||||
</h3>
|
</h3>
|
||||||
{setup(RenderTypes.DistPlusRenderer.Ingredients.make(~guesstimatorString, ()))}
|
{setup(DistPlusRenderer.Inputs.Ingredients.make(~guesstimatorString, ()))}
|
||||||
</>;
|
</>;
|
||||||
|
|
||||||
let timeExample = (name, guesstimatorString) =>
|
let timeExample = (name, guesstimatorString) =>
|
||||||
|
@ -37,7 +37,7 @@ let timeExample = (name, guesstimatorString) =>
|
||||||
{name |> ReasonReact.string}
|
{name |> ReasonReact.string}
|
||||||
</h3>
|
</h3>
|
||||||
{setup(
|
{setup(
|
||||||
RenderTypes.DistPlusRenderer.Ingredients.make(
|
DistPlusRenderer.Inputs.Ingredients.make(
|
||||||
~guesstimatorString,
|
~guesstimatorString,
|
||||||
~unit=TimeDistribution({zero: MomentRe.momentNow(), unit: `years}),
|
~unit=TimeDistribution({zero: MomentRe.momentNow(), unit: `years}),
|
||||||
(),
|
(),
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
let setup = dist =>
|
let setup = dist =>
|
||||||
RenderTypes.DistPlusRenderer.make(~distPlusIngredients=dist, ())
|
DistPlusRenderer.Inputs.make(~distPlusIngredients=dist, ())
|
||||||
|> DistPlusRenderer.run
|
|> DistPlusRenderer.run
|
||||||
|> E.R.fmap(distPlus => <DistPlusPlot distPlus />)
|
|> E.R.fmap(distPlus => <DistPlusPlot distPlus />)
|
||||||
|> E.R.toOption
|
|> E.R.toOption
|
||||||
|
@ -10,7 +10,7 @@ let simpleExample = (guesstimatorString, ~problem="", ()) =>
|
||||||
<p> {guesstimatorString |> ReasonReact.string} </p>
|
<p> {guesstimatorString |> ReasonReact.string} </p>
|
||||||
<p> {problem |> (e => "problem: " ++ e) |> ReasonReact.string} </p>
|
<p> {problem |> (e => "problem: " ++ e) |> ReasonReact.string} </p>
|
||||||
{setup(
|
{setup(
|
||||||
RenderTypes.DistPlusRenderer.Ingredients.make(~guesstimatorString, ()),
|
DistPlusRenderer.Inputs.Ingredients.make(~guesstimatorString, ()),
|
||||||
)}
|
)}
|
||||||
</>;
|
</>;
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,23 @@ module Form = ReForm.Make(FormConfig);
|
||||||
|
|
||||||
let schema = Form.Validation.Schema([||]);
|
let schema = Form.Validation.Schema([||]);
|
||||||
|
|
||||||
|
module FieldText = {
|
||||||
|
[@react.component]
|
||||||
|
let make = (~field, ~label) => {
|
||||||
|
<Form.Field
|
||||||
|
field
|
||||||
|
render={({handleChange, error, value, validate}) =>
|
||||||
|
<Antd.Form.Item label={label |> R.ste}>
|
||||||
|
<Antd.Input.TextArea
|
||||||
|
value
|
||||||
|
onChange={BsReform.Helpers.handleChange(handleChange)}
|
||||||
|
onBlur={_ => validate()}
|
||||||
|
/>
|
||||||
|
</Antd.Form.Item>
|
||||||
|
}
|
||||||
|
/>;
|
||||||
|
};
|
||||||
|
};
|
||||||
module FieldString = {
|
module FieldString = {
|
||||||
[@react.component]
|
[@react.component]
|
||||||
let make = (~field, ~label) => {
|
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 = {
|
module DemoDist = {
|
||||||
[@react.component]
|
[@react.component]
|
||||||
let make = (~guesstimatorString, ~domain, ~unit, ~options) => {
|
let make = (~guesstimatorString, ~domain, ~unit, ~options) => {
|
||||||
|
@ -127,30 +137,34 @@ module DemoDist = {
|
||||||
{switch (domain, unit, options) {
|
{switch (domain, unit, options) {
|
||||||
| (Some(domain), Some(unit), Some(options)) =>
|
| (Some(domain), Some(unit), Some(options)) =>
|
||||||
let distPlusIngredients =
|
let distPlusIngredients =
|
||||||
RenderTypes.DistPlusRenderer.Ingredients.make(
|
DistPlusRenderer.Inputs.Ingredients.make(
|
||||||
~guesstimatorString,
|
~guesstimatorString,
|
||||||
~domain,
|
~domain,
|
||||||
~unit,
|
~unit,
|
||||||
(),
|
(),
|
||||||
);
|
);
|
||||||
let inputs =
|
let inputs1 =
|
||||||
RenderTypes.DistPlusRenderer.make(
|
DistPlusRenderer.Inputs.make(
|
||||||
~samplingInputs={
|
~samplingInputs={
|
||||||
sampleCount: Some(options.sampleCount),
|
sampleCount: Some(options.sampleCount),
|
||||||
outputXYPoints: Some(options.outputXYPoints),
|
outputXYPoints: Some(options.outputXYPoints),
|
||||||
kernelWidth: options.kernelWidth,
|
kernelWidth: options.kernelWidth,
|
||||||
|
shapeLength: Some(options.downsampleTo |> E.O.default(1000))
|
||||||
},
|
},
|
||||||
~distPlusIngredients,
|
~distPlusIngredients,
|
||||||
~shouldDownsample=options.downsampleTo |> E.O.isSome,
|
~environment=
|
||||||
~recommendedLength=options.downsampleTo |> E.O.default(1000),
|
[|("p", `SymbolicDist(`Float(1.0)))|]
|
||||||
|
->Belt.Map.String.fromArray,
|
||||||
(),
|
(),
|
||||||
);
|
);
|
||||||
let response = DistPlusRenderer.run(inputs);
|
|
||||||
switch (response) {
|
let response1 = DistPlusRenderer.run(inputs1);
|
||||||
| Ok(distPlus) =>
|
switch (response1) {
|
||||||
let normalizedDistPlus = DistPlus.T.normalize(distPlus);
|
| (Ok(distPlus1)) =>
|
||||||
<DistPlusPlot distPlus=normalizedDistPlus />;
|
<>
|
||||||
| Error(r) => r |> R.ste
|
<DistPlusPlot distPlus={DistPlus.T.normalize(distPlus1)} />
|
||||||
|
</>
|
||||||
|
| (Error(r)) => r |> R.ste
|
||||||
};
|
};
|
||||||
| _ =>
|
| _ =>
|
||||||
"Nothing to show. Try to change the distribution description."
|
"Nothing to show. Try to change the distribution description."
|
||||||
|
@ -314,7 +328,7 @@ let make = () => {
|
||||||
<Antd.Form onSubmit>
|
<Antd.Form onSubmit>
|
||||||
<Row _type=`flex className=Styles.rows>
|
<Row _type=`flex className=Styles.rows>
|
||||||
<Col span=24>
|
<Col span=24>
|
||||||
<FieldString
|
<FieldText
|
||||||
field=FormConfig.GuesstimatorString
|
field=FormConfig.GuesstimatorString
|
||||||
label="Guesstimator String"
|
label="Guesstimator String"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -392,8 +392,8 @@ module Draw = {
|
||||||
let normal: SymbolicTypes.symbolicDist = `Normal({mean, stdev});
|
let normal: SymbolicTypes.symbolicDist = `Normal({mean, stdev});
|
||||||
let normalShape =
|
let normalShape =
|
||||||
ExpressionTree.toShape(
|
ExpressionTree.toShape(
|
||||||
numSamples,
|
{sampleCount: 10000, outputXYPoints: 10000, kernelWidth: None, shapeLength:numSamples},
|
||||||
{sampleCount: 10000, outputXYPoints: 10000, kernelWidth: None},
|
ExpressionTypes.ExpressionTree.Environment.empty,
|
||||||
`SymbolicDist(normal),
|
`SymbolicDist(normal),
|
||||||
) |> E.R.toExn;
|
) |> E.R.toExn;
|
||||||
let xyShape: Types.xyShape =
|
let xyShape: Types.xyShape =
|
||||||
|
|
|
@ -106,7 +106,6 @@ let init = {
|
||||||
showParams: false,
|
showParams: false,
|
||||||
showPercentiles: true,
|
showPercentiles: true,
|
||||||
distributions: [
|
distributions: [
|
||||||
{yLog: false, xLog: false, isCumulative: false, height: 4},
|
{yLog: false, xLog: false, isCumulative: false, height: 1},
|
||||||
{yLog: false, xLog: false, isCumulative: true, height: 1},
|
|
||||||
],
|
],
|
||||||
};
|
};
|
|
@ -45,11 +45,8 @@ let toDiscretePointMassesFromTriangulars =
|
||||||
|
|
||||||
if (inverse) {
|
if (inverse) {
|
||||||
for (i in 1 to n - 2) {
|
for (i in 1 to n - 2) {
|
||||||
Belt.Array.set(
|
Belt.Array.set(masses, i - 1, (xs[i + 1] -. xs[i - 1]) *. ys[i] /. 2.)
|
||||||
masses,
|
|> ignore;
|
||||||
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
|
// this only works when the whole triange is either on the left or on the right of zero
|
||||||
let a = xs[i - 1];
|
let a = xs[i - 1];
|
||||||
|
@ -80,14 +77,12 @@ let toDiscretePointMassesFromTriangulars =
|
||||||
} else {
|
} else {
|
||||||
for (i in 1 to n - 2) {
|
for (i in 1 to n - 2) {
|
||||||
// area of triangle = width * height / 2
|
// area of triangle = width * height / 2
|
||||||
Belt.Array.set(
|
Belt.Array.set(masses, i - 1, (xs[i + 1] -. xs[i - 1]) *. ys[i] /. 2.)
|
||||||
masses,
|
|> ignore;
|
||||||
i - 1,
|
|
||||||
(xs[i + 1] -. xs[i - 1]) *. ys[i] /. 2.,
|
|
||||||
) |> ignore;
|
|
||||||
|
|
||||||
// means of triangle = (a + b + c) / 3
|
// 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
|
// variance of triangle = (a^2 + b^2 + c^2 - ab - ac - bc) / 18
|
||||||
Belt.Array.set(
|
Belt.Array.set(
|
||||||
|
@ -102,7 +97,8 @@ let toDiscretePointMassesFromTriangulars =
|
||||||
-. xsProdN2[i - 1]
|
-. xsProdN2[i - 1]
|
||||||
)
|
)
|
||||||
/. 18.,
|
/. 18.,
|
||||||
) |> ignore;
|
)
|
||||||
|
|> ignore;
|
||||||
();
|
();
|
||||||
};
|
};
|
||||||
{n: n - 2, masses, means, variances};
|
{n: n - 2, masses, means, variances};
|
||||||
|
@ -134,8 +130,10 @@ let combineShapesContinuousContinuous =
|
||||||
| `Subtract => ((m1, m2) => m1 -. m2)
|
| `Subtract => ((m1, m2) => m1 -. m2)
|
||||||
| `Multiply => ((m1, m2) => m1 *. m2)
|
| `Multiply => ((m1, m2) => m1 *. m2)
|
||||||
| `Divide => ((m1, mInv2) => m1 *. mInv2)
|
| `Divide => ((m1, mInv2) => m1 *. mInv2)
|
||||||
|
| `Exponentiate => ((m1, mInv2) => m1 ** mInv2)
|
||||||
}; // note: here, mInv2 = mean(1 / t2) ~= 1 / mean(t2)
|
}; // 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
|
// converts the variances and means of the two inputs into the variance of the output
|
||||||
let combineVariancesFn =
|
let combineVariancesFn =
|
||||||
switch (op) {
|
switch (op) {
|
||||||
|
@ -144,6 +142,8 @@ let combineShapesContinuousContinuous =
|
||||||
| `Multiply => (
|
| `Multiply => (
|
||||||
(v1, v2, m1, m2) => v1 *. v2 +. v1 *. m2 ** 2. +. v2 *. m1 ** 2.
|
(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 => (
|
| `Divide => (
|
||||||
(v1, vInv2, m1, mInv2) =>
|
(v1, vInv2, m1, mInv2) =>
|
||||||
v1 *. vInv2 +. v1 *. mInv2 ** 2. +. vInv2 *. m1 ** 2.
|
v1 *. vInv2 +. v1 *. mInv2 ** 2. +. vInv2 *. m1 ** 2.
|
||||||
|
@ -225,9 +225,12 @@ let toDiscretePointMassesFromDiscrete =
|
||||||
};
|
};
|
||||||
|
|
||||||
let combineShapesContinuousDiscrete =
|
let combineShapesContinuousDiscrete =
|
||||||
(op: ExpressionTypes.algebraicOperation, continuousShape: DistTypes.xyShape, discreteShape: DistTypes.xyShape)
|
(
|
||||||
|
op: ExpressionTypes.algebraicOperation,
|
||||||
|
continuousShape: DistTypes.xyShape,
|
||||||
|
discreteShape: DistTypes.xyShape,
|
||||||
|
)
|
||||||
: DistTypes.xyShape => {
|
: DistTypes.xyShape => {
|
||||||
|
|
||||||
let t1n = continuousShape |> XYShape.T.length;
|
let t1n = continuousShape |> XYShape.T.length;
|
||||||
let t2n = discreteShape |> XYShape.T.length;
|
let t2n = discreteShape |> XYShape.T.length;
|
||||||
|
|
||||||
|
@ -248,15 +251,19 @@ let combineShapesContinuousDiscrete =
|
||||||
Belt.Array.set(
|
Belt.Array.set(
|
||||||
dxyShape,
|
dxyShape,
|
||||||
i,
|
i,
|
||||||
(fn(continuousShape.xs[i], discreteShape.xs[j]),
|
(
|
||||||
continuousShape.ys[i] *. discreteShape.ys[j]),
|
fn(continuousShape.xs[i], discreteShape.xs[j]),
|
||||||
) |> ignore;
|
continuousShape.ys[i] *. discreteShape.ys[j],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|> ignore;
|
||||||
();
|
();
|
||||||
};
|
};
|
||||||
Belt.Array.set(outXYShapes, j, dxyShape) |> ignore;
|
Belt.Array.set(outXYShapes, j, dxyShape) |> ignore;
|
||||||
();
|
();
|
||||||
}
|
}
|
||||||
| `Multiply
|
| `Multiply
|
||||||
|
| `Exponentiate
|
||||||
| `Divide =>
|
| `Divide =>
|
||||||
for (j in 0 to t2n - 1) {
|
for (j in 0 to t2n - 1) {
|
||||||
// creates a new continuous shape for each one of the discrete points, and collects them in outXYShapes.
|
// 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(
|
Belt.Array.set(
|
||||||
dxyShape,
|
dxyShape,
|
||||||
i,
|
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;
|
Belt.Array.set(outXYShapes, j, dxyShape) |> ignore;
|
||||||
|
@ -278,7 +289,10 @@ let combineShapesContinuousDiscrete =
|
||||||
outXYShapes
|
outXYShapes
|
||||||
|> E.A.fmap(XYShape.T.fromZippedArray)
|
|> E.A.fmap(XYShape.T.fromZippedArray)
|
||||||
|> E.A.fold_left(
|
|> E.A.fold_left(
|
||||||
XYShape.PointwiseCombination.combine((+.),
|
XYShape.PointwiseCombination.combine(
|
||||||
XYShape.XtoY.continuousInterpolator(`Linear, `UseZero)),
|
(+.),
|
||||||
XYShape.T.empty);
|
XYShape.XtoY.continuousInterpolator(`Linear, `UseZero),
|
||||||
|
),
|
||||||
|
XYShape.T.empty,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -37,6 +37,7 @@ let empty: DistTypes.continuousShape = {
|
||||||
let stepwiseToLinear = (t: t): t =>
|
let stepwiseToLinear = (t: t): t =>
|
||||||
make(~integralSumCache=t.integralSumCache, ~integralCache=t.integralCache, XYShape.Range.stepwiseToLinear(t.xyShape));
|
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 =
|
let combinePointwise =
|
||||||
(
|
(
|
||||||
~integralSumCachesFn=(_, _) => None,
|
~integralSumCachesFn=(_, _) => None,
|
||||||
|
|
|
@ -216,6 +216,11 @@ let sample = (t: t): float => {
|
||||||
bar;
|
bar;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let isFloat = (t:t) => switch(t){
|
||||||
|
| Discrete({xyShape: {xs: [|_|], ys: [|1.0|]}}) => true
|
||||||
|
| _ => false
|
||||||
|
}
|
||||||
|
|
||||||
let sampleNRendered = (n, dist) => {
|
let sampleNRendered = (n, dist) => {
|
||||||
let integralCache = T.Integral.get(dist);
|
let integralCache = T.Integral.get(dist);
|
||||||
let distWithUpdatedIntegralCache = T.updateIntegralCache(Some(integralCache), dist);
|
let distWithUpdatedIntegralCache = T.updateIntegralCache(Some(integralCache), dist);
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
open ExpressionTypes.ExpressionTree;
|
open ExpressionTypes.ExpressionTree;
|
||||||
|
|
||||||
let toShape = (intendedShapeLength: int, samplingInputs, node: node) => {
|
let toLeaf = (samplingInputs, environment, node: node) => {
|
||||||
let renderResult =
|
node
|
||||||
`Render(`Normalize(node))
|
|
||||||
|> ExpressionTreeEvaluator.toLeaf({
|
|> ExpressionTreeEvaluator.toLeaf({
|
||||||
samplingInputs,
|
samplingInputs,
|
||||||
intendedShapeLength,
|
environment,
|
||||||
evaluateNode: ExpressionTreeEvaluator.toLeaf,
|
evaluateNode: ExpressionTreeEvaluator.toLeaf,
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
let toShape = (samplingInputs, environment, node: node) => {
|
||||||
|
let renderResult =
|
||||||
|
`Render(`Normalize(node)) |> toLeaf(samplingInputs, environment);
|
||||||
|
|
||||||
switch (renderResult) {
|
switch (renderResult) {
|
||||||
| Ok(`RenderedDist(shape)) => Ok(shape)
|
| Ok(`RenderedDist(shape)) => Ok(shape)
|
||||||
|
|
|
@ -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
|
// 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.
|
// 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!
|
// TODO: This should work for symbolic distributions too!
|
||||||
|
@ -176,7 +176,8 @@ module PointwiseCombination = {
|
||||||
) => {
|
) => {
|
||||||
switch (pointwiseOp) {
|
switch (pointwiseOp) {
|
||||||
| `Add => pointwiseAdd(evaluationParams, t1, t2)
|
| `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 = {
|
module Render = {
|
||||||
let rec operationToLeaf =
|
let rec operationToLeaf =
|
||||||
(evaluationParams: evaluationParams, t: node): result(t, string) => {
|
(evaluationParams: evaluationParams, t: node): result(t, string) => {
|
||||||
switch (t) {
|
switch (t) {
|
||||||
|
| `Function(_) => Error("Cannot render a function")
|
||||||
| `SymbolicDist(d) =>
|
| `SymbolicDist(d) =>
|
||||||
Ok(
|
Ok(
|
||||||
`RenderedDist(
|
`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
|
| `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,
|
/* This function recursively goes through the nodes of the parse tree,
|
||||||
replacing each Operation node and its subtree with a Data node.
|
replacing each Operation node and its subtree with a Data node.
|
||||||
Whenever possible, the replacement produces a new Symbolic Data node,
|
Whenever possible, the replacement produces a new Symbolic Data node,
|
||||||
|
@ -314,5 +335,9 @@ let toLeaf =
|
||||||
FloatFromDist.operationToLeaf(evaluationParams, distToFloatOp, t)
|
FloatFromDist.operationToLeaf(evaluationParams, distToFloatOp, t)
|
||||||
| `Normalize(t) => Normalize.operationToLeaf(evaluationParams, t)
|
| `Normalize(t) => Normalize.operationToLeaf(evaluationParams, t)
|
||||||
| `Render(t) => Render.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)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
type algebraicOperation = [ | `Add | `Multiply | `Subtract | `Divide];
|
type algebraicOperation = [ | `Add | `Multiply | `Subtract | `Divide | `Exponentiate];
|
||||||
type pointwiseOperation = [ | `Add | `Multiply];
|
type pointwiseOperation = [ | `Add | `Multiply | `Exponentiate];
|
||||||
type scaleOperation = [ | `Multiply | `Exponentiate | `Log];
|
type scaleOperation = [ | `Multiply | `Exponentiate | `Log];
|
||||||
type distToFloatOperation = [
|
type distToFloatOperation = [
|
||||||
| `Pdf(float)
|
| `Pdf(float)
|
||||||
|
@ -20,17 +20,37 @@ module ExpressionTree = {
|
||||||
| `Truncate(option(float), option(float), node)
|
| `Truncate(option(float), option(float), node)
|
||||||
| `Normalize(node)
|
| `Normalize(node)
|
||||||
| `FloatFromDist(distToFloatOperation, node)
|
| `FloatFromDist(distToFloatOperation, node)
|
||||||
|
| `Function(array(string), node)
|
||||||
|
| `FunctionCall(string, array(node))
|
||||||
|
| `Symbol(string)
|
||||||
];
|
];
|
||||||
|
|
||||||
type samplingInputs = {
|
type samplingInputs = {
|
||||||
sampleCount: int,
|
sampleCount: int,
|
||||||
outputXYPoints: int,
|
outputXYPoints: int,
|
||||||
kernelWidth: option(float),
|
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 = {
|
type evaluationParams = {
|
||||||
samplingInputs,
|
samplingInputs,
|
||||||
intendedShapeLength: int,
|
environment,
|
||||||
evaluateNode: (evaluationParams, node) => Belt.Result.t(node, string),
|
evaluateNode: (evaluationParams, node) => Belt.Result.t(node, string),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -71,8 +91,20 @@ module ExpressionTree = {
|
||||||
| `RenderedDist(r) => Some(r)
|
| `RenderedDist(r) => Some(r)
|
||||||
| _ => None
|
| _ => 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 = [
|
type simplificationResult = [
|
||||||
|
@ -80,3 +112,8 @@ type simplificationResult = [
|
||||||
| `Error(string)
|
| `Error(string)
|
||||||
| `NoSolution
|
| `NoSolution
|
||||||
];
|
];
|
||||||
|
|
||||||
|
module Program = {
|
||||||
|
type statement = [ | `Assignment(string, ExpressionTree.node) | `Expression(ExpressionTree.node)];
|
||||||
|
type program = array(statement);
|
||||||
|
}
|
122
src/distPlus/expressionTree/Functions.re
Normal file
122
src/distPlus/expressionTree/Functions.re
Normal file
|
@ -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")
|
||||||
|
};
|
|
@ -4,10 +4,18 @@ module MathJsonToMathJsAdt = {
|
||||||
| Value(float)
|
| Value(float)
|
||||||
| Fn(fn)
|
| Fn(fn)
|
||||||
| Array(array(arg))
|
| Array(array(arg))
|
||||||
|
| Blocks(array(arg))
|
||||||
| Object(Js.Dict.t(arg))
|
| Object(Js.Dict.t(arg))
|
||||||
|
| Assignment(arg, arg)
|
||||||
|
| FunctionAssignment(fnAssignment)
|
||||||
and fn = {
|
and fn = {
|
||||||
name: string,
|
name: string,
|
||||||
args: array(arg),
|
args: array(arg),
|
||||||
|
}
|
||||||
|
and fnAssignment = {
|
||||||
|
name: string,
|
||||||
|
args: array(string),
|
||||||
|
expression: arg,
|
||||||
};
|
};
|
||||||
|
|
||||||
let rec run = (j: Js.Json.t) =>
|
let rec run = (j: Js.Json.t) =>
|
||||||
|
@ -40,6 +48,25 @@ module MathJsonToMathJsAdt = {
|
||||||
let items = field("items", array(run), j);
|
let items = field("items", array(run), j);
|
||||||
Some(Array(items |> E.A.O.concatSomes));
|
Some(Array(items |> E.A.O.concatSomes));
|
||||||
| "SymbolNode" => Some(Symbol(field("name", string, j)))
|
| "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 =>
|
| n =>
|
||||||
Js.log3("Couldn't parse mathjs node", j, n);
|
Js.log3("Couldn't parse mathjs node", j, n);
|
||||||
None;
|
None;
|
||||||
|
@ -50,29 +77,37 @@ module MathJsonToMathJsAdt = {
|
||||||
module MathAdtToDistDst = {
|
module MathAdtToDistDst = {
|
||||||
open MathJsonToMathJsAdt;
|
open MathJsonToMathJsAdt;
|
||||||
|
|
||||||
|
let handleSymbol = sym => {
|
||||||
|
Ok(`Symbol(sym));
|
||||||
|
};
|
||||||
|
|
||||||
module MathAdtCleaner = {
|
module MathAdtCleaner = {
|
||||||
let transformWithSymbol = (f: float, s: string) =>
|
let transformWithSymbol = (f: float, s: string) =>
|
||||||
switch (s) {
|
switch (s) {
|
||||||
| "K"
|
| "K"
|
||||||
| "k" => f *. 1000.
|
| "k" => Some(f *. 1000.)
|
||||||
| "M"
|
| "M"
|
||||||
| "m" => f *. 1000000.
|
| "m" => Some(f *. 1000000.)
|
||||||
| "B"
|
| "B"
|
||||||
| "b" => f *. 1000000000.
|
| "b" => Some(f *. 1000000000.)
|
||||||
| "T"
|
| "T"
|
||||||
| "t" => f *. 1000000000000.
|
| "t" => Some(f *. 1000000000000.)
|
||||||
| _ => f
|
| _ => None
|
||||||
};
|
};
|
||||||
|
|
||||||
let rec run =
|
let rec run =
|
||||||
fun
|
fun
|
||||||
| Fn({name: "multiply", args: [|Value(f), Symbol(s)|]}) =>
|
| Fn({name: "multiply", args: [|Value(f), Symbol(s)|]}) as doNothing =>
|
||||||
Value(transformWithSymbol(f, s))
|
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: "unaryMinus", args: [|Value(f)|]}) => Value((-1.0) *. f)
|
||||||
| Fn({name, args}) => Fn({name, args: args |> E.A.fmap(run)})
|
| Fn({name, args}) => Fn({name, args: args |> E.A.fmap(run)})
|
||||||
| Array(args) => Array(args |> E.A.fmap(run))
|
| Array(args) => Array(args |> E.A.fmap(run))
|
||||||
| Symbol(s) => Symbol(s)
|
| Symbol(s) => Symbol(s)
|
||||||
| Value(v) => Value(v)
|
| 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) =>
|
||||||
Object(
|
Object(
|
||||||
v
|
v
|
||||||
|
@ -82,83 +117,31 @@ module MathAdtToDistDst = {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
let normal:
|
let lognormal = (args, parseArgs, nodeParser) =>
|
||||||
array(arg) => result(ExpressionTypes.ExpressionTree.node, string) =
|
switch (args) {
|
||||||
fun
|
| [|Object(o)|] =>
|
||||||
| [|Value(mean), Value(stdev)|] =>
|
let g = s =>
|
||||||
Ok(`SymbolicDist(`Normal({mean, stdev})))
|
Js.Dict.get(o, s)
|
||||||
| _ => Error("Wrong number of variables in normal distribution");
|
|> E.O.toResult("Variable was empty")
|
||||||
|
|> E.R.bind(_, nodeParser);
|
||||||
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")) {
|
switch (g("mean"), g("stdev"), g("mu"), g("sigma")) {
|
||||||
| (Some(Value(mean)), Some(Value(stdev)), _, _) =>
|
| (Ok(mean), Ok(stdev), _, _) =>
|
||||||
Ok(
|
Ok(
|
||||||
`SymbolicDist(
|
`FunctionCall(("lognormalFromMeanAndStdDev", [|mean, stdev|])),
|
||||||
SymbolicDist.Lognormal.fromMeanAndStdev(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))
|
||||||
)
|
)
|
||||||
| (_, _, 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 => {
|
|
||||||
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");
|
|
||||||
|
|
||||||
let multiModal =
|
let multiModal =
|
||||||
(
|
(
|
||||||
|
@ -166,15 +149,6 @@ module MathAdtToDistDst = {
|
||||||
weights: option(array(float)),
|
weights: option(array(float)),
|
||||||
) => {
|
) => {
|
||||||
let weights = weights |> E.O.default([||]);
|
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 firstWithError = args |> Belt.Array.getBy(_, Belt.Result.isError);
|
||||||
let withoutErrors = args |> E.A.fmap(E.R.toOption) |> E.A.O.concatSomes;
|
let withoutErrors = args |> E.A.fmap(E.R.toOption) |> E.A.O.concatSomes;
|
||||||
|
|
||||||
|
@ -203,62 +177,42 @@ module MathAdtToDistDst = {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// let arrayParser =
|
// Error("Dotwise exponentiation needs two operands")
|
||||||
// (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")
|
|
||||||
// };
|
|
||||||
// };
|
|
||||||
|
|
||||||
let operationParser =
|
let operationParser =
|
||||||
(
|
(
|
||||||
name: string,
|
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 toOkAlgebraic = r => Ok(`AlgebraicCombination(r));
|
||||||
let toOkPointwise = r => Ok(`PointwiseCombination(r));
|
let toOkPointwise = r => Ok(`PointwiseCombination(r));
|
||||||
let toOkTruncate = r => Ok(`Truncate(r));
|
let toOkTruncate = r => Ok(`Truncate(r));
|
||||||
let toOkFloatFromDist = r => Ok(`FloatFromDist(r));
|
let toOkFloatFromDist = r => Ok(`FloatFromDist(r));
|
||||||
|
args
|
||||||
|
|> E.R.bind(_, args => {
|
||||||
switch (name, args) {
|
switch (name, args) {
|
||||||
| ("add", [|Ok(l), Ok(r)|]) => toOkAlgebraic((`Add, l, r))
|
| ("add", [|l, r|]) => toOkAlgebraic((`Add, l, r))
|
||||||
| ("add", _) => Error("Addition needs two operands")
|
| ("add", _) => Error("Addition needs two operands")
|
||||||
| ("subtract", [|Ok(l), Ok(r)|]) => toOkAlgebraic((`Subtract, l, r))
|
| ("subtract", [|l, r|]) => toOkAlgebraic((`Subtract, l, r))
|
||||||
| ("subtract", _) => Error("Subtraction needs two operands")
|
| ("subtract", _) => Error("Subtraction needs two operands")
|
||||||
| ("multiply", [|Ok(l), Ok(r)|]) => toOkAlgebraic((`Multiply, l, r))
|
| ("multiply", [|l, r|]) => toOkAlgebraic((`Multiply, l, r))
|
||||||
| ("multiply", _) => Error("Multiplication needs two operands")
|
| ("multiply", _) => Error("Multiplication needs two operands")
|
||||||
| ("dotMultiply", [|Ok(l), Ok(r)|]) => toOkPointwise((`Multiply, l, r))
|
| ("pow", [|l,r|]) => toOkAlgebraic((`Exponentiate, l, r))
|
||||||
|
| ("pow", _) => Error("Exponentiation needs two operands")
|
||||||
|
| ("dotMultiply", [|l, r|]) => toOkPointwise((`Multiply, l, r))
|
||||||
| ("dotMultiply", _) =>
|
| ("dotMultiply", _) =>
|
||||||
Error("Dotwise multiplication needs two operands")
|
Error("Dotwise multiplication needs two operands")
|
||||||
| ("rightLogShift", [|Ok(l), Ok(r)|]) => toOkPointwise((`Add, l, r))
|
| ("rightLogShift", [|l, r|]) => toOkPointwise((`Add, l, r))
|
||||||
| ("rightLogShift", _) => Error("Dotwise addition needs two operands")
|
| ("rightLogShift", _) =>
|
||||||
| ("divide", [|Ok(l), Ok(r)|]) => toOkAlgebraic((`Divide, l, r))
|
Error("Dotwise addition needs two operands")
|
||||||
|
| ("divide", [|l, r|]) => toOkAlgebraic((`Divide, l, r))
|
||||||
| ("divide", _) => Error("Division needs two operands")
|
| ("divide", _) => Error("Division needs two operands")
|
||||||
| ("pow", _) => Error("Exponentiation is not yet supported.")
|
| ("leftTruncate", [|d, `SymbolicDist(`Float(lc))|]) =>
|
||||||
| ("leftTruncate", [|Ok(d), Ok(`SymbolicDist(`Float(lc)))|]) =>
|
|
||||||
toOkTruncate((Some(lc), None, d))
|
toOkTruncate((Some(lc), None, d))
|
||||||
| ("leftTruncate", _) =>
|
| ("leftTruncate", _) =>
|
||||||
Error("leftTruncate needs two arguments: the expression and the cutoff")
|
Error(
|
||||||
| ("rightTruncate", [|Ok(d), Ok(`SymbolicDist(`Float(rc)))|]) =>
|
"leftTruncate needs two arguments: the expression and the cutoff",
|
||||||
|
)
|
||||||
|
| ("rightTruncate", [|d, `SymbolicDist(`Float(rc))|]) =>
|
||||||
toOkTruncate((None, Some(rc), d))
|
toOkTruncate((None, Some(rc), d))
|
||||||
| ("rightTruncate", _) =>
|
| ("rightTruncate", _) =>
|
||||||
Error(
|
Error(
|
||||||
|
@ -266,38 +220,32 @@ module MathAdtToDistDst = {
|
||||||
)
|
)
|
||||||
| (
|
| (
|
||||||
"truncate",
|
"truncate",
|
||||||
[|
|
[|d, `SymbolicDist(`Float(lc)), `SymbolicDist(`Float(rc))|],
|
||||||
Ok(d),
|
|
||||||
Ok(`SymbolicDist(`Float(lc))),
|
|
||||||
Ok(`SymbolicDist(`Float(rc))),
|
|
||||||
|],
|
|
||||||
) =>
|
) =>
|
||||||
toOkTruncate((Some(lc), Some(rc), d))
|
toOkTruncate((Some(lc), Some(rc), d))
|
||||||
| ("truncate", _) =>
|
| ("truncate", _) =>
|
||||||
Error("truncate needs three arguments: the expression and both cutoffs")
|
Error(
|
||||||
| ("pdf", [|Ok(d), Ok(`SymbolicDist(`Float(v)))|]) =>
|
"truncate needs three arguments: the expression and both cutoffs",
|
||||||
|
)
|
||||||
|
| ("pdf", [|d, `SymbolicDist(`Float(v))|]) =>
|
||||||
toOkFloatFromDist((`Pdf(v), d))
|
toOkFloatFromDist((`Pdf(v), d))
|
||||||
| ("cdf", [|Ok(d), Ok(`SymbolicDist(`Float(v)))|]) =>
|
| ("cdf", [|d, `SymbolicDist(`Float(v))|]) =>
|
||||||
toOkFloatFromDist((`Cdf(v), d))
|
toOkFloatFromDist((`Cdf(v), d))
|
||||||
| ("inv", [|Ok(d), Ok(`SymbolicDist(`Float(v)))|]) =>
|
| ("inv", [|d, `SymbolicDist(`Float(v))|]) =>
|
||||||
toOkFloatFromDist((`Inv(v), d))
|
toOkFloatFromDist((`Inv(v), d))
|
||||||
| ("mean", [|Ok(d)|]) => toOkFloatFromDist((`Mean, d))
|
| ("mean", [|d|]) => toOkFloatFromDist((`Mean, d))
|
||||||
| ("sample", [|Ok(d)|]) => toOkFloatFromDist((`Sample, d))
|
| ("sample", [|d|]) => toOkFloatFromDist((`Sample, d))
|
||||||
| _ => Error("This type not currently supported")
|
| _ => Error("This type not currently supported")
|
||||||
};
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
let functionParser = (nodeParser, name, args) => {
|
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) {
|
switch (name) {
|
||||||
| "normal" => normal(args)
|
| "lognormal" => lognormal(args, parseArgs, nodeParser)
|
||||||
| "lognormal" => lognormal(args)
|
|
||||||
| "uniform" => uniform(args)
|
|
||||||
| "beta" => beta(args)
|
|
||||||
| "to" => to_(args)
|
|
||||||
| "exponential" => exponential(args)
|
|
||||||
| "cauchy" => cauchy(args)
|
|
||||||
| "triangular" => triangular(args)
|
|
||||||
| "mm" =>
|
| "mm" =>
|
||||||
let weights =
|
let weights =
|
||||||
args
|
args
|
||||||
|
@ -338,27 +286,55 @@ module MathAdtToDistDst = {
|
||||||
| "sample"
|
| "sample"
|
||||||
| "cdf"
|
| "cdf"
|
||||||
| "pdf" => operationParser(name, parseArgs())
|
| "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
|
fun
|
||||||
| Value(f) => Ok(`SymbolicDist(`Float(f)))
|
| Value(f) => Ok(`SymbolicDist(`Float(f)))
|
||||||
|
| Symbol(sym) => Ok(`Symbol(sym))
|
||||||
| Fn({name, args}) => functionParser(nodeParser, name, args)
|
| Fn({name, args}) => functionParser(nodeParser, name, args)
|
||||||
| _ => {
|
| _ => {
|
||||||
Error("This type not currently supported");
|
Error("This type not currently supported")
|
||||||
};
|
};
|
||||||
|
|
||||||
let topLevel =
|
// | FunctionAssignment({name, args, expression}) => {
|
||||||
fun
|
// let evaluatedExpression = run(expression);
|
||||||
| Value(_) as r => nodeParser(r)
|
// `Function(_ => Ok(evaluatedExpression));
|
||||||
| Fn(_) as r => nodeParser(r)
|
// }
|
||||||
|
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")
|
| Array(_) => Error("Array not valid as top level")
|
||||||
| Symbol(_) => Error("Symbol not valid as top level")
|
| Symbol(s) => handleSymbol(s) |> E.R.fmap(r => [|`Expression(r)|])
|
||||||
| Object(_) => Error("Object not valid as top level");
|
| 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;
|
r |> MathAdtCleaner.run |> topLevel;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -369,7 +345,7 @@ module MathAdtToDistDst = {
|
||||||
*/
|
*/
|
||||||
let pointwiseToRightLogShift = Js.String.replaceByRe([%re "/\.\+/g"], ">>>");
|
let pointwiseToRightLogShift = Js.String.replaceByRe([%re "/\.\+/g"], ">>>");
|
||||||
|
|
||||||
let fromString = str => {
|
let fromString2 = str => {
|
||||||
/* We feed the user-typed string into Mathjs.parseMath,
|
/* We feed the user-typed string into Mathjs.parseMath,
|
||||||
which returns a JSON with (hopefully) a single-element array.
|
which returns a JSON with (hopefully) a single-element array.
|
||||||
This array element is the top-level node of a nested-object tree
|
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);
|
let value = E.R.bind(mathJsParse, MathAdtToDistDst.run);
|
||||||
value;
|
value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let fromString = str => {
|
||||||
|
fromString2(str);
|
||||||
|
};
|
||||||
|
|
|
@ -7,6 +7,7 @@ module Algebraic = {
|
||||||
| `Add => (+.)
|
| `Add => (+.)
|
||||||
| `Subtract => (-.)
|
| `Subtract => (-.)
|
||||||
| `Multiply => ( *. )
|
| `Multiply => ( *. )
|
||||||
|
| `Exponentiate => ( ** )
|
||||||
| `Divide => (/.);
|
| `Divide => (/.);
|
||||||
|
|
||||||
let applyFn = (t, f1, f2) => {
|
let applyFn = (t, f1, f2) => {
|
||||||
|
@ -21,6 +22,7 @@ module Algebraic = {
|
||||||
| `Add => "+"
|
| `Add => "+"
|
||||||
| `Subtract => "-"
|
| `Subtract => "-"
|
||||||
| `Multiply => "*"
|
| `Multiply => "*"
|
||||||
|
| `Exponentiate => ( "**" )
|
||||||
| `Divide => "/";
|
| `Divide => "/";
|
||||||
|
|
||||||
let format = (a, b, c) => b ++ " " ++ toString(a) ++ " " ++ c;
|
let format = (a, b, c) => b ++ " " ++ toString(a) ++ " " ++ c;
|
||||||
|
|
5
src/distPlus/expressionTree/Program.re
Normal file
5
src/distPlus/expressionTree/Program.re
Normal file
|
@ -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 =>
|
||||||
|
// )
|
|
@ -60,17 +60,11 @@ let combineShapesUsingSampling =
|
||||||
let shape =
|
let shape =
|
||||||
samples
|
samples
|
||||||
|> E.O.fmap(
|
|> E.O.fmap(
|
||||||
Samples.T.fromSamples(
|
SamplesToShape.fromSamples(
|
||||||
~samplingInputs={
|
~samplingInputs=evaluationParams.samplingInputs,
|
||||||
sampleCount:
|
|
||||||
Some(evaluationParams.samplingInputs.sampleCount),
|
|
||||||
outputXYPoints:
|
|
||||||
Some(evaluationParams.samplingInputs.outputXYPoints),
|
|
||||||
kernelWidth: evaluationParams.samplingInputs.kernelWidth,
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|> E.O.bind(_, (r) => r.shape)
|
|> E.O.bind(_, r => r.shape)
|
||||||
|> E.O.toResult("No response");
|
|> E.O.toResult("No response");
|
||||||
shape |> E.R.fmap(r => `Normalize(`RenderedDist(r)));
|
shape |> E.R.fmap(r => `Normalize(`RenderedDist(r)));
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,23 +1,147 @@
|
||||||
let run = (inputs: RenderTypes.DistPlusRenderer.inputs) => {
|
// TODO: This setup is more confusing than it should be, there's more work to do in cleanup here.
|
||||||
let toDist = shape =>
|
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(
|
DistPlus.make(
|
||||||
~shape,
|
~shape,
|
||||||
~domain=inputs.distPlusIngredients.domain,
|
~domain=inputs.distPlusIngredients.domain,
|
||||||
~unit=inputs.distPlusIngredients.unit,
|
~unit=inputs.distPlusIngredients.unit,
|
||||||
~guesstimatorString=Some(inputs.distPlusIngredients.guesstimatorString),
|
~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));
|
||||||
|
};
|
||||||
|
|
0
src/distPlus/renderers/ProgramRunner.re
Normal file
0
src/distPlus/renderers/ProgramRunner.re
Normal file
|
@ -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};
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -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);
|
|
||||||
};
|
|
|
@ -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, ());
|
|
||||||
};
|
|
||||||
};
|
|
164
src/distPlus/renderers/samplesRenderer/SamplesToShape.re
Normal file
164
src/distPlus/renderers/samplesRenderer/SamplesToShape.re
Normal file
|
@ -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, ());
|
||||||
|
};
|
|
@ -2,6 +2,12 @@ open SymbolicTypes;
|
||||||
|
|
||||||
module Exponential = {
|
module Exponential = {
|
||||||
type t = exponential;
|
type t = exponential;
|
||||||
|
let make = (rate:float): symbolicDist =>
|
||||||
|
`Exponential(
|
||||||
|
{
|
||||||
|
rate:rate
|
||||||
|
},
|
||||||
|
);
|
||||||
let pdf = (x, t: t) => Jstat.exponential##pdf(x, t.rate);
|
let pdf = (x, t: t) => Jstat.exponential##pdf(x, t.rate);
|
||||||
let cdf = (x, t: t) => Jstat.exponential##cdf(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);
|
let inv = (p, t: t) => Jstat.exponential##inv(p, t.rate);
|
||||||
|
@ -12,6 +18,7 @@ module Exponential = {
|
||||||
|
|
||||||
module Cauchy = {
|
module Cauchy = {
|
||||||
type t = 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 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 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);
|
let inv = (p, t: t) => Jstat.cauchy##inv(p, t.local, t.scale);
|
||||||
|
@ -22,6 +29,10 @@ module Cauchy = {
|
||||||
|
|
||||||
module Triangular = {
|
module Triangular = {
|
||||||
type t = 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 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 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);
|
let inv = (p, t: t) => Jstat.triangular##inv(p, t.low, t.high, t.medium);
|
||||||
|
@ -32,6 +43,7 @@ module Triangular = {
|
||||||
|
|
||||||
module Normal = {
|
module Normal = {
|
||||||
type t = 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 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);
|
let cdf = (x, t: t) => Jstat.normal##cdf(x, t.mean, t.stdev);
|
||||||
|
|
||||||
|
@ -75,6 +87,7 @@ module Normal = {
|
||||||
|
|
||||||
module Beta = {
|
module Beta = {
|
||||||
type t = 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 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 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);
|
let inv = (p, t: t) => Jstat.beta##inv(p, t.alpha, t.beta);
|
||||||
|
@ -85,6 +98,7 @@ module Beta = {
|
||||||
|
|
||||||
module Lognormal = {
|
module Lognormal = {
|
||||||
type t = 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 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 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);
|
let inv = (p, t: t) => Jstat.lognormal##inv(p, t.mu, t.sigma);
|
||||||
|
@ -131,6 +145,7 @@ module Lognormal = {
|
||||||
|
|
||||||
module Uniform = {
|
module Uniform = {
|
||||||
type t = 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 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 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);
|
let inv = (p, t: t) => Jstat.uniform##inv(p, t.low, t.high);
|
||||||
|
@ -146,6 +161,7 @@ module Uniform = {
|
||||||
|
|
||||||
module Float = {
|
module Float = {
|
||||||
type t = float;
|
type t = float;
|
||||||
|
let make = t => `Float(t);
|
||||||
let pdf = (x, t: t) => x == t ? 1.0 : 0.0;
|
let pdf = (x, t: t) => x == t ? 1.0 : 0.0;
|
||||||
let cdf = (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;
|
let inv = (p, t: t) => p < t ? 0.0 : 1.0;
|
||||||
|
@ -317,13 +333,14 @@ module T = {
|
||||||
switch (d) {
|
switch (d) {
|
||||||
| `Float(v) =>
|
| `Float(v) =>
|
||||||
Discrete(
|
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 xs = interpolateXs(~xSelection=`ByWeight, d, sampleCount);
|
||||||
let ys = xs |> E.A.fmap(x => pdf(x, d));
|
let ys = xs |> E.A.fmap(x => pdf(x, d));
|
||||||
Continuous(
|
Continuous(Continuous.make(~integralSumCache=Some(1.0), {xs, ys}));
|
||||||
Continuous.make(~integralSumCache=Some(1.0), {xs, ys}),
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,12 +17,10 @@ let propValue = (t: Prop.Value.t) => {
|
||||||
switch (t) {
|
switch (t) {
|
||||||
| SelectSingle(r) => r |> ReasonReact.string
|
| SelectSingle(r) => r |> ReasonReact.string
|
||||||
| ConditionalArray(r) => "Array" |> ReasonReact.string
|
| ConditionalArray(r) => "Array" |> ReasonReact.string
|
||||||
| DistPlusIngredients((r: RenderTypes.DistPlusRenderer.ingredients)) =>
|
| DistPlusIngredients((r: DistPlusRenderer.Inputs.ingredients)) =>
|
||||||
let newDistribution =
|
let newDistribution =
|
||||||
RenderTypes.DistPlusRenderer.make(
|
DistPlusRenderer.Inputs.make(
|
||||||
~distPlusIngredients=r,
|
~distPlusIngredients=r,
|
||||||
~recommendedLength=10000,
|
|
||||||
~shouldDownsample=true,
|
|
||||||
(),
|
(),
|
||||||
)
|
)
|
||||||
|> DistPlusRenderer.run;
|
|> DistPlusRenderer.run;
|
||||||
|
|
|
@ -9,7 +9,7 @@ module Value = {
|
||||||
| DateTime(MomentRe.Moment.t)
|
| DateTime(MomentRe.Moment.t)
|
||||||
| FloatPoint(float)
|
| FloatPoint(float)
|
||||||
| Probability(float)
|
| Probability(float)
|
||||||
| DistPlusIngredients(RenderTypes.DistPlusRenderer.ingredients)
|
| DistPlusIngredients(DistPlusRenderer.Inputs.ingredients)
|
||||||
| ConditionalArray(array(conditional))
|
| ConditionalArray(array(conditional))
|
||||||
| FloatCdf(string);
|
| FloatCdf(string);
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ module ValueCluster = {
|
||||||
[ | `combination(range(MomentRe.Moment.t)) | `item(string)],
|
[ | `combination(range(MomentRe.Moment.t)) | `item(string)],
|
||||||
)
|
)
|
||||||
| Probability([ | `item(string)])
|
| Probability([ | `item(string)])
|
||||||
| DistPlusIngredients([ | `item(RenderTypes.DistPlusRenderer.ingredients)])
|
| DistPlusIngredients([ | `item(DistPlusRenderer.Inputs.ingredients)])
|
||||||
| ConditionalArray([ | `item(array(conditional))])
|
| ConditionalArray([ | `item(array(conditional))])
|
||||||
| FloatCdf([ | `item(string)]);
|
| FloatCdf([ | `item(string)]);
|
||||||
};
|
};
|
||||||
|
|
|
@ -110,7 +110,7 @@ module Model = {
|
||||||
// TODO: Fixe number that integral is calculated for
|
// TODO: Fixe number that integral is calculated for
|
||||||
let getGlobalCatastropheChance = dateTime => {
|
let getGlobalCatastropheChance = dateTime => {
|
||||||
GlobalCatastrophe.makeI(MomentRe.momentNow())
|
GlobalCatastrophe.makeI(MomentRe.momentNow())
|
||||||
|> RenderTypes.DistPlusRenderer.make(~distPlusIngredients=_, ())
|
|> DistPlusRenderer.Inputs.make(~distPlusIngredients=_, ())
|
||||||
|> DistPlusRenderer.run
|
|> DistPlusRenderer.run
|
||||||
|> E.R.bind(_, r =>
|
|> E.R.bind(_, r =>
|
||||||
r
|
r
|
||||||
|
@ -157,7 +157,7 @@ module Model = {
|
||||||
};
|
};
|
||||||
|
|
||||||
let distPlusIngredients =
|
let distPlusIngredients =
|
||||||
RenderTypes.DistPlusRenderer.Ingredients.make(
|
DistPlusRenderer.Inputs.Ingredients.make(
|
||||||
~guesstimatorString=str,
|
~guesstimatorString=str,
|
||||||
~domain=Complete,
|
~domain=Complete,
|
||||||
~unit=UnspecifiedDistribution,
|
~unit=UnspecifiedDistribution,
|
||||||
|
@ -167,7 +167,7 @@ module Model = {
|
||||||
|
|
||||||
| CHANCE_OF_EXISTENCE =>
|
| CHANCE_OF_EXISTENCE =>
|
||||||
Prop.Value.DistPlusIngredients(
|
Prop.Value.DistPlusIngredients(
|
||||||
RenderTypes.DistPlusRenderer.Ingredients.make(
|
DistPlusRenderer.Inputs.Ingredients.make(
|
||||||
~guesstimatorString=
|
~guesstimatorString=
|
||||||
GuesstimatorDist.min(
|
GuesstimatorDist.min(
|
||||||
GlobalCatastrophe.guesstimatorString,
|
GlobalCatastrophe.guesstimatorString,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
let guesstimatorString = "uniform(1, 100)";
|
let guesstimatorString = "uniform(1, 100)";
|
||||||
|
|
||||||
let makeI = (currentDateTime: MomentRe.Moment.t) => {
|
let makeI = (currentDateTime: MomentRe.Moment.t) => {
|
||||||
RenderTypes.DistPlusRenderer.Ingredients.make(
|
DistPlusRenderer.Inputs.Ingredients.make(
|
||||||
~guesstimatorString,
|
~guesstimatorString,
|
||||||
~unit=TimeDistribution({zero: currentDateTime, unit: `years}),
|
~unit=TimeDistribution({zero: currentDateTime, unit: `years}),
|
||||||
~domain=RightLimited({xPoint: 300.0, excludingProbabilityMass: 0.3}),
|
~domain=RightLimited({xPoint: 300.0, excludingProbabilityMass: 0.3}),
|
||||||
|
|
|
@ -2,7 +2,7 @@ let guesstimatorString = age =>
|
||||||
GuesstimatorDist.normal(72.0 -. age, 5.0 -. age *. 0.01);
|
GuesstimatorDist.normal(72.0 -. age, 5.0 -. age *. 0.01);
|
||||||
|
|
||||||
let makeI = (age: float) => {
|
let makeI = (age: float) => {
|
||||||
RenderTypes.DistPlusRenderer.Ingredients.make(
|
DistPlusRenderer.Inputs.Ingredients.make(
|
||||||
~guesstimatorString=guesstimatorString(age),
|
~guesstimatorString=guesstimatorString(age),
|
||||||
~unit=TimeDistribution({zero: MomentRe.momentNow(), unit: `years}),
|
~unit=TimeDistribution({zero: MomentRe.momentNow(), unit: `years}),
|
||||||
~domain=RightLimited({xPoint: 300.0, excludingProbabilityMass: 0.3}),
|
~domain=RightLimited({xPoint: 300.0, excludingProbabilityMass: 0.3}),
|
||||||
|
|
Loading…
Reference in New Issue
Block a user