It compiles!
This commit is contained in:
parent
bd528571af
commit
dc1ec1bb86
|
@ -24,7 +24,7 @@ let makeTestCloseEquality = (~only=false, str, item1, item2, ~digits) =>
|
||||||
describe("Shape", () => {
|
describe("Shape", () => {
|
||||||
describe("Continuous", () => {
|
describe("Continuous", () => {
|
||||||
open Distributions.Continuous;
|
open Distributions.Continuous;
|
||||||
let continuous = make(`Linear, shape);
|
let continuous = make(`Linear, shape, None);
|
||||||
makeTest("minX", T.minX(continuous), 1.0);
|
makeTest("minX", T.minX(continuous), 1.0);
|
||||||
makeTest("maxX", T.maxX(continuous), 8.0);
|
makeTest("maxX", T.maxX(continuous), 8.0);
|
||||||
makeTest(
|
makeTest(
|
||||||
|
@ -57,7 +57,7 @@ describe("Shape", () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
describe("when Stepwise", () => {
|
describe("when Stepwise", () => {
|
||||||
let continuous = make(`Stepwise, shape);
|
let continuous = make(`Stepwise, shape, None);
|
||||||
makeTest(
|
makeTest(
|
||||||
"at 4.0",
|
"at 4.0",
|
||||||
T.xToY(4., continuous),
|
T.xToY(4., continuous),
|
||||||
|
@ -89,7 +89,7 @@ describe("Shape", () => {
|
||||||
"toLinear",
|
"toLinear",
|
||||||
{
|
{
|
||||||
let continuous =
|
let continuous =
|
||||||
make(`Stepwise, {xs: [|1., 4., 8.|], ys: [|0.1, 5., 1.0|]});
|
make(`Stepwise, {xs: [|1., 4., 8.|], ys: [|0.1, 5., 1.0|]}, None);
|
||||||
continuous |> toLinear |> E.O.fmap(getShape);
|
continuous |> toLinear |> E.O.fmap(getShape);
|
||||||
},
|
},
|
||||||
Some({
|
Some({
|
||||||
|
@ -100,7 +100,7 @@ describe("Shape", () => {
|
||||||
makeTest(
|
makeTest(
|
||||||
"toLinear",
|
"toLinear",
|
||||||
{
|
{
|
||||||
let continuous = make(`Stepwise, {xs: [|0.0|], ys: [|0.3|]});
|
let continuous = make(`Stepwise, {xs: [|0.0|], ys: [|0.3|]}, None);
|
||||||
continuous |> toLinear |> E.O.fmap(getShape);
|
continuous |> toLinear |> E.O.fmap(getShape);
|
||||||
},
|
},
|
||||||
Some({xs: [|0.0|], ys: [|0.3|]}),
|
Some({xs: [|0.0|], ys: [|0.3|]}),
|
||||||
|
@ -123,7 +123,7 @@ describe("Shape", () => {
|
||||||
makeTest(
|
makeTest(
|
||||||
"integralEndY",
|
"integralEndY",
|
||||||
continuous
|
continuous
|
||||||
|> T.scaleToIntegralSum(~intendedSum=1.0)
|
|> T.normalize //scaleToIntegralSum(~intendedSum=1.0)
|
||||||
|> T.Integral.sum(~cache=None),
|
|> T.Integral.sum(~cache=None),
|
||||||
1.0,
|
1.0,
|
||||||
);
|
);
|
||||||
|
@ -135,12 +135,12 @@ describe("Shape", () => {
|
||||||
xs: [|1., 4., 8.|],
|
xs: [|1., 4., 8.|],
|
||||||
ys: [|0.3, 0.5, 0.2|],
|
ys: [|0.3, 0.5, 0.2|],
|
||||||
};
|
};
|
||||||
let discrete = shape;
|
let discrete = make(shape, None);
|
||||||
makeTest("minX", T.minX(discrete), 1.0);
|
makeTest("minX", T.minX(discrete), 1.0);
|
||||||
makeTest("maxX", T.maxX(discrete), 8.0);
|
makeTest("maxX", T.maxX(discrete), 8.0);
|
||||||
makeTest(
|
makeTest(
|
||||||
"mapY",
|
"mapY",
|
||||||
T.mapY(r => r *. 2.0, discrete) |> (r => r.ys),
|
T.mapY(r => r *. 2.0, discrete) |> (r => getShape(r).ys),
|
||||||
[|0.6, 1.0, 0.4|],
|
[|0.6, 1.0, 0.4|],
|
||||||
);
|
);
|
||||||
makeTest(
|
makeTest(
|
||||||
|
@ -160,19 +160,22 @@ describe("Shape", () => {
|
||||||
);
|
);
|
||||||
makeTest(
|
makeTest(
|
||||||
"scaleBy",
|
"scaleBy",
|
||||||
T.scaleBy(~scale=4.0, discrete),
|
scaleBy(~scale=4.0, discrete),
|
||||||
{xs: [|1., 4., 8.|], ys: [|1.2, 2.0, 0.8|]},
|
make({xs: [|1., 4., 8.|], ys: [|1.2, 2.0, 0.8|]}, None),
|
||||||
);
|
);
|
||||||
makeTest(
|
makeTest(
|
||||||
"scaleToIntegralSum",
|
"normalize, then scale by 4.0",
|
||||||
T.scaleToIntegralSum(~intendedSum=4.0, discrete),
|
discrete
|
||||||
{xs: [|1., 4., 8.|], ys: [|1.2, 2.0, 0.8|]},
|
|> T.normalize
|
||||||
|
|> scaleBy(~scale=4.0),
|
||||||
|
make({xs: [|1., 4., 8.|], ys: [|1.2, 2.0, 0.8|]}, None),
|
||||||
);
|
);
|
||||||
makeTest(
|
makeTest(
|
||||||
"scaleToIntegralSum: back and forth",
|
"scaleToIntegralSum: back and forth",
|
||||||
discrete
|
discrete
|
||||||
|> T.scaleToIntegralSum(~intendedSum=4.0)
|
|> T.normalize
|
||||||
|> T.scaleToIntegralSum(~intendedSum=1.0),
|
|> scaleBy(~scale=4.0)
|
||||||
|
|> T.normalize,
|
||||||
discrete,
|
discrete,
|
||||||
);
|
);
|
||||||
makeTest(
|
makeTest(
|
||||||
|
@ -181,12 +184,13 @@ describe("Shape", () => {
|
||||||
Distributions.Continuous.make(
|
Distributions.Continuous.make(
|
||||||
`Stepwise,
|
`Stepwise,
|
||||||
{xs: [|1., 4., 8.|], ys: [|0.3, 0.8, 1.0|]},
|
{xs: [|1., 4., 8.|], ys: [|0.3, 0.8, 1.0|]},
|
||||||
|
None
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
makeTest(
|
makeTest(
|
||||||
"integral with 1 element",
|
"integral with 1 element",
|
||||||
T.Integral.get(~cache=None, {xs: [|0.0|], ys: [|1.0|]}),
|
T.Integral.get(~cache=None, Distributions.Discrete.make({xs: [|0.0|], ys: [|1.0|]}, None)),
|
||||||
Distributions.Continuous.make(`Stepwise, {xs: [|0.0|], ys: [|1.0|]}),
|
Distributions.Continuous.make(`Stepwise, {xs: [|0.0|], ys: [|1.0|]}, None),
|
||||||
);
|
);
|
||||||
makeTest(
|
makeTest(
|
||||||
"integralXToY",
|
"integralXToY",
|
||||||
|
@ -205,27 +209,22 @@ describe("Shape", () => {
|
||||||
|
|
||||||
describe("Mixed", () => {
|
describe("Mixed", () => {
|
||||||
open Distributions.Mixed;
|
open Distributions.Mixed;
|
||||||
let discrete: DistTypes.xyShape = {
|
let discreteShape: DistTypes.xyShape = {
|
||||||
xs: [|1., 4., 8.|],
|
xs: [|1., 4., 8.|],
|
||||||
ys: [|0.3, 0.5, 0.2|],
|
ys: [|0.3, 0.5, 0.2|],
|
||||||
};
|
};
|
||||||
|
let discrete = Distributions.Discrete.make(discreteShape, None);
|
||||||
let continuous =
|
let continuous =
|
||||||
Distributions.Continuous.make(
|
Distributions.Continuous.make(
|
||||||
`Linear,
|
`Linear,
|
||||||
{xs: [|3., 7., 14.|], ys: [|0.058, 0.082, 0.124|]},
|
{xs: [|3., 7., 14.|], ys: [|0.058, 0.082, 0.124|]},
|
||||||
|
None
|
||||||
)
|
)
|
||||||
|> Distributions.Continuous.T.scaleToIntegralSum(~intendedSum=1.0);
|
|> Distributions.Continuous.T.normalize; //scaleToIntegralSum(~intendedSum=1.0);
|
||||||
let mixed =
|
let mixed = Distributions.Mixed.make(
|
||||||
MixedShapeBuilder.build(
|
|
||||||
~continuous,
|
~continuous,
|
||||||
~discrete,
|
~discrete,
|
||||||
~assumptions={
|
);
|
||||||
continuous: ADDS_TO_CORRECT_PROBABILITY,
|
|
||||||
discrete: ADDS_TO_CORRECT_PROBABILITY,
|
|
||||||
discreteProbabilityMass: Some(0.5),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|> E.O.toExn("");
|
|
||||||
makeTest("minX", T.minX(mixed), 1.0);
|
makeTest("minX", T.minX(mixed), 1.0);
|
||||||
makeTest("maxX", T.maxX(mixed), 14.0);
|
makeTest("maxX", T.maxX(mixed), 14.0);
|
||||||
makeTest(
|
makeTest(
|
||||||
|
@ -243,9 +242,9 @@ describe("Shape", () => {
|
||||||
0.24775224775224775,
|
0.24775224775224775,
|
||||||
|],
|
|],
|
||||||
},
|
},
|
||||||
|
None
|
||||||
),
|
),
|
||||||
~discrete={xs: [|1., 4., 8.|], ys: [|0.6, 1.0, 0.4|]},
|
~discrete=Distributions.Discrete.make({xs: [|1., 4., 8.|], ys: [|0.6, 1.0, 0.4|]}, None)
|
||||||
~discreteProbabilityMassFraction=0.5,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
makeTest(
|
makeTest(
|
||||||
|
@ -266,7 +265,7 @@ describe("Shape", () => {
|
||||||
makeTest("integralEndY", T.Integral.sum(~cache=None, mixed), 1.0);
|
makeTest("integralEndY", T.Integral.sum(~cache=None, mixed), 1.0);
|
||||||
makeTest(
|
makeTest(
|
||||||
"scaleBy",
|
"scaleBy",
|
||||||
T.scaleBy(~scale=2.0, mixed),
|
Distributions.Mixed.scaleBy(~scale=2.0, mixed),
|
||||||
Distributions.Mixed.make(
|
Distributions.Mixed.make(
|
||||||
~continuous=
|
~continuous=
|
||||||
Distributions.Continuous.make(
|
Distributions.Continuous.make(
|
||||||
|
@ -279,9 +278,9 @@ describe("Shape", () => {
|
||||||
0.24775224775224775,
|
0.24775224775224775,
|
||||||
|],
|
|],
|
||||||
},
|
},
|
||||||
|
None
|
||||||
),
|
),
|
||||||
~discrete={xs: [|1., 4., 8.|], ys: [|0.6, 1.0, 0.4|]},
|
~discrete=Distributions.Discrete.make({xs: [|1., 4., 8.|], ys: [|0.6, 1.0, 0.4|]}, None),
|
||||||
~discreteProbabilityMassFraction=0.5,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
makeTest(
|
makeTest(
|
||||||
|
@ -302,34 +301,31 @@ describe("Shape", () => {
|
||||||
0.6913122927072927,
|
0.6913122927072927,
|
||||||
1.0,
|
1.0,
|
||||||
|],
|
|],
|
||||||
},
|
},
|
||||||
|
None,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Distplus", () => {
|
describe("Distplus", () => {
|
||||||
open Distributions.DistPlus;
|
open Distributions.DistPlus;
|
||||||
let discrete: DistTypes.xyShape = {
|
let discreteShape: DistTypes.xyShape = {
|
||||||
xs: [|1., 4., 8.|],
|
xs: [|1., 4., 8.|],
|
||||||
ys: [|0.3, 0.5, 0.2|],
|
ys: [|0.3, 0.5, 0.2|],
|
||||||
};
|
};
|
||||||
|
let discrete = Distributions.Discrete.make(discreteShape, None);
|
||||||
let continuous =
|
let continuous =
|
||||||
Distributions.Continuous.make(
|
Distributions.Continuous.make(
|
||||||
`Linear,
|
`Linear,
|
||||||
{xs: [|3., 7., 14.|], ys: [|0.058, 0.082, 0.124|]},
|
{xs: [|3., 7., 14.|], ys: [|0.058, 0.082, 0.124|]},
|
||||||
|
None
|
||||||
)
|
)
|
||||||
|> Distributions.Continuous.T.scaleToIntegralSum(~intendedSum=1.0);
|
|> Distributions.Continuous.T.normalize; //scaleToIntegralSum(~intendedSum=1.0);
|
||||||
let mixed =
|
let mixed =
|
||||||
MixedShapeBuilder.build(
|
Distributions.Mixed.make(
|
||||||
~continuous,
|
~continuous,
|
||||||
~discrete,
|
~discrete,
|
||||||
~assumptions={
|
);
|
||||||
continuous: ADDS_TO_CORRECT_PROBABILITY,
|
|
||||||
discrete: ADDS_TO_CORRECT_PROBABILITY,
|
|
||||||
discreteProbabilityMass: Some(0.5),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|> E.O.toExn("");
|
|
||||||
let distPlus =
|
let distPlus =
|
||||||
Distributions.DistPlus.make(
|
Distributions.DistPlus.make(
|
||||||
~shape=Mixed(mixed),
|
~shape=Mixed(mixed),
|
||||||
|
@ -374,6 +370,7 @@ describe("Shape", () => {
|
||||||
1.0,
|
1.0,
|
||||||
|],
|
|],
|
||||||
},
|
},
|
||||||
|
None,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -386,9 +383,9 @@ describe("Shape", () => {
|
||||||
let numSamples = 10000;
|
let numSamples = 10000;
|
||||||
open Distributions.Shape;
|
open Distributions.Shape;
|
||||||
let normal: SymbolicDist.dist = `Normal({mean, stdev});
|
let normal: SymbolicDist.dist = `Normal({mean, stdev});
|
||||||
let normalShape = TreeNode.toShape(numSamples, normal);
|
let normalShape = TreeNode.toShape(numSamples, `DistData(`Symbolic(normal)));
|
||||||
let lognormal = SymbolicDist.Lognormal.fromMeanAndStdev(mean, stdev);
|
let lognormal = SymbolicDist.Lognormal.fromMeanAndStdev(mean, stdev);
|
||||||
let lognormalShape = TreeNode.toShape(numSamples, lognormal);
|
let lognormalShape = TreeNode.toShape(numSamples, `DistData(`Symbolic(lognormal)));
|
||||||
|
|
||||||
makeTestCloseEquality(
|
makeTestCloseEquality(
|
||||||
"Mean of a normal",
|
"Mean of a normal",
|
||||||
|
|
|
@ -17,7 +17,7 @@ module FormConfig = [%lenses
|
||||||
//
|
//
|
||||||
sampleCount: string,
|
sampleCount: string,
|
||||||
outputXYPoints: string,
|
outputXYPoints: string,
|
||||||
truncateTo: string,
|
downsampleTo: string,
|
||||||
kernelWidth: string,
|
kernelWidth: string,
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
@ -25,7 +25,7 @@ module FormConfig = [%lenses
|
||||||
type options = {
|
type options = {
|
||||||
sampleCount: int,
|
sampleCount: int,
|
||||||
outputXYPoints: int,
|
outputXYPoints: int,
|
||||||
truncateTo: option(int),
|
downsampleTo: option(int),
|
||||||
kernelWidth: option(float),
|
kernelWidth: option(float),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@ type inputs = {
|
||||||
samplingInputs: RenderTypes.ShapeRenderer.Sampling.inputs,
|
samplingInputs: RenderTypes.ShapeRenderer.Sampling.inputs,
|
||||||
guesstimatorString: string,
|
guesstimatorString: string,
|
||||||
length: int,
|
length: int,
|
||||||
shouldTruncateSampledDistribution: int,
|
shouldDownsampleSampledDistribution: int,
|
||||||
};
|
};
|
||||||
|
|
||||||
module DemoDist = {
|
module DemoDist = {
|
||||||
|
@ -141,8 +141,8 @@ module DemoDist = {
|
||||||
kernelWidth: options.kernelWidth,
|
kernelWidth: options.kernelWidth,
|
||||||
},
|
},
|
||||||
~distPlusIngredients,
|
~distPlusIngredients,
|
||||||
~shouldTruncate=options.truncateTo |> E.O.isSome,
|
~shouldDownsample=options.downsampleTo |> E.O.isSome,
|
||||||
~recommendedLength=options.truncateTo |> E.O.default(10000),
|
~recommendedLength=options.downsampleTo |> E.O.default(10000),
|
||||||
(),
|
(),
|
||||||
);
|
);
|
||||||
let response = DistPlusRenderer.run(inputs);
|
let response = DistPlusRenderer.run(inputs);
|
||||||
|
@ -182,7 +182,7 @@ let make = () => {
|
||||||
unit: "days",
|
unit: "days",
|
||||||
sampleCount: "30000",
|
sampleCount: "30000",
|
||||||
outputXYPoints: "10000",
|
outputXYPoints: "10000",
|
||||||
truncateTo: "1000",
|
downsampleTo: "1000",
|
||||||
kernelWidth: "5",
|
kernelWidth: "5",
|
||||||
},
|
},
|
||||||
(),
|
(),
|
||||||
|
@ -210,7 +210,7 @@ let make = () => {
|
||||||
let sampleCount = reform.state.values.sampleCount |> Js.Float.fromString;
|
let sampleCount = reform.state.values.sampleCount |> Js.Float.fromString;
|
||||||
let outputXYPoints =
|
let outputXYPoints =
|
||||||
reform.state.values.outputXYPoints |> Js.Float.fromString;
|
reform.state.values.outputXYPoints |> Js.Float.fromString;
|
||||||
let truncateTo = reform.state.values.truncateTo |> Js.Float.fromString;
|
let downsampleTo = reform.state.values.downsampleTo |> Js.Float.fromString;
|
||||||
let kernelWidth = reform.state.values.kernelWidth |> Js.Float.fromString;
|
let kernelWidth = reform.state.values.kernelWidth |> Js.Float.fromString;
|
||||||
|
|
||||||
let domain =
|
let domain =
|
||||||
|
@ -252,20 +252,20 @@ let make = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
let options =
|
let options =
|
||||||
switch (sampleCount, outputXYPoints, truncateTo) {
|
switch (sampleCount, outputXYPoints, downsampleTo) {
|
||||||
| (_, _, _)
|
| (_, _, _)
|
||||||
when
|
when
|
||||||
!Js.Float.isNaN(sampleCount)
|
!Js.Float.isNaN(sampleCount)
|
||||||
&& !Js.Float.isNaN(outputXYPoints)
|
&& !Js.Float.isNaN(outputXYPoints)
|
||||||
&& !Js.Float.isNaN(truncateTo)
|
&& !Js.Float.isNaN(downsampleTo)
|
||||||
&& sampleCount > 10.
|
&& sampleCount > 10.
|
||||||
&& outputXYPoints > 10. =>
|
&& outputXYPoints > 10. =>
|
||||||
Some({
|
Some({
|
||||||
sampleCount: sampleCount |> int_of_float,
|
sampleCount: sampleCount |> int_of_float,
|
||||||
outputXYPoints: outputXYPoints |> int_of_float,
|
outputXYPoints: outputXYPoints |> int_of_float,
|
||||||
truncateTo:
|
downsampleTo:
|
||||||
int_of_float(truncateTo) > 0
|
int_of_float(downsampleTo) > 0
|
||||||
? Some(int_of_float(truncateTo)) : None,
|
? Some(int_of_float(downsampleTo)) : None,
|
||||||
kernelWidth: kernelWidth == 0.0 ? None : Some(kernelWidth),
|
kernelWidth: kernelWidth == 0.0 ? None : Some(kernelWidth),
|
||||||
})
|
})
|
||||||
| _ => None
|
| _ => None
|
||||||
|
@ -287,7 +287,7 @@ let make = () => {
|
||||||
reform.state.values.unit,
|
reform.state.values.unit,
|
||||||
reform.state.values.sampleCount,
|
reform.state.values.sampleCount,
|
||||||
reform.state.values.outputXYPoints,
|
reform.state.values.outputXYPoints,
|
||||||
reform.state.values.truncateTo,
|
reform.state.values.downsampleTo,
|
||||||
reform.state.values.kernelWidth,
|
reform.state.values.kernelWidth,
|
||||||
reloader |> string_of_int,
|
reloader |> string_of_int,
|
||||||
|],
|
|],
|
||||||
|
@ -481,7 +481,7 @@ let make = () => {
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span=4>
|
<Col span=4>
|
||||||
<FieldFloat field=FormConfig.TruncateTo label="Truncate To" />
|
<FieldFloat field=FormConfig.DownsampleTo label="Downsample To" />
|
||||||
</Col>
|
</Col>
|
||||||
<Col span=4>
|
<Col span=4>
|
||||||
<FieldFloat field=FormConfig.KernelWidth label="Kernel Width" />
|
<FieldFloat field=FormConfig.KernelWidth label="Kernel Width" />
|
||||||
|
@ -496,4 +496,4 @@ let make = () => {
|
||||||
</Antd.Card>
|
</Antd.Card>
|
||||||
<div className=Styles.spacer />
|
<div className=Styles.spacer />
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -43,7 +43,7 @@ module DemoDist = {
|
||||||
|
|
||||||
let str =
|
let str =
|
||||||
switch (parsed1) {
|
switch (parsed1) {
|
||||||
| Ok(r) => SymbolicDist.toString(r)
|
| Ok(r) => TreeNode.toString(r)
|
||||||
| Error(e) => e
|
| Error(e) => e
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ module DemoDist = {
|
||||||
~guesstimatorString=None,
|
~guesstimatorString=None,
|
||||||
(),
|
(),
|
||||||
)
|
)
|
||||||
|> Distributions.DistPlus.T.scaleToIntegralSum(~intendedSum=1.0);
|
|> Distributions.DistPlus.T.normalize;
|
||||||
<DistPlusPlot distPlus />;
|
<DistPlusPlot distPlus />;
|
||||||
})
|
})
|
||||||
|> E.O.default(ReasonReact.null);
|
|> E.O.default(ReasonReact.null);
|
||||||
|
|
|
@ -3,7 +3,8 @@ module type dist = {
|
||||||
type integral;
|
type integral;
|
||||||
let minX: t => float;
|
let minX: t => float;
|
||||||
let maxX: t => float;
|
let maxX: t => float;
|
||||||
let mapY: (~knownIntegralSumFn: float => option(float)=?, float => float, t) => t;
|
let mapY:
|
||||||
|
(~knownIntegralSumFn: float => option(float)=?, float => float, t) => t;
|
||||||
let xToY: (float, t) => DistTypes.mixedPoint;
|
let xToY: (float, t) => DistTypes.mixedPoint;
|
||||||
let toShape: t => DistTypes.shape;
|
let toShape: t => DistTypes.shape;
|
||||||
let toContinuous: t => option(DistTypes.continuousShape);
|
let toContinuous: t => option(DistTypes.continuousShape);
|
||||||
|
@ -13,6 +14,7 @@ module type dist = {
|
||||||
let normalizedToDiscrete: t => option(DistTypes.discreteShape);
|
let normalizedToDiscrete: t => option(DistTypes.discreteShape);
|
||||||
let toDiscreteProbabilityMassFraction: t => float;
|
let toDiscreteProbabilityMassFraction: t => float;
|
||||||
let downsample: (~cache: option(integral)=?, int, t) => t;
|
let downsample: (~cache: option(integral)=?, int, t) => t;
|
||||||
|
let truncate: (option(float), option(float), t) => t;
|
||||||
|
|
||||||
let integral: (~cache: option(integral), t) => integral;
|
let integral: (~cache: option(integral), t) => integral;
|
||||||
let integralEndY: (~cache: option(integral), t) => float;
|
let integralEndY: (~cache: option(integral), t) => float;
|
||||||
|
@ -38,6 +40,7 @@ module Dist = (T: dist) => {
|
||||||
let toContinuous = T.toContinuous;
|
let toContinuous = T.toContinuous;
|
||||||
let toDiscrete = T.toDiscrete;
|
let toDiscrete = T.toDiscrete;
|
||||||
let normalize = T.normalize;
|
let normalize = T.normalize;
|
||||||
|
let truncate = T.truncate;
|
||||||
let normalizedToContinuous = T.normalizedToContinuous;
|
let normalizedToContinuous = T.normalizedToContinuous;
|
||||||
let normalizedToDiscrete = T.normalizedToDiscrete;
|
let normalizedToDiscrete = T.normalizedToDiscrete;
|
||||||
let mean = T.mean;
|
let mean = T.mean;
|
||||||
|
@ -52,7 +55,22 @@ module Dist = (T: dist) => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
module Continuous {
|
module Common = {
|
||||||
|
let combineIntegralSums =
|
||||||
|
(
|
||||||
|
combineFn: (float, float) => option(float),
|
||||||
|
t1KnownIntegralSum: option(float),
|
||||||
|
t2KnownIntegralSum: option(float),
|
||||||
|
) => {
|
||||||
|
switch (t1KnownIntegralSum, t2KnownIntegralSum) {
|
||||||
|
| (None, _)
|
||||||
|
| (_, None) => None
|
||||||
|
| (Some(s1), Some(s2)) => combineFn(s1, s2)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
module Continuous = {
|
||||||
type t = DistTypes.continuousShape;
|
type t = DistTypes.continuousShape;
|
||||||
let getShape = (t: t) => t.xyShape;
|
let getShape = (t: t) => t.xyShape;
|
||||||
let interpolation = (t: t) => t.interpolation;
|
let interpolation = (t: t) => t.interpolation;
|
||||||
|
@ -78,17 +96,21 @@ module Continuous {
|
||||||
knownIntegralSum: Some(0.0),
|
knownIntegralSum: Some(0.0),
|
||||||
};
|
};
|
||||||
let combine =
|
let combine =
|
||||||
(fn, t1: DistTypes.continuousShape, t2: DistTypes.continuousShape)
|
(
|
||||||
|
~knownIntegralSumsFn,
|
||||||
|
fn,
|
||||||
|
t1: DistTypes.continuousShape,
|
||||||
|
t2: DistTypes.continuousShape,
|
||||||
|
)
|
||||||
: DistTypes.continuousShape => {
|
: DistTypes.continuousShape => {
|
||||||
|
|
||||||
// If we're adding the distributions, and we know the total of each, then we
|
// If we're adding the distributions, and we know the total of each, then we
|
||||||
// can just sum them up. Otherwise, all bets are off.
|
// can just sum them up. Otherwise, all bets are off.
|
||||||
let combinedIntegralSum =
|
let combinedIntegralSum =
|
||||||
switch (fn, t1.knownIntegralSum, t2.knownIntegralSum) {
|
Common.combineIntegralSums(
|
||||||
| (_, None, _)
|
knownIntegralSumsFn,
|
||||||
| (_, _, None) => None
|
t1.knownIntegralSum,
|
||||||
| ((+.), Some(s1), Some(s2)) => Some(s1 +. s2)
|
t2.knownIntegralSum,
|
||||||
};
|
);
|
||||||
|
|
||||||
make(
|
make(
|
||||||
`Linear,
|
`Linear,
|
||||||
|
@ -102,7 +124,6 @@ module Continuous {
|
||||||
combinedIntegralSum,
|
combinedIntegralSum,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
let reduce = (fn, items) => items |> E.A.fold_left(combine(fn), empty);
|
|
||||||
|
|
||||||
let toLinear = (t: t): option(t) => {
|
let toLinear = (t: t): option(t) => {
|
||||||
switch (t) {
|
switch (t) {
|
||||||
|
@ -114,7 +135,19 @@ module Continuous {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
let shapeFn = (fn, t: t) => t |> getShape |> fn;
|
let shapeFn = (fn, t: t) => t |> getShape |> fn;
|
||||||
let updateKnownIntegralSum = (knownIntegralSum, t: t): t => ({...t, knownIntegralSum});
|
let updateKnownIntegralSum = (knownIntegralSum, t: t): t => {
|
||||||
|
...t,
|
||||||
|
knownIntegralSum,
|
||||||
|
};
|
||||||
|
|
||||||
|
let reduce =
|
||||||
|
(
|
||||||
|
~knownIntegralSumsFn: (float, float) => option(float)=(_, _) => None,
|
||||||
|
fn,
|
||||||
|
continuousShapes,
|
||||||
|
) =>
|
||||||
|
continuousShapes
|
||||||
|
|> E.A.fold_left(combine(~knownIntegralSumsFn, fn), empty);
|
||||||
|
|
||||||
// Contracts every point in the continuous xyShape into a single dirac-Delta-like point,
|
// Contracts every point in the continuous xyShape into a single dirac-Delta-like point,
|
||||||
// using the centerpoints between adjacent xs and the area under each trapezoid.
|
// using the centerpoints between adjacent xs and the area under each trapezoid.
|
||||||
|
@ -128,11 +161,18 @@ module Continuous {
|
||||||
Belt.Array.set(
|
Belt.Array.set(
|
||||||
pointMassesY,
|
pointMassesY,
|
||||||
x,
|
x,
|
||||||
(xs[x + 1] -. xs[x]) *. ((ys[x] +. ys[x + 1]) /. 2.)); // = dx * (1/2) * (avgY)
|
(xs[x + 1] -. xs[x]) *. ((ys[x] +. ys[x + 1]) /. 2.),
|
||||||
|
); // = dx * (1/2) * (avgY)
|
||||||
();
|
();
|
||||||
};
|
};
|
||||||
|
|
||||||
{xyShape: {xs: xs, ys: pointMassesY}, knownIntegralSum: t.knownIntegralSum};
|
{
|
||||||
|
xyShape: {
|
||||||
|
xs,
|
||||||
|
ys: pointMassesY,
|
||||||
|
},
|
||||||
|
knownIntegralSum: t.knownIntegralSum,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Performs a discrete convolution between two continuous distributions A and B.
|
/* Performs a discrete convolution between two continuous distributions A and B.
|
||||||
|
@ -153,18 +193,25 @@ module Continuous {
|
||||||
let t1n = t1s |> XYShape.T.length;
|
let t1n = t1s |> XYShape.T.length;
|
||||||
let t2n = t2s |> XYShape.T.length;
|
let t2n = t2s |> XYShape.T.length;
|
||||||
|
|
||||||
let outXYShapes: array(array((float, float))) = Belt.Array.makeUninitializedUnsafe(t1n);
|
let outXYShapes: array(array((float, float))) =
|
||||||
|
Belt.Array.makeUninitializedUnsafe(t1n);
|
||||||
|
|
||||||
for (i in 0 to t1n - 1) {
|
for (i in 0 to t1n - 1) {
|
||||||
// create a new distribution
|
// create a new distribution
|
||||||
let dxyShape: array((float, float)) = Belt.Array.makeUninitializedUnsafe(t2n);
|
let dxyShape: array((float, float)) =
|
||||||
|
Belt.Array.makeUninitializedUnsafe(t2n);
|
||||||
for (j in 0 to t2n - 1) {
|
for (j in 0 to t2n - 1) {
|
||||||
let _ = Belt.Array.set(dxyShape, j, (fn(t1s.xs[i], t2s.xs[j]), t1s.ys[i] *. t2s.ys[j]));
|
let _ =
|
||||||
|
Belt.Array.set(
|
||||||
|
dxyShape,
|
||||||
|
j,
|
||||||
|
(fn(t1s.xs[i], t2s.xs[j]), t1s.ys[i] *. t2s.ys[j]),
|
||||||
|
);
|
||||||
();
|
();
|
||||||
}
|
};
|
||||||
let _ = Belt.Array.set(outXYShapes, i, dxyShape);
|
let _ = Belt.Array.set(outXYShapes, i, dxyShape);
|
||||||
();
|
();
|
||||||
}
|
};
|
||||||
|
|
||||||
let combinedIntegralSum =
|
let combinedIntegralSum =
|
||||||
switch (t1.knownIntegralSum, t2.knownIntegralSum) {
|
switch (t1.knownIntegralSum, t2.knownIntegralSum) {
|
||||||
|
@ -175,9 +222,9 @@ module Continuous {
|
||||||
|
|
||||||
outXYShapes
|
outXYShapes
|
||||||
|> E.A.fmap(s => {
|
|> E.A.fmap(s => {
|
||||||
let xyShape = XYShape.T.fromZippedArray(s);
|
let xyShape = XYShape.T.fromZippedArray(s);
|
||||||
make(`Linear, xyShape, None);
|
make(`Linear, xyShape, None);
|
||||||
})
|
})
|
||||||
|> reduce((+.))
|
|> reduce((+.))
|
||||||
|> updateKnownIntegralSum(combinedIntegralSum);
|
|> updateKnownIntegralSum(combinedIntegralSum);
|
||||||
};
|
};
|
||||||
|
@ -185,35 +232,22 @@ module Continuous {
|
||||||
let convolve = (fn, t1: t, t2: t) =>
|
let convolve = (fn, t1: t, t2: t) =>
|
||||||
convolveWithDiscrete(fn, t1, toDiscretePointMasses(t2));
|
convolveWithDiscrete(fn, t1, toDiscretePointMasses(t2));
|
||||||
|
|
||||||
let mapY = (~knownIntegralSumFn=(previousKnownIntegralSum => None), fn, t: t) => {
|
let mapY = (~knownIntegralSumFn=previousKnownIntegralSum => None, fn, t: t) => {
|
||||||
let u = E.O.bind(_, knownIntegralSumFn);
|
let u = E.O.bind(_, knownIntegralSumFn);
|
||||||
let yMapFn = shapeMap(XYShape.T.mapY(fn));
|
let yMapFn = shapeMap(XYShape.T.mapY(fn));
|
||||||
|
|
||||||
t |> yMapFn |> updateKnownIntegralSum(u(t.knownIntegralSum));
|
t |> yMapFn |> updateKnownIntegralSum(u(t.knownIntegralSum));
|
||||||
};
|
};
|
||||||
|
|
||||||
let scaleBy = (~scale=1.0, ~knownIntegralSum=None, t: t): t =>
|
let scaleBy = (~scale=1.0, t: t): t => {
|
||||||
t |> mapY((r: float) => r *. scale) |> updateKnownIntegralSum(knownIntegralSum);
|
t
|
||||||
|
|> mapY((r: float) => r *. scale)
|
||||||
let truncate = (leftCutoff: option(float), rightCutoff: option(float), t: t) => {
|
|> updateKnownIntegralSum(
|
||||||
let truncatedZippedPairs =
|
E.O.bind(t.knownIntegralSum, v => Some(scale *. v)),
|
||||||
t
|
);
|
||||||
|> getShape
|
|
||||||
|> XYShape.T.zip
|
|
||||||
|> XYShape.Zipped.filterByX(x => x >= E.O.default(neg_infinity, leftCutoff) || x <= E.O.default(infinity, rightCutoff));
|
|
||||||
|
|
||||||
let eps = (t |> getShape |> XYShape.T.xTotalRange) *. 0.0001;
|
|
||||||
|
|
||||||
let leftNewPoint = leftCutoff |> E.O.dimap(lc => [| (lc -. eps, 0.) |], _ => [||]);
|
|
||||||
let rightNewPoint = rightCutoff |> E.O.dimap(rc => [| (rc +. eps, 0.) |], _ => [||]);
|
|
||||||
|
|
||||||
let truncatedZippedPairsWithNewPoints =
|
|
||||||
E.A.concatMany([| leftNewPoint, truncatedZippedPairs, rightNewPoint |]);
|
|
||||||
let truncatedShape = XYShape.T.fromZippedArray(truncatedZippedPairsWithNewPoints);
|
|
||||||
|
|
||||||
make(`Linear, truncatedShape, None);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
module T =
|
module T =
|
||||||
Dist({
|
Dist({
|
||||||
type t = DistTypes.continuousShape;
|
type t = DistTypes.continuousShape;
|
||||||
|
@ -236,12 +270,31 @@ module Continuous {
|
||||||
|> DistTypes.MixedPoint.makeContinuous;
|
|> DistTypes.MixedPoint.makeContinuous;
|
||||||
};
|
};
|
||||||
|
|
||||||
// let combineWithFn = (t1: t, t2: t, fn: (float, float) => float) => {
|
let truncate =
|
||||||
// switch(t1, t2){
|
(leftCutoff: option(float), rightCutoff: option(float), t: t) => {
|
||||||
// | ({interpolation: `Stepwise}, {interpolation: `Stepwise}) => 3.0
|
let truncatedZippedPairs =
|
||||||
// | ({interpolation: `Linear}, {interpolation: `Linear}) => 3.0
|
t
|
||||||
// }
|
|> getShape
|
||||||
// };
|
|> XYShape.T.zip
|
||||||
|
|> XYShape.Zipped.filterByX(x =>
|
||||||
|
x >= E.O.default(neg_infinity, leftCutoff)
|
||||||
|
|| x <= E.O.default(infinity, rightCutoff)
|
||||||
|
);
|
||||||
|
|
||||||
|
let eps = (t |> getShape |> XYShape.T.xTotalRange) *. 0.0001;
|
||||||
|
|
||||||
|
let leftNewPoint =
|
||||||
|
leftCutoff |> E.O.dimap(lc => [|(lc -. eps, 0.)|], _ => [||]);
|
||||||
|
let rightNewPoint =
|
||||||
|
rightCutoff |> E.O.dimap(rc => [|(rc +. eps, 0.)|], _ => [||]);
|
||||||
|
|
||||||
|
let truncatedZippedPairsWithNewPoints =
|
||||||
|
E.A.concatMany([|leftNewPoint, truncatedZippedPairs, rightNewPoint|]);
|
||||||
|
let truncatedShape =
|
||||||
|
XYShape.T.fromZippedArray(truncatedZippedPairsWithNewPoints);
|
||||||
|
|
||||||
|
make(`Linear, truncatedShape, None);
|
||||||
|
};
|
||||||
|
|
||||||
// TODO: This should work with stepwise plots.
|
// TODO: This should work with stepwise plots.
|
||||||
let integral = (~cache, t) =>
|
let integral = (~cache, t) =>
|
||||||
|
@ -272,9 +325,9 @@ module Continuous {
|
||||||
let toDiscrete = _ => None;
|
let toDiscrete = _ => None;
|
||||||
|
|
||||||
let normalize = (t: t): t => {
|
let normalize = (t: t): t => {
|
||||||
let continuousIntegralSum = integralEndY(~cache=None, t);
|
t
|
||||||
|
|> scaleBy(~scale=1. /. integralEndY(~cache=None, t))
|
||||||
scaleBy(~scale=(1. /. continuousIntegralSum), ~knownIntegralSum=Some(1.0), t);
|
|> updateKnownIntegralSum(Some(1.0));
|
||||||
};
|
};
|
||||||
|
|
||||||
let normalizedToContinuous = t => Some(t); // TODO: this should be normalized
|
let normalizedToContinuous = t => Some(t); // TODO: this should be normalized
|
||||||
|
@ -316,40 +369,41 @@ module Discrete = {
|
||||||
|
|
||||||
let lastY = (t: t) => t |> getShape |> XYShape.T.lastY;
|
let lastY = (t: t) => t |> getShape |> XYShape.T.lastY;
|
||||||
|
|
||||||
let combineIntegralSums = (combineFn: ((float, float) => option(float)), t1KnownIntegralSum: option(float), t2KnownIntegralSum: option(float)) => {
|
let combine =
|
||||||
switch (t1KnownIntegralSum, t2KnownIntegralSum) {
|
(
|
||||||
| (None, _)
|
~knownIntegralSumsFn,
|
||||||
| (_, None) => None
|
fn,
|
||||||
| (Some(s1), Some(s2)) => combineFn(s1, s2)
|
t1: DistTypes.discreteShape,
|
||||||
};
|
t2: DistTypes.discreteShape,
|
||||||
};
|
)
|
||||||
|
|
||||||
let combine = (combineIntegralSumsFn, fn, t1: DistTypes.discreteShape, t2: DistTypes.discreteShape)
|
|
||||||
: DistTypes.discreteShape => {
|
: DistTypes.discreteShape => {
|
||||||
|
let combinedIntegralSum =
|
||||||
let combinedIntegralSum = combineIntegralSums(combineIntegralSumsFn, t1.knownIntegralSum, t2.knownIntegralSum);
|
Common.combineIntegralSums(
|
||||||
|
knownIntegralSumsFn,
|
||||||
|
t1.knownIntegralSum,
|
||||||
|
t2.knownIntegralSum,
|
||||||
|
);
|
||||||
|
|
||||||
make(
|
make(
|
||||||
XYShape.Combine.combine(
|
XYShape.Combine.combine(
|
||||||
~xsSelection=ALL_XS,
|
~xsSelection=ALL_XS,
|
||||||
~xToYSelection=XYShape.XtoY.stepwiseIfAtX,
|
~xToYSelection=XYShape.XtoY.stepwiseIfAtX,
|
||||||
~fn, // stepwiseIfAtX returns option(float), so this fn needs to handle None, which is what the _default0 wrapper is for
|
~fn=((a, b) => fn(E.O.default(0.0, a), E.O.default(0.0, b))), // stepwiseIfAtX returns option(float), so this fn needs to handle None
|
||||||
t1.xyShape,
|
t1.xyShape,
|
||||||
t2.xyShape,
|
t2.xyShape,
|
||||||
),
|
),
|
||||||
combinedIntegralSum,
|
combinedIntegralSum,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
let _default0 = (fn, a, b) =>
|
|
||||||
fn(E.O.default(0.0, a), E.O.default(0.0, b));
|
|
||||||
let reduce = (fn, items) =>
|
|
||||||
items |> E.A.fold_left(combine((_, _) => None, _default0(fn)), empty);
|
|
||||||
// a special version of reduce that adds the results (which should be the most common case by far),
|
|
||||||
// and conveniently also adds the knownIntegralSums.
|
|
||||||
let reduceAdd = (fn, items) =>
|
|
||||||
items |> E.A.fold_left(combine((s1, s2) => Some(s1 +. s2), _default0((+.))), empty);
|
|
||||||
|
|
||||||
let updateKnownIntegralSum = (knownIntegralSum, t: t): t => ({...t, knownIntegralSum});
|
let reduce = (~knownIntegralSumsFn=(_, _) => None, fn, discreteShapes): DistTypes.discreteShape =>
|
||||||
|
discreteShapes
|
||||||
|
|> E.A.fold_left(combine(~knownIntegralSumsFn, fn), empty);
|
||||||
|
|
||||||
|
let updateKnownIntegralSum = (knownIntegralSum, t: t): t => {
|
||||||
|
...t,
|
||||||
|
knownIntegralSum,
|
||||||
|
};
|
||||||
|
|
||||||
let convolve = (fn, t1: t, t2: t) => {
|
let convolve = (fn, t1: t, t2: t) => {
|
||||||
let t1s = t1 |> getShape;
|
let t1s = t1 |> getShape;
|
||||||
|
@ -357,7 +411,12 @@ module Discrete = {
|
||||||
let t1n = t1s |> XYShape.T.length;
|
let t1n = t1s |> XYShape.T.length;
|
||||||
let t2n = t2s |> XYShape.T.length;
|
let t2n = t2s |> XYShape.T.length;
|
||||||
|
|
||||||
let combinedIntegralSum = combineIntegralSums((s1, s2) => Some(s1 *. s2), t1.knownIntegralSum, t2.knownIntegralSum);
|
let combinedIntegralSum =
|
||||||
|
Common.combineIntegralSums(
|
||||||
|
(s1, s2) => Some(s1 *. s2),
|
||||||
|
t1.knownIntegralSum,
|
||||||
|
t2.knownIntegralSum,
|
||||||
|
);
|
||||||
|
|
||||||
let xToYMap = E.FloatFloatMap.empty();
|
let xToYMap = E.FloatFloatMap.empty();
|
||||||
|
|
||||||
|
@ -368,8 +427,8 @@ module Discrete = {
|
||||||
let my = t1s.ys[i] *. t2s.ys[j];
|
let my = t1s.ys[i] *. t2s.ys[j];
|
||||||
let _ = Belt.MutableMap.set(xToYMap, x, cv +. my);
|
let _ = Belt.MutableMap.set(xToYMap, x, cv +. my);
|
||||||
();
|
();
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
let rxys = xToYMap |> E.FloatFloatMap.toArray |> XYShape.Zipped.sortByX;
|
let rxys = xToYMap |> E.FloatFloatMap.toArray |> XYShape.Zipped.sortByX;
|
||||||
|
|
||||||
|
@ -378,25 +437,19 @@ module Discrete = {
|
||||||
make(convolvedShape, combinedIntegralSum);
|
make(convolvedShape, combinedIntegralSum);
|
||||||
};
|
};
|
||||||
|
|
||||||
let mapY = (~knownIntegralSumFn=(previousKnownIntegralSum => None), fn, t: t) => {
|
let mapY = (~knownIntegralSumFn=previousKnownIntegralSum => None, fn, t: t) => {
|
||||||
let u = E.O.bind(_, knownIntegralSumFn);
|
let u = E.O.bind(_, knownIntegralSumFn);
|
||||||
let yMapFn = shapeMap(XYShape.T.mapY(fn));
|
let yMapFn = shapeMap(XYShape.T.mapY(fn));
|
||||||
|
|
||||||
t |> yMapFn |> updateKnownIntegralSum(u(t.knownIntegralSum));
|
t |> yMapFn |> updateKnownIntegralSum(u(t.knownIntegralSum));
|
||||||
};
|
};
|
||||||
|
|
||||||
let scaleBy = (~scale=1.0, ~knownIntegralSum=None, t: t): t =>
|
let scaleBy = (~scale=1.0, t: t): t => {
|
||||||
t |> mapY((r: float) => r *. scale) |> updateKnownIntegralSum(knownIntegralSum);
|
t
|
||||||
|
|> mapY((r: float) => r *. scale)
|
||||||
let truncate = (leftCutoff: option(float), rightCutoff: option(float), t: t) => {
|
|> updateKnownIntegralSum(
|
||||||
let truncatedShape =
|
E.O.bind(t.knownIntegralSum, v => Some(scale *. v)),
|
||||||
t
|
);
|
||||||
|> getShape
|
|
||||||
|> XYShape.T.zip
|
|
||||||
|> XYShape.Zipped.filterByX(x => x >= E.O.default(neg_infinity, leftCutoff) || x <= E.O.default(infinity, rightCutoff))
|
|
||||||
|> XYShape.T.fromZippedArray;
|
|
||||||
|
|
||||||
make(truncatedShape, None);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module T =
|
module T =
|
||||||
|
@ -414,7 +467,8 @@ module Discrete = {
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let integralEndY = (~cache, t: t) =>
|
let integralEndY = (~cache, t: t) =>
|
||||||
t.knownIntegralSum |> E.O.default(t |> integral(~cache) |> Continuous.lastY);
|
t.knownIntegralSum
|
||||||
|
|> E.O.default(t |> integral(~cache) |> Continuous.lastY);
|
||||||
let minX = shapeFn(XYShape.T.minX);
|
let minX = shapeFn(XYShape.T.minX);
|
||||||
let maxX = shapeFn(XYShape.T.maxX);
|
let maxX = shapeFn(XYShape.T.maxX);
|
||||||
let toDiscreteProbabilityMassFraction = _ => 1.0;
|
let toDiscreteProbabilityMassFraction = _ => 1.0;
|
||||||
|
@ -424,9 +478,9 @@ module Discrete = {
|
||||||
let toDiscrete = t => Some(t);
|
let toDiscrete = t => Some(t);
|
||||||
|
|
||||||
let normalize = (t: t): t => {
|
let normalize = (t: t): t => {
|
||||||
let discreteIntegralSum = integralEndY(~cache=None, t);
|
t
|
||||||
|
|> scaleBy(~scale=1. /. integralEndY(~cache=None, t))
|
||||||
scaleBy(~scale=(1. /. discreteIntegralSum), ~knownIntegralSum=Some(1.0), t);
|
|> updateKnownIntegralSum(Some(1.0));
|
||||||
};
|
};
|
||||||
|
|
||||||
let normalizedToContinuous = _ => None;
|
let normalizedToContinuous = _ => None;
|
||||||
|
@ -448,6 +502,21 @@ module Discrete = {
|
||||||
make(clippedShape, None); // if someone needs the sum, they'll have to recompute it
|
make(clippedShape, None); // if someone needs the sum, they'll have to recompute it
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let truncate =
|
||||||
|
(leftCutoff: option(float), rightCutoff: option(float), t: t): t => {
|
||||||
|
let truncatedShape =
|
||||||
|
t
|
||||||
|
|> getShape
|
||||||
|
|> XYShape.T.zip
|
||||||
|
|> XYShape.Zipped.filterByX(x =>
|
||||||
|
x >= E.O.default(neg_infinity, leftCutoff)
|
||||||
|
|| x <= E.O.default(infinity, rightCutoff)
|
||||||
|
)
|
||||||
|
|> XYShape.T.fromZippedArray;
|
||||||
|
|
||||||
|
make(truncatedShape, None);
|
||||||
|
};
|
||||||
|
|
||||||
let xToY = (f, t) =>
|
let xToY = (f, t) =>
|
||||||
t
|
t
|
||||||
|> getShape
|
|> getShape
|
||||||
|
@ -477,53 +546,43 @@ module Discrete = {
|
||||||
XYShape.Analysis.getVarianceDangerously(t, mean, getMeanOfSquares);
|
XYShape.Analysis.getVarianceDangerously(t, mean, getMeanOfSquares);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: I think this shouldn't assume continuous/discrete are normalized to 1.0, and thus should not need the discreteProbabilityMassFraction being separate.
|
|
||||||
module Mixed = {
|
module Mixed = {
|
||||||
type t = DistTypes.mixedShape;
|
type t = DistTypes.mixedShape;
|
||||||
let make = (~continuous, ~discrete): t => {
|
let make = (~continuous, ~discrete): t => {continuous, discrete};
|
||||||
continuous,
|
|
||||||
discrete,
|
|
||||||
};
|
|
||||||
|
|
||||||
let totalLength = (t: t): int => {
|
let totalLength = (t: t): int => {
|
||||||
let continuousLength = t.continuous |> Continuous.getShape |> XYShape.T.length;
|
let continuousLength =
|
||||||
|
t.continuous |> Continuous.getShape |> XYShape.T.length;
|
||||||
let discreteLength = t.discrete |> Discrete.getShape |> XYShape.T.length;
|
let discreteLength = t.discrete |> Discrete.getShape |> XYShape.T.length;
|
||||||
|
|
||||||
continuousLength + discreteLength;
|
continuousLength + discreteLength;
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Put into scaling module
|
let scaleBy = (~scale=1.0, {discrete, continuous}: t): t => {
|
||||||
//let normalizeMixedPoint = (t, f) => f *. discreteProbabilityMassFraction;*/
|
let scaledDiscrete = Discrete.scaleBy(~scale, discrete);
|
||||||
|
let scaledContinuous = Continuous.scaleBy(~scale, continuous);
|
||||||
|
make(~discrete=scaledDiscrete, ~continuous=scaledContinuous);
|
||||||
|
};
|
||||||
|
|
||||||
//TODO: Warning: This currently computes the integral, which is expensive.
|
let toContinuous = ({continuous}: t) => Some(continuous);
|
||||||
/*let scaleContinuousFn =
|
let toDiscrete = ({discrete}: t) => Some(discrete);
|
||||||
({discreteProbabilityMassFraction}: DistTypes.mixedShape, f) =>
|
|
||||||
f *. (1.0 -. discreteProbabilityMassFraction); */
|
|
||||||
|
|
||||||
//TODO: Warning: This currently computes the integral, which is expensive.
|
let combine = (~knownIntegralSumsFn, fn, t1: t, t2: t) => {
|
||||||
|
let reducedDiscrete =
|
||||||
|
[|t1, t2|]
|
||||||
|
|> E.A.fmap(toDiscrete)
|
||||||
|
|> E.A.O.concatSomes
|
||||||
|
|> Discrete.reduce(~knownIntegralSumsFn, fn);
|
||||||
|
|
||||||
// Normalizes to 1.0.
|
let reducedContinuous =
|
||||||
/*let scaleContinuous = ({discreteProbabilityMassFraction}: t, continuous) =>
|
[|t1, t2|]
|
||||||
// get only the continuous, and scale it to the respective
|
|> E.A.fmap(toContinuous)
|
||||||
continuous
|
|> E.A.O.concatSomes
|
||||||
|> Continuous.T.scaleToIntegralSum(
|
|> Continuous.reduce(~knownIntegralSumsFn, fn);
|
||||||
~intendedSum=1.0 -. discreteProbabilityMassFraction,
|
|
||||||
);
|
|
||||||
|
|
||||||
let scaleDiscrete = ({discreteProbabilityMassFraction}: t, disrete) =>
|
make(~discrete=reducedDiscrete, ~continuous=reducedContinuous);
|
||||||
disrete
|
|
||||||
|> Discrete.T.scaleToIntegralSum(
|
|
||||||
~intendedSum=discreteProbabilityMassFraction,
|
|
||||||
);*/
|
|
||||||
|
|
||||||
let truncate = (leftCutoff: option(float), rightCutoff: option(float), {discrete, continuous}: t) => {
|
|
||||||
let truncatedDiscrete = Discrete.truncate(leftCutoff, rightCutoff, discrete);
|
|
||||||
let truncatedContinuous = Continuous.truncate(leftCutoff, rightCutoff, continuous);
|
|
||||||
|
|
||||||
make(~discrete=truncatedDiscrete, ~continuous=truncatedContinuous);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module T =
|
module T =
|
||||||
|
@ -536,19 +595,40 @@ module Mixed = {
|
||||||
let maxX = ({continuous, discrete}: t) =>
|
let maxX = ({continuous, discrete}: t) =>
|
||||||
max(Continuous.T.maxX(continuous), Discrete.T.maxX(discrete));
|
max(Continuous.T.maxX(continuous), Discrete.T.maxX(discrete));
|
||||||
let toShape = (t: t): DistTypes.shape => Mixed(t);
|
let toShape = (t: t): DistTypes.shape => Mixed(t);
|
||||||
let toContinuous = ({continuous}: t) => Some(continuous);
|
|
||||||
let toDiscrete = ({discrete}: t) => Some(discrete);
|
let toContinuous = toContinuous;
|
||||||
|
let toDiscrete = toDiscrete;
|
||||||
|
|
||||||
|
let truncate =
|
||||||
|
(
|
||||||
|
leftCutoff: option(float),
|
||||||
|
rightCutoff: option(float),
|
||||||
|
{discrete, continuous}: t,
|
||||||
|
) => {
|
||||||
|
let truncatedContinuous = Continuous.T.truncate(leftCutoff, rightCutoff, continuous);
|
||||||
|
let truncatedDiscrete = Discrete.T.truncate(leftCutoff, rightCutoff, discrete);
|
||||||
|
|
||||||
|
make(~discrete=truncatedDiscrete, ~continuous=truncatedContinuous);
|
||||||
|
};
|
||||||
|
|
||||||
let normalize = (t: t): t => {
|
let normalize = (t: t): t => {
|
||||||
let continuousIntegralSum = Continuous.T.Integral.sum(~cache=None, t.continuous);
|
let continuousIntegralSum =
|
||||||
let discreteIntegralSum = Discrete.T.Integral.sum(~cache=None, t.discrete);
|
Continuous.T.Integral.sum(~cache=None, t.continuous);
|
||||||
|
let discreteIntegralSum =
|
||||||
|
Discrete.T.Integral.sum(~cache=None, t.discrete);
|
||||||
let totalIntegralSum = continuousIntegralSum +. discreteIntegralSum;
|
let totalIntegralSum = continuousIntegralSum +. discreteIntegralSum;
|
||||||
|
|
||||||
let newContinuousSum = continuousIntegralSum /. totalIntegralSum;
|
let newContinuousSum = continuousIntegralSum /. totalIntegralSum;
|
||||||
let newDiscreteSum = discreteIntegralSum /. totalIntegralSum;
|
let newDiscreteSum = discreteIntegralSum /. totalIntegralSum;
|
||||||
|
|
||||||
let normalizedContinuous = Continuous.scaleBy(~scale=(1. /. newContinuousSum), ~knownIntegralSum=Some(newContinuousSum), t.continuous);
|
let normalizedContinuous =
|
||||||
let normalizedDiscrete = Discrete.scaleBy(~scale=(1. /. newDiscreteSum), ~knownIntegralSum=Some(newDiscreteSum), t.discrete);
|
t.continuous
|
||||||
|
|> Continuous.scaleBy(~scale=1. /. newContinuousSum)
|
||||||
|
|> Continuous.updateKnownIntegralSum(Some(newContinuousSum));
|
||||||
|
let normalizedDiscrete =
|
||||||
|
t.discrete
|
||||||
|
|> Discrete.scaleBy(~scale=1. /. newDiscreteSum)
|
||||||
|
|> Discrete.updateKnownIntegralSum(Some(newDiscreteSum));
|
||||||
|
|
||||||
make(~continuous=normalizedContinuous, ~discrete=normalizedDiscrete);
|
make(~continuous=normalizedContinuous, ~discrete=normalizedDiscrete);
|
||||||
};
|
};
|
||||||
|
@ -563,8 +643,10 @@ module Mixed = {
|
||||||
};
|
};
|
||||||
|
|
||||||
let toDiscreteProbabilityMassFraction = ({discrete, continuous}: t) => {
|
let toDiscreteProbabilityMassFraction = ({discrete, continuous}: t) => {
|
||||||
let discreteIntegralSum = Discrete.T.Integral.sum(~cache=None, discrete);
|
let discreteIntegralSum =
|
||||||
let continuousIntegralSum = Continuous.T.Integral.sum(~cache=None, continuous);
|
Discrete.T.Integral.sum(~cache=None, discrete);
|
||||||
|
let continuousIntegralSum =
|
||||||
|
Continuous.T.Integral.sum(~cache=None, continuous);
|
||||||
let totalIntegralSum = discreteIntegralSum +. continuousIntegralSum;
|
let totalIntegralSum = discreteIntegralSum +. continuousIntegralSum;
|
||||||
|
|
||||||
discreteIntegralSum /. totalIntegralSum;
|
discreteIntegralSum /. totalIntegralSum;
|
||||||
|
@ -575,20 +657,25 @@ module Mixed = {
|
||||||
// The easiest way to do this is to simply go by the previous probability masses.
|
// The easiest way to do this is to simply go by the previous probability masses.
|
||||||
|
|
||||||
// The cache really isn't helpful here, because we would need two separate caches
|
// The cache really isn't helpful here, because we would need two separate caches
|
||||||
let discreteIntegralSum = Discrete.T.Integral.sum(~cache=None, discrete);
|
let discreteIntegralSum =
|
||||||
let continuousIntegralSum = Continuous.T.Integral.sum(~cache=None, continuous);
|
Discrete.T.Integral.sum(~cache=None, discrete);
|
||||||
|
let continuousIntegralSum =
|
||||||
|
Continuous.T.Integral.sum(~cache=None, continuous);
|
||||||
let totalIntegralSum = discreteIntegralSum +. continuousIntegralSum;
|
let totalIntegralSum = discreteIntegralSum +. continuousIntegralSum;
|
||||||
|
|
||||||
let downsampledDiscrete =
|
let downsampledDiscrete =
|
||||||
Discrete.T.downsample(
|
Discrete.T.downsample(
|
||||||
int_of_float(float_of_int(count) *. (discreteIntegralSum /. totalIntegralSum)),
|
int_of_float(
|
||||||
|
float_of_int(count) *. (discreteIntegralSum /. totalIntegralSum),
|
||||||
|
),
|
||||||
discrete,
|
discrete,
|
||||||
);
|
);
|
||||||
|
|
||||||
let downsampledContinuous =
|
let downsampledContinuous =
|
||||||
Continuous.T.downsample(
|
Continuous.T.downsample(
|
||||||
int_of_float(
|
int_of_float(
|
||||||
float_of_int(count) *. (continuousIntegralSum /. totalIntegralSum),
|
float_of_int(count)
|
||||||
|
*. (continuousIntegralSum /. totalIntegralSum),
|
||||||
),
|
),
|
||||||
continuous,
|
continuous,
|
||||||
);
|
);
|
||||||
|
@ -596,23 +683,20 @@ module Mixed = {
|
||||||
{discrete: downsampledDiscrete, continuous: downsampledContinuous};
|
{discrete: downsampledDiscrete, continuous: downsampledContinuous};
|
||||||
};
|
};
|
||||||
|
|
||||||
let normalizedToContinuous = (t: t) =>
|
let normalizedToContinuous = (t: t) => Some(normalize(t).continuous);
|
||||||
Some(normalize(t).continuous);
|
|
||||||
|
|
||||||
let normalizedToDiscrete = ({discrete} as t: t) =>
|
let normalizedToDiscrete = ({discrete} as t: t) =>
|
||||||
Some(normalize(t).discrete);
|
Some(normalize(t).discrete);
|
||||||
|
|
||||||
let integral =
|
let integral = (~cache, {continuous, discrete}: t) => {
|
||||||
(
|
|
||||||
~cache,
|
|
||||||
{continuous, discrete}: t,
|
|
||||||
) => {
|
|
||||||
switch (cache) {
|
switch (cache) {
|
||||||
| Some(cache) => cache
|
| Some(cache) => cache
|
||||||
| None => {
|
| None =>
|
||||||
// note: if the underlying shapes aren't normalized, then these integrals won't be either!
|
// note: if the underlying shapes aren't normalized, then these integrals won't be either!
|
||||||
let continuousIntegral = Continuous.T.Integral.get(~cache=None, continuous);
|
let continuousIntegral =
|
||||||
let discreteIntegral = Discrete.T.Integral.get(~cache=None, discrete);
|
Continuous.T.Integral.get(~cache=None, continuous);
|
||||||
|
let discreteIntegral =
|
||||||
|
Discrete.T.Integral.get(~cache=None, discrete);
|
||||||
|
|
||||||
Continuous.make(
|
Continuous.make(
|
||||||
`Linear,
|
`Linear,
|
||||||
|
@ -623,7 +707,6 @@ module Mixed = {
|
||||||
),
|
),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -648,14 +731,26 @@ module Mixed = {
|
||||||
// This pipes all ys (continuous and discrete) through fn.
|
// This pipes all ys (continuous and discrete) through fn.
|
||||||
// If mapY is a linear operation, we might be able to update the knownIntegralSums as well;
|
// If mapY is a linear operation, we might be able to update the knownIntegralSums as well;
|
||||||
// if not, they'll be set to None.
|
// if not, they'll be set to None.
|
||||||
let mapY = (~knownIntegralSumFn=(previousIntegralSum => None), fn, {discrete, continuous}: t): t => {
|
let mapY =
|
||||||
|
(
|
||||||
|
~knownIntegralSumFn=previousIntegralSum => None,
|
||||||
|
fn,
|
||||||
|
{discrete, continuous}: t,
|
||||||
|
)
|
||||||
|
: t => {
|
||||||
let u = E.O.bind(_, knownIntegralSumFn);
|
let u = E.O.bind(_, knownIntegralSumFn);
|
||||||
|
|
||||||
let yMappedDiscrete =
|
let yMappedDiscrete =
|
||||||
discrete |> Discrete.T.mapY(fn) |> Discrete.updateKnownIntegralSum(u(discrete.knownIntegralSum));
|
discrete
|
||||||
|
|> Discrete.T.mapY(fn)
|
||||||
|
|> Discrete.updateKnownIntegralSum(u(discrete.knownIntegralSum));
|
||||||
|
|
||||||
let yMappedContinuous =
|
let yMappedContinuous =
|
||||||
continuous |> Continuous.T.mapY(fn) |> Continuous.updateKnownIntegralSum(u(continuous.knownIntegralSum));
|
continuous
|
||||||
|
|> Continuous.T.mapY(fn)
|
||||||
|
|> Continuous.updateKnownIntegralSum(
|
||||||
|
u(continuous.knownIntegralSum),
|
||||||
|
);
|
||||||
|
|
||||||
{
|
{
|
||||||
discrete: yMappedDiscrete,
|
discrete: yMappedDiscrete,
|
||||||
|
@ -668,34 +763,55 @@ module Mixed = {
|
||||||
let continuousMean = Continuous.T.mean(continuous);
|
let continuousMean = Continuous.T.mean(continuous);
|
||||||
|
|
||||||
// the combined mean is the weighted sum of the two:
|
// the combined mean is the weighted sum of the two:
|
||||||
let discreteIntegralSum = Discrete.T.Integral.sum(~cache=None, discrete);
|
let discreteIntegralSum =
|
||||||
let continuousIntegralSum = Continuous.T.Integral.sum(~cache=None, continuous);
|
Discrete.T.Integral.sum(~cache=None, discrete);
|
||||||
|
let continuousIntegralSum =
|
||||||
|
Continuous.T.Integral.sum(~cache=None, continuous);
|
||||||
let totalIntegralSum = discreteIntegralSum +. continuousIntegralSum;
|
let totalIntegralSum = discreteIntegralSum +. continuousIntegralSum;
|
||||||
|
|
||||||
(discreteMean *. discreteIntegralSum +. continuousMean *. continuousIntegralSum) /. totalIntegralSum;
|
(
|
||||||
|
discreteMean
|
||||||
|
*. discreteIntegralSum
|
||||||
|
+. continuousMean
|
||||||
|
*. continuousIntegralSum
|
||||||
|
)
|
||||||
|
/. totalIntegralSum;
|
||||||
};
|
};
|
||||||
|
|
||||||
let variance = ({discrete, continuous} as t: t): float => {
|
let variance = ({discrete, continuous} as t: t): float => {
|
||||||
// the combined mean is the weighted sum of the two:
|
// the combined mean is the weighted sum of the two:
|
||||||
let discreteIntegralSum = Discrete.T.Integral.sum(~cache=None, discrete);
|
let discreteIntegralSum =
|
||||||
let continuousIntegralSum = Continuous.T.Integral.sum(~cache=None, continuous);
|
Discrete.T.Integral.sum(~cache=None, discrete);
|
||||||
|
let continuousIntegralSum =
|
||||||
|
Continuous.T.Integral.sum(~cache=None, continuous);
|
||||||
let totalIntegralSum = discreteIntegralSum +. continuousIntegralSum;
|
let totalIntegralSum = discreteIntegralSum +. continuousIntegralSum;
|
||||||
|
|
||||||
let getMeanOfSquares = ({discrete, continuous} as t: t) => {
|
let getMeanOfSquares = ({discrete, continuous} as t: t) => {
|
||||||
let discreteMean = discrete |> Discrete.shapeMap(XYShape.Analysis.squareXYShape) |> Discrete.T.mean;
|
let discreteMean =
|
||||||
let continuousMean = continuous |> XYShape.Analysis.getMeanOfSquaresContinuousShape;
|
discrete
|
||||||
(discreteMean *. discreteIntegralSum +. continuousMean *. continuousIntegralSum) /. totalIntegralSum
|
|> Discrete.shapeMap(XYShape.Analysis.squareXYShape)
|
||||||
|
|> Discrete.T.mean;
|
||||||
|
let continuousMean =
|
||||||
|
continuous |> XYShape.Analysis.getMeanOfSquaresContinuousShape;
|
||||||
|
(
|
||||||
|
discreteMean
|
||||||
|
*. discreteIntegralSum
|
||||||
|
+. continuousMean
|
||||||
|
*. continuousIntegralSum
|
||||||
|
)
|
||||||
|
/. totalIntegralSum;
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (discreteIntegralSum /. totalIntegralSum) {
|
switch (discreteIntegralSum /. totalIntegralSum) {
|
||||||
| 1.0 => Discrete.T.variance(discrete)
|
| 1.0 => Discrete.T.variance(discrete)
|
||||||
| 0.0 => Continuous.T.variance(continuous)
|
| 0.0 => Continuous.T.variance(continuous)
|
||||||
| _ => XYShape.Analysis.getVarianceDangerously(t, mean, getMeanOfSquares)
|
| _ =>
|
||||||
|
XYShape.Analysis.getVarianceDangerously(t, mean, getMeanOfSquares)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
let convolve = (fn: ((float, float) => float), t1: t, t2: t): t => {
|
let convolve = (fn: (float, float) => float, t1: t, t2: t): t => {
|
||||||
// Discrete convolution can cause a huge increase in the number of samples,
|
// Discrete convolution can cause a huge increase in the number of samples,
|
||||||
// so we'll first downsample.
|
// so we'll first downsample.
|
||||||
|
|
||||||
|
@ -713,16 +829,21 @@ module Mixed = {
|
||||||
|
|
||||||
// continuous (*) continuous => continuous, but also
|
// continuous (*) continuous => continuous, but also
|
||||||
// discrete (*) continuous => continuous (and vice versa). We have to take care of all combos and then combine them:
|
// discrete (*) continuous => continuous (and vice versa). We have to take care of all combos and then combine them:
|
||||||
let ccConvResult = Continuous.convolve(fn, t1d.continuous, t2d.continuous);
|
let ccConvResult =
|
||||||
let dcConvResult = Continuous.convolveWithDiscrete(fn, t2d.continuous, t1d.discrete);
|
Continuous.convolve(fn, t1d.continuous, t2d.continuous);
|
||||||
let cdConvResult = Continuous.convolveWithDiscrete(fn, t1d.continuous, t2d.discrete);
|
let dcConvResult =
|
||||||
let continuousConvResult = Continuous.reduce((+.), [|ccConvResult, dcConvResult, cdConvResult|]);
|
Continuous.convolveWithDiscrete(fn, t2d.continuous, t1d.discrete);
|
||||||
|
let cdConvResult =
|
||||||
|
Continuous.convolveWithDiscrete(fn, t1d.continuous, t2d.discrete);
|
||||||
|
let continuousConvResult =
|
||||||
|
Continuous.reduce((+.), [|ccConvResult, dcConvResult, cdConvResult|]);
|
||||||
|
|
||||||
// ... finally, discrete (*) discrete => discrete, obviously:
|
// ... finally, discrete (*) discrete => discrete, obviously:
|
||||||
let discreteConvResult = Discrete.convolve(fn, t1d.discrete, t2d.discrete);
|
let discreteConvResult =
|
||||||
|
Discrete.convolve(fn, t1d.discrete, t2d.discrete);
|
||||||
|
|
||||||
{discrete: discreteConvResult, continuous: continuousConvResult};
|
{discrete: discreteConvResult, continuous: continuousConvResult};
|
||||||
}
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
module Shape = {
|
module Shape = {
|
||||||
|
@ -741,43 +862,31 @@ module Shape = {
|
||||||
| Continuous(m) => Continuous(fn3(m))
|
| Continuous(m) => Continuous(fn3(m))
|
||||||
};
|
};
|
||||||
|
|
||||||
let toMixed = mapToAll((
|
let toMixed =
|
||||||
m => m,
|
mapToAll((
|
||||||
d => Mixed.make(~discrete=d, ~continuous=Continuous.empty),
|
m => m,
|
||||||
c => Mixed.make(~discrete=Discrete.empty, ~continuous=c),
|
d => Mixed.make(~discrete=d, ~continuous=Continuous.empty),
|
||||||
));
|
c => Mixed.make(~discrete=Discrete.empty, ~continuous=c),
|
||||||
|
));
|
||||||
|
|
||||||
let convolve = (fn, t1: t, t2: t): t => {
|
let convolve = (fn, t1: t, t2: t): t => {
|
||||||
Mixed(Mixed.convolve(fn, toMixed(t1), toMixed(t2)));
|
Mixed(Mixed.convolve(fn, toMixed(t1), toMixed(t2)));
|
||||||
};
|
};
|
||||||
|
|
||||||
let downsample = (~cache=None, i, t) =>
|
let combine = (~knownIntegralSumsFn=(_, _) => None, fn, t1: t, t2: t) =>
|
||||||
fmap((
|
switch ((t1, t2)) {
|
||||||
Mixed.T.downsample(i),
|
| (Continuous(m1), Continuous(m2)) => DistTypes.Continuous(Continuous.combine(~knownIntegralSumsFn, fn, m1, m2))
|
||||||
Discrete.T.downsample(i),
|
| (Discrete(m1), Discrete(m2)) => DistTypes.Discrete(Discrete.combine(~knownIntegralSumsFn, fn, m1, m2))
|
||||||
Continuous.T.downsample(i),
|
| (m1, m2) => {
|
||||||
), t);
|
DistTypes.Mixed(Mixed.combine(~knownIntegralSumsFn, fn, toMixed(m1), toMixed(m2)))
|
||||||
|
}
|
||||||
let normalize =
|
};
|
||||||
fmap((
|
|
||||||
Mixed.T.normalize,
|
|
||||||
Discrete.T.normalize,
|
|
||||||
Continuous.T.normalize,
|
|
||||||
));
|
|
||||||
|
|
||||||
let truncate (leftCutoff, rightCutoff, t): t =
|
|
||||||
fmap((
|
|
||||||
Mixed.truncate(leftCutoff, rightCutoff),
|
|
||||||
Discrete.truncate(leftCutoff, rightCutoff),
|
|
||||||
Continuous.truncate(leftCutoff, rightCutoff),
|
|
||||||
), t);
|
|
||||||
|
|
||||||
module T =
|
module T =
|
||||||
Dist({
|
Dist({
|
||||||
type t = DistTypes.shape;
|
type t = DistTypes.shape;
|
||||||
type integral = DistTypes.continuousShape;
|
type integral = DistTypes.continuousShape;
|
||||||
|
|
||||||
|
|
||||||
let xToY = (f: float) =>
|
let xToY = (f: float) =>
|
||||||
mapToAll((
|
mapToAll((
|
||||||
Mixed.T.xToY(f),
|
Mixed.T.xToY(f),
|
||||||
|
@ -789,9 +898,31 @@ module Shape = {
|
||||||
|
|
||||||
let toContinuous = t => None;
|
let toContinuous = t => None;
|
||||||
let toDiscrete = t => None;
|
let toDiscrete = t => None;
|
||||||
let downsample = (~cache=None, i, t) => t;
|
|
||||||
let toDiscreteProbabilityMassFraction = t => 0.0;
|
|
||||||
let normalize = t => t;
|
let downsample = (~cache=None, i, t) =>
|
||||||
|
fmap(
|
||||||
|
(
|
||||||
|
Mixed.T.downsample(i),
|
||||||
|
Discrete.T.downsample(i),
|
||||||
|
Continuous.T.downsample(i),
|
||||||
|
),
|
||||||
|
t,
|
||||||
|
);
|
||||||
|
|
||||||
|
let truncate = (leftCutoff, rightCutoff, t): t =>
|
||||||
|
fmap(
|
||||||
|
(
|
||||||
|
Mixed.T.truncate(leftCutoff, rightCutoff),
|
||||||
|
Discrete.T.truncate(leftCutoff, rightCutoff),
|
||||||
|
Continuous.T.truncate(leftCutoff, rightCutoff),
|
||||||
|
),
|
||||||
|
t,
|
||||||
|
);
|
||||||
|
|
||||||
|
let toDiscreteProbabilityMassFraction = t => 0.0;
|
||||||
|
let normalize =
|
||||||
|
fmap((Mixed.T.normalize, Discrete.T.normalize, Continuous.T.normalize));
|
||||||
let toContinuous =
|
let toContinuous =
|
||||||
mapToAll((
|
mapToAll((
|
||||||
Mixed.T.toContinuous,
|
Mixed.T.toContinuous,
|
||||||
|
@ -853,7 +984,7 @@ module Shape = {
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
let maxX = mapToAll((Mixed.T.maxX, Discrete.T.maxX, Continuous.T.maxX));
|
let maxX = mapToAll((Mixed.T.maxX, Discrete.T.maxX, Continuous.T.maxX));
|
||||||
let mapY = (~knownIntegralSumFn=(previousIntegralSum => None), fn) =>
|
let mapY = (~knownIntegralSumFn=previousIntegralSum => None, fn) =>
|
||||||
fmap((
|
fmap((
|
||||||
Mixed.T.mapY(~knownIntegralSumFn, fn),
|
Mixed.T.mapY(~knownIntegralSumFn, fn),
|
||||||
Discrete.T.mapY(~knownIntegralSumFn, fn),
|
Discrete.T.mapY(~knownIntegralSumFn, fn),
|
||||||
|
@ -935,14 +1066,18 @@ module DistPlus = {
|
||||||
let toDiscrete = shapeFn(Shape.T.toDiscrete);
|
let toDiscrete = shapeFn(Shape.T.toDiscrete);
|
||||||
|
|
||||||
let normalize = (t: t): t => {
|
let normalize = (t: t): t => {
|
||||||
let normalizedShape =
|
let normalizedShape = t |> toShape |> Shape.T.normalize;
|
||||||
t |> toShape |> Shape.T.normalize;
|
|
||||||
|
|
||||||
t |> updateShape(normalizedShape);
|
|
||||||
|
|
||||||
|
t |> updateShape(normalizedShape);
|
||||||
// TODO: also adjust for domainIncludedProbabilityMass here.
|
// TODO: also adjust for domainIncludedProbabilityMass here.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let truncate = (leftCutoff, rightCutoff, t: t): t => {
|
||||||
|
let truncatedShape = t |> toShape |> Shape.T.truncate(leftCutoff, rightCutoff);
|
||||||
|
|
||||||
|
t |> updateShape(truncatedShape);
|
||||||
|
};
|
||||||
|
|
||||||
// TODO: replace this with
|
// TODO: replace this with
|
||||||
let normalizedToContinuous = (t: t) => {
|
let normalizedToContinuous = (t: t) => {
|
||||||
t
|
t
|
||||||
|
@ -980,7 +1115,13 @@ module DistPlus = {
|
||||||
let downsample = (~cache=None, i, t): t =>
|
let downsample = (~cache=None, i, t): t =>
|
||||||
updateShape(t |> toShape |> Shape.T.downsample(i), t);
|
updateShape(t |> toShape |> Shape.T.downsample(i), t);
|
||||||
// todo: adjust for limit, maybe?
|
// todo: adjust for limit, maybe?
|
||||||
let mapY = (~knownIntegralSumFn=(previousIntegralSum => None), fn, {shape, _} as t: t): t =>
|
let mapY =
|
||||||
|
(
|
||||||
|
~knownIntegralSumFn=previousIntegralSum => None,
|
||||||
|
fn,
|
||||||
|
{shape, _} as t: t,
|
||||||
|
)
|
||||||
|
: t =>
|
||||||
Shape.T.mapY(~knownIntegralSumFn, fn, shape) |> updateShape(_, t);
|
Shape.T.mapY(~knownIntegralSumFn, fn, shape) |> updateShape(_, t);
|
||||||
|
|
||||||
let integralEndY = (~cache as _, t: t) =>
|
let integralEndY = (~cache as _, t: t) =>
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
let truncateIfShould =
|
let downsampleIfShould =
|
||||||
(
|
(
|
||||||
{recommendedLength, shouldTruncate}: RenderTypes.DistPlusRenderer.inputs,
|
{recommendedLength, shouldDownsample}: RenderTypes.DistPlusRenderer.inputs,
|
||||||
outputs: RenderTypes.ShapeRenderer.Combined.outputs,
|
outputs: RenderTypes.ShapeRenderer.Combined.outputs,
|
||||||
dist,
|
dist,
|
||||||
) => {
|
) => {
|
||||||
let willTruncate =
|
let willDownsample =
|
||||||
shouldTruncate
|
shouldDownsample
|
||||||
&& RenderTypes.ShapeRenderer.Combined.methodUsed(outputs) == `Sampling;
|
&& RenderTypes.ShapeRenderer.Combined.methodUsed(outputs) == `Sampling;
|
||||||
willTruncate ? dist |> Distributions.DistPlus.T.truncate(recommendedLength) : dist;
|
willDownsample ? dist |> Distributions.DistPlus.T.downsample(recommendedLength) : dist;
|
||||||
};
|
};
|
||||||
|
|
||||||
let run =
|
let run =
|
||||||
|
@ -21,7 +21,7 @@ let run =
|
||||||
~guesstimatorString=Some(inputs.distPlusIngredients.guesstimatorString),
|
~guesstimatorString=Some(inputs.distPlusIngredients.guesstimatorString),
|
||||||
(),
|
(),
|
||||||
)
|
)
|
||||||
|> Distributions.DistPlus.T.scaleToIntegralSum(~intendedSum=1.0);
|
|> Distributions.DistPlus.T.normalize;
|
||||||
let outputs =
|
let outputs =
|
||||||
ShapeRenderer.run({
|
ShapeRenderer.run({
|
||||||
samplingInputs: inputs.samplingInputs,
|
samplingInputs: inputs.samplingInputs,
|
||||||
|
@ -32,6 +32,6 @@ let run =
|
||||||
});
|
});
|
||||||
let shape = outputs |> RenderTypes.ShapeRenderer.Combined.getShape;
|
let shape = outputs |> RenderTypes.ShapeRenderer.Combined.getShape;
|
||||||
let dist =
|
let dist =
|
||||||
shape |> E.O.fmap(toDist) |> E.O.fmap(truncateIfShould(inputs, outputs));
|
shape |> E.O.fmap(toDist) |> E.O.fmap(downsampleIfShould(inputs, outputs));
|
||||||
RenderTypes.DistPlusRenderer.Outputs.make(outputs, dist);
|
RenderTypes.DistPlusRenderer.Outputs.make(outputs, dist);
|
||||||
};
|
};
|
||||||
|
|
|
@ -75,7 +75,7 @@ module ShapeRenderer = {
|
||||||
|
|
||||||
module DistPlusRenderer = {
|
module DistPlusRenderer = {
|
||||||
let defaultRecommendedLength = 10000;
|
let defaultRecommendedLength = 10000;
|
||||||
let defaultShouldTruncate = true;
|
let defaultShouldDownsample = true;
|
||||||
type ingredients = {
|
type ingredients = {
|
||||||
guesstimatorString: string,
|
guesstimatorString: string,
|
||||||
domain: DistTypes.domain,
|
domain: DistTypes.domain,
|
||||||
|
@ -85,7 +85,7 @@ module DistPlusRenderer = {
|
||||||
distPlusIngredients: ingredients,
|
distPlusIngredients: ingredients,
|
||||||
samplingInputs: ShapeRenderer.Sampling.inputs,
|
samplingInputs: ShapeRenderer.Sampling.inputs,
|
||||||
recommendedLength: int,
|
recommendedLength: int,
|
||||||
shouldTruncate: bool,
|
shouldDownsample: bool,
|
||||||
};
|
};
|
||||||
module Ingredients = {
|
module Ingredients = {
|
||||||
let make =
|
let make =
|
||||||
|
@ -105,7 +105,7 @@ module DistPlusRenderer = {
|
||||||
(
|
(
|
||||||
~samplingInputs=ShapeRenderer.Sampling.Inputs.empty,
|
~samplingInputs=ShapeRenderer.Sampling.Inputs.empty,
|
||||||
~recommendedLength=defaultRecommendedLength,
|
~recommendedLength=defaultRecommendedLength,
|
||||||
~shouldTruncate=defaultShouldTruncate,
|
~shouldDownsample=defaultShouldDownsample,
|
||||||
~distPlusIngredients,
|
~distPlusIngredients,
|
||||||
(),
|
(),
|
||||||
)
|
)
|
||||||
|
@ -113,7 +113,7 @@ module DistPlusRenderer = {
|
||||||
distPlusIngredients,
|
distPlusIngredients,
|
||||||
samplingInputs,
|
samplingInputs,
|
||||||
recommendedLength,
|
recommendedLength,
|
||||||
shouldTruncate,
|
shouldDownsample,
|
||||||
};
|
};
|
||||||
type outputs = {
|
type outputs = {
|
||||||
shapeRenderOutputs: ShapeRenderer.Combined.outputs,
|
shapeRenderOutputs: ShapeRenderer.Combined.outputs,
|
||||||
|
|
|
@ -154,16 +154,17 @@ module MathAdtToDistDst = {
|
||||||
weights: option(array(float)),
|
weights: option(array(float)),
|
||||||
) => {
|
) => {
|
||||||
let weights = weights |> E.O.default([||]);
|
let weights = weights |> E.O.default([||]);
|
||||||
let dists =
|
|
||||||
|
/*let dists: =
|
||||||
args
|
args
|
||||||
|> E.A.fmap(
|
|> E.A.fmap(
|
||||||
fun
|
fun
|
||||||
| Ok(a) => a
|
| Ok(a) => a
|
||||||
| Error(e) => Error(e)
|
| Error(e) => Error(e)
|
||||||
);
|
);*/
|
||||||
|
|
||||||
let firstWithError = dists |> Belt.Array.getBy(_, Belt.Result.isError);
|
let firstWithError = args |> Belt.Array.getBy(_, Belt.Result.isError);
|
||||||
let withoutErrors = dists |> E.A.fmap(E.R.toOption) |> E.A.O.concatSomes;
|
let withoutErrors = args |> E.A.fmap(E.R.toOption) |> E.A.O.concatSomes;
|
||||||
|
|
||||||
switch (firstWithError) {
|
switch (firstWithError) {
|
||||||
| Some(Error(e)) => Error(e)
|
| Some(Error(e)) => Error(e)
|
||||||
|
@ -174,16 +175,16 @@ module MathAdtToDistDst = {
|
||||||
|> E.A.fmapi((index, t) => {
|
|> E.A.fmapi((index, t) => {
|
||||||
let w = weights |> E.A.get(_, index) |> E.O.default(1.0);
|
let w = weights |> E.A.get(_, index) |> E.O.default(1.0);
|
||||||
|
|
||||||
`Operation(`ScaleBy(`Multiply, t, `DistData(`Symbolic(`Float(w)))))
|
`Operation(`ScaleOperation(`Multiply, t, `DistData(`Symbolic(`Float(w)))))
|
||||||
});
|
});
|
||||||
|
|
||||||
let pointwiseSum = components
|
let pointwiseSum = components
|
||||||
|> Js.Array.sliceFrom(1)
|
|> Js.Array.sliceFrom(1)
|
||||||
|> E.A.fold_left((acc, x) => {
|
|> E.A.fold_left((acc, x) => {
|
||||||
`PointwiseSum(acc, x)
|
`Operation(`PointwiseOperation(`Add, acc, x))
|
||||||
}, E.A.unsafe_get(components, 0))
|
}, E.A.unsafe_get(components, 0))
|
||||||
|
|
||||||
Ok(`Normalize(pointwiseSum))
|
Ok(`Operation(`Normalize(pointwiseSum)))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -254,21 +255,21 @@ module MathAdtToDistDst = {
|
||||||
args
|
args
|
||||||
|> E.A.fmap(functionParser)
|
|> E.A.fmap(functionParser)
|
||||||
|> (fun
|
|> (fun
|
||||||
| [|Ok(l), Ok(r)|] => Ok(`Combination(l, r, `AddOperation))
|
| [|Ok(l), Ok(r)|] => Ok(`Operation(`StandardOperation(`Add, l, r)))
|
||||||
| _ => Error("Addition needs two operands"))
|
| _ => Error("Addition needs two operands"))
|
||||||
}
|
}
|
||||||
| Fn({name: "subtract", args}) => {
|
| Fn({name: "subtract", args}) => {
|
||||||
args
|
args
|
||||||
|> E.A.fmap(functionParser)
|
|> E.A.fmap(functionParser)
|
||||||
|> (fun
|
|> (fun
|
||||||
| [|Ok(l), Ok(r)|] => Ok(`Combination(l, r, `SubtractOperation))
|
| [|Ok(l), Ok(r)|] => Ok(`Operation(`StandardOperation(`Subtract, l, r)))
|
||||||
| _ => Error("Subtraction needs two operands"))
|
| _ => Error("Subtraction needs two operands"))
|
||||||
}
|
}
|
||||||
| Fn({name: "multiply", args}) => {
|
| Fn({name: "multiply", args}) => {
|
||||||
args
|
args
|
||||||
|> E.A.fmap(functionParser)
|
|> E.A.fmap(functionParser)
|
||||||
|> (fun
|
|> (fun
|
||||||
| [|Ok(l), Ok(r)|] => Ok(`Combination(l, r, `MultiplyOperation))
|
| [|Ok(l), Ok(r)|] => Ok(`Operation(`StandardOperation(`Multiply, l, r)))
|
||||||
| _ => Error("Multiplication needs two operands"))
|
| _ => Error("Multiplication needs two operands"))
|
||||||
}
|
}
|
||||||
| Fn({name: "divide", args}) => {
|
| Fn({name: "divide", args}) => {
|
||||||
|
@ -276,28 +277,37 @@ module MathAdtToDistDst = {
|
||||||
|> E.A.fmap(functionParser)
|
|> E.A.fmap(functionParser)
|
||||||
|> (fun
|
|> (fun
|
||||||
| [|Ok(l), Ok(`DistData(`Symbolic(`Float(0.0))))|] => Error("Division by zero")
|
| [|Ok(l), Ok(`DistData(`Symbolic(`Float(0.0))))|] => Error("Division by zero")
|
||||||
| [|Ok(l), Ok(r)|] => Ok(`Combination(l, r, `DivideOperation))
|
| [|Ok(l), Ok(r)|] => Ok(`Operation(`StandardOperation(`Divide, l, r)))
|
||||||
| _ => Error("Division needs two operands"))
|
| _ => Error("Division needs two operands"))
|
||||||
}
|
}
|
||||||
| Fn({name: "pow", args}) => {
|
| Fn({name: "pow", args}) => {
|
||||||
args
|
args
|
||||||
|> E.A.fmap(functionParser)
|
|> E.A.fmap(functionParser)
|
||||||
|> (fun
|
|> (fun
|
||||||
| [|Ok(l), Ok(r)|] => Ok(`Combination(l, r, `ExponentiateOperation))
|
| [|Ok(l), Ok(r)|] => Ok(`Operation(`StandardOperation(`Exponentiate, l, r)))
|
||||||
|
| _ => Error("Division needs two operands")
|
||||||
| _ => Error("Exponentiations needs two operands"))
|
| _ => Error("Exponentiations needs two operands"))
|
||||||
}
|
}
|
||||||
| Fn({name: "leftTruncate", args}) => {
|
| Fn({name: "leftTruncate", args}) => {
|
||||||
args
|
args
|
||||||
|> E.A.fmap(functionParser)
|
|> E.A.fmap(functionParser)
|
||||||
|> (fun
|
|> (fun
|
||||||
| [|Ok(l), Ok(`DistData(`Symbolic(`Float(r))))|] => Ok(`LeftTruncate(l, r))
|
| [|Ok(d), Ok(`DistData(`Symbolic(`Float(lc))))|] => Ok(`Operation(`Truncate(Some(lc), None, d)))
|
||||||
| _ => Error("leftTruncate needs two arguments: the expression and the cutoff"))
|
| _ => Error("leftTruncate needs two arguments: the expression and the cutoff"))
|
||||||
}
|
}
|
||||||
| Fn({name: "rightTruncate", args}) => {
|
| Fn({name: "rightTruncate", args}) => {
|
||||||
args
|
args
|
||||||
|> E.A.fmap(functionParser)
|
|> E.A.fmap(functionParser)
|
||||||
|> (fun
|
|> (fun
|
||||||
| [|Ok(l), Ok(`DistData(`Symbolic(`Float(r))))|] => Ok(`RightTruncate(l, r))
|
| [|Ok(d), Ok(`DistData(`Symbolic(`Float(rc))))|] => Ok(`Operation(`Truncate(None, Some(rc), d)))
|
||||||
|
| _ => Error("rightTruncate needs two arguments: the expression and the cutoff"))
|
||||||
|
}
|
||||||
|
| Fn({name: "truncate", args}) => {
|
||||||
|
args
|
||||||
|
|> E.A.fmap(functionParser)
|
||||||
|
|> (fun
|
||||||
|
| [|Ok(d), Ok(`DistData(`Symbolic(`Float(lc)))), Ok(`DistData(`Symbolic(`Float(rc))))|] => Ok(`Operation(`Truncate(Some(lc), Some(rc), d)))
|
||||||
|
// TODO: allow on-the-fly evaluations of FloatFromDists to be used as cutoff arguments here.
|
||||||
| _ => Error("rightTruncate needs two arguments: the expression and the cutoff"))
|
| _ => Error("rightTruncate needs two arguments: the expression and the cutoff"))
|
||||||
}
|
}
|
||||||
| Fn({name}) => Error(name ++ ": function not supported")
|
| Fn({name}) => Error(name ++ ": function not supported")
|
||||||
|
|
|
@ -1,69 +1,60 @@
|
||||||
/* This module represents a tree node. */
|
/* This module represents a tree node. */
|
||||||
|
|
||||||
/* TreeNodes are either Data (i.e. symbolic or rendered distributions) or Operations. */
|
type distData = [
|
||||||
type treeNode = [
|
|
||||||
| `DistData(distData)
|
|
||||||
| `Operation(operation)
|
|
||||||
] and distData = [
|
|
||||||
| `Symbolic(SymbolicDist.dist)
|
| `Symbolic(SymbolicDist.dist)
|
||||||
| `RenderedShape(DistTypes.shape)
|
| `RenderedShape(DistTypes.shape)
|
||||||
] and operation = [
|
];
|
||||||
// binary operations
|
|
||||||
| `StandardOperation(standardOperation, treeNode, treeNode)
|
type standardOperation = [
|
||||||
| `PointwiseOperation(pointwiseOperation, treeNode, treeNode)
|
|
||||||
| `ScaleOperation(scaleOperation, treeNode, scaleBy)
|
|
||||||
// unary operations
|
|
||||||
| `Render(treeNode) // always evaluates to `DistData(`RenderedShape(...))
|
|
||||||
| `Truncate(leftCutoff, rightCutoff, treeNode)
|
|
||||||
| `Normalize(treeNode)
|
|
||||||
// direct evaluations of dists (e.g. cdf, sample)
|
|
||||||
| `FloatFromDist(distToFloatOperation, treeNode)
|
|
||||||
] and standardOperation = [
|
|
||||||
| `Add
|
| `Add
|
||||||
| `Multiply
|
| `Multiply
|
||||||
| `Subtract
|
| `Subtract
|
||||||
| `Divide
|
| `Divide
|
||||||
| `Exponentiate
|
| `Exponentiate
|
||||||
] and pointwiseOperation = [
|
];
|
||||||
| `Add
|
type pointwiseOperation = [ | `Add | `Multiply];
|
||||||
| `Multiply
|
type scaleOperation = [ | `Multiply | `Exponentiate | `Log];
|
||||||
] and scaleOperation = [
|
type distToFloatOperation = [ | `Pdf(float) | `Inv(float) | `Mean | `Sample];
|
||||||
| `Multiply
|
|
||||||
| `Log
|
/* TreeNodes are either Data (i.e. symbolic or rendered distributions) or Operations. */
|
||||||
|
type treeNode = [
|
||||||
|
| `DistData(distData) // a leaf node that describes a distribution
|
||||||
|
| `Operation(operation) // an operation on two child nodes
|
||||||
]
|
]
|
||||||
and scaleBy = treeNode and leftCutoff = option(float) and rightCutoff = option(float)
|
and operation = [
|
||||||
and distToFloatOperation = [
|
| // binary operations
|
||||||
| `Pdf(float)
|
`StandardOperation(
|
||||||
| `Cdf(float)
|
standardOperation,
|
||||||
| `Inv(float)
|
treeNode,
|
||||||
| `Sample
|
treeNode,
|
||||||
|
)
|
||||||
|
// unary operations
|
||||||
|
| `PointwiseOperation(pointwiseOperation, treeNode, treeNode) // always evaluates to `DistData(`RenderedShape(...))
|
||||||
|
| `ScaleOperation(scaleOperation, treeNode, treeNode) // always evaluates to `DistData(`RenderedShape(...))
|
||||||
|
| `Render(treeNode) // always evaluates to `DistData(`RenderedShape(...))
|
||||||
|
| `Truncate // always evaluates to `DistData(`RenderedShape(...))
|
||||||
|
(
|
||||||
|
option(float),
|
||||||
|
option(float),
|
||||||
|
treeNode,
|
||||||
|
) // leftCutoff and rightCutoff
|
||||||
|
| `Normalize // always evaluates to `DistData(`RenderedShape(...))
|
||||||
|
// leftCutoff and rightCutoff
|
||||||
|
(
|
||||||
|
treeNode,
|
||||||
|
)
|
||||||
|
| `FloatFromDist // always evaluates to `DistData(`RenderedShape(...))
|
||||||
|
// leftCutoff and rightCutoff
|
||||||
|
(
|
||||||
|
distToFloatOperation,
|
||||||
|
treeNode,
|
||||||
|
)
|
||||||
];
|
];
|
||||||
|
|
||||||
module TreeNode = {
|
module TreeNode = {
|
||||||
type t = treeNode;
|
type t = treeNode;
|
||||||
type simplifier = treeNode => result(treeNode, string);
|
type simplifier = treeNode => result(treeNode, string);
|
||||||
|
|
||||||
type renderParams = {
|
|
||||||
operationToDistData: (int, operation) => result(t, string),
|
|
||||||
sampleCount: int,
|
|
||||||
}
|
|
||||||
|
|
||||||
let rec renderToShape = (renderParams, t: t): result(DistTypes.shape, string) => {
|
|
||||||
switch (t) {
|
|
||||||
| `DistData(`RenderedShape(s)) => Ok(s) // already a rendered shape, we're done here
|
|
||||||
| `DistData(`Symbolic(d)) =>
|
|
||||||
switch (d) {
|
|
||||||
| `Float(v) =>
|
|
||||||
Ok(Discrete(Distributions.Discrete.make({xs: [|v|], ys: [|1.0|]}, Some(1.0))));
|
|
||||||
| _ =>
|
|
||||||
let xs = SymbolicDist.GenericDistFunctions.interpolateXs(~xSelection=`ByWeight, d, renderParams.sampleCount);
|
|
||||||
let ys = xs |> E.A.fmap(x => SymbolicDist.GenericDistFunctions.pdf(x, d));
|
|
||||||
Ok(Continuous(Distributions.Continuous.make(`Linear, {xs, ys}, Some(1.0))));
|
|
||||||
}
|
|
||||||
| `Operation(op) => E.R.bind(renderParams.operationToDistData(renderParams.sampleCount, op), renderToShape(renderParams))
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/* The following modules encapsulate everything we can do with
|
/* The following modules encapsulate everything we can do with
|
||||||
* different kinds of operations. */
|
* different kinds of operations. */
|
||||||
|
|
||||||
|
@ -154,207 +145,328 @@ module TreeNode = {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
let evaluateNumerically = (standardOp, renderParams, t1, t2) => {
|
let evaluateNumerically = (standardOp, operationToDistData, t1, t2) => {
|
||||||
let func = funcFromOp(standardOp);
|
let func = funcFromOp(standardOp);
|
||||||
|
|
||||||
// TODO: downsample the two shapes
|
// force rendering into shapes
|
||||||
let renderedShape1 = t1 |> renderToShape(renderParams);
|
let renderedShape1 = operationToDistData(`Render(t1));
|
||||||
let renderedShape2 = t2 |> renderToShape(renderParams);
|
let renderedShape2 = operationToDistData(`Render(t2));
|
||||||
|
|
||||||
// This will most likely require a mixed
|
switch (renderedShape1, renderedShape2) {
|
||||||
|
| (
|
||||||
switch ((renderedShape1, renderedShape2)) {
|
Ok(`DistData(`RenderedShape(s1))),
|
||||||
| (Error(e1), _) => Error(e1)
|
Ok(`DistData(`RenderedShape(s2))),
|
||||||
| (_, Error(e2)) => Error(e2)
|
) =>
|
||||||
| (Ok(s1), Ok(s2)) => Ok(`DistData(`RenderedShape(Distributions.Shape.convolve(func, s1, s2))))
|
Ok(
|
||||||
|
`DistData(
|
||||||
|
`RenderedShape(Distributions.Shape.convolve(func, s1, s2)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
| (Error(e1), _) => Error(e1)
|
||||||
|
| (_, Error(e2)) => Error(e2)
|
||||||
|
| _ => Error("Could not render shapes.")
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
let evaluateToDistData =
|
let evaluateToDistData =
|
||||||
(standardOp: standardOperation, renderParams, t1: t, t2: t): result(treeNode, string) =>
|
(standardOp: standardOperation, operationToDistData, t1: t, t2: t)
|
||||||
|
: result(treeNode, string) =>
|
||||||
standardOp
|
standardOp
|
||||||
|> Simplify.attempt(_, t1, t2)
|
|> Simplify.attempt(_, t1, t2)
|
||||||
|> E.R.bind(
|
|> E.R.bind(
|
||||||
_,
|
_,
|
||||||
fun
|
fun
|
||||||
| `DistData(d) => Ok(`DistData(d)) // the analytical simplifaction worked, nice!
|
| `DistData(d) => Ok(`DistData(d)) // the analytical simplifaction worked, nice!
|
||||||
| `Operation(_) => // if not, run the convolution
|
| `Operation(_) =>
|
||||||
evaluateNumerically(standardOp, renderParams, t1, t2),
|
// if not, run the convolution
|
||||||
|
evaluateNumerically(standardOp, operationToDistData, t1, t2),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
module ScaleOperation = {
|
module ScaleOperation = {
|
||||||
let rec mean = (renderParams, t: t): result(float, string) => {
|
|
||||||
switch (t) {
|
|
||||||
| `DistData(`RenderedShape(s)) => Ok(Distributions.Shape.T.mean(s))
|
|
||||||
| `DistData(`Symbolic(s)) => SymbolicDist.GenericDistFunctions.mean(s)
|
|
||||||
// evaluating the operation returns result(treeNode(distData)). We then want to make sure
|
|
||||||
| `Operation(op) => E.R.bind(renderParams.operationToDistData(renderParams.sampleCount, op), mean(renderParams))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let fnFromOp =
|
let fnFromOp =
|
||||||
fun
|
fun
|
||||||
| `Multiply => (*.)
|
| `Multiply => ( *. )
|
||||||
| `Log => ((a, b) => ( log(a) /. log(b) ));
|
| `Exponentiate => ( ** )
|
||||||
|
| `Log => ((a, b) => log(a) /. log(b));
|
||||||
|
|
||||||
let knownIntegralSumFnFromOp =
|
let knownIntegralSumFnFromOp =
|
||||||
fun
|
fun
|
||||||
| `Multiply => (a, b) => Some(a *. b)
|
| `Multiply => ((a, b) => Some(a *. b))
|
||||||
|
| `Exponentiate => ((_, _) => None)
|
||||||
| `Log => ((_, _) => None);
|
| `Log => ((_, _) => None);
|
||||||
|
|
||||||
let evaluateToDistData = (scaleOp, renderParams, t, scaleBy) => {
|
let evaluateToDistData = (scaleOp, operationToDistData, t, scaleBy) => {
|
||||||
|
// scaleBy has to be a single float, otherwise we'll return an error.
|
||||||
let fn = fnFromOp(scaleOp);
|
let fn = fnFromOp(scaleOp);
|
||||||
let knownIntegralSumFn = knownIntegralSumFnFromOp(scaleOp);
|
let knownIntegralSumFn = knownIntegralSumFnFromOp(scaleOp);
|
||||||
let renderedShape = t |> renderToShape(renderParams);
|
|
||||||
let scaleByMeanValue = mean(renderParams, scaleBy);
|
|
||||||
|
|
||||||
switch ((renderedShape, scaleByMeanValue)) {
|
let renderedShape = operationToDistData(`Render(t));
|
||||||
|
|
||||||
|
switch (renderedShape, scaleBy) {
|
||||||
| (Error(e1), _) => Error(e1)
|
| (Error(e1), _) => Error(e1)
|
||||||
| (_, Error(e2)) => Error(e2)
|
| (
|
||||||
| (Ok(rs), Ok(sm)) =>
|
Ok(`DistData(`RenderedShape(rs))),
|
||||||
Ok(`DistData(`RenderedShape(Distributions.Shape.T.mapY(~knownIntegralSumFn=knownIntegralSumFn(sm), fn(sm), rs))))
|
`DistData(`Symbolic(`Float(sm))),
|
||||||
}
|
) =>
|
||||||
|
Ok(
|
||||||
|
`DistData(
|
||||||
|
`RenderedShape(
|
||||||
|
Distributions.Shape.T.mapY(
|
||||||
|
~knownIntegralSumFn=knownIntegralSumFn(sm),
|
||||||
|
fn(sm),
|
||||||
|
rs,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
| (_, _) => Error("Can only scale by float values.")
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
module PointwiseOperation = {
|
module PointwiseOperation = {
|
||||||
let funcFromOp: (pointwiseOperation => ((float, float) => float)) =
|
let pointwiseAdd = (operationToDistData, t1, t2) => {
|
||||||
fun
|
let renderedShape1 = operationToDistData(`Render(t1));
|
||||||
| `Add => (+.)
|
let renderedShape2 = operationToDistData(`Render(t2));
|
||||||
| `Multiply => ( *. );
|
|
||||||
|
|
||||||
let evaluateToDistData = (pointwiseOp, renderParams, t1, t2) => {
|
switch ((renderedShape1, renderedShape2)) {
|
||||||
let func = funcFromOp(pointwiseOp);
|
| (Error(e1), _) => Error(e1)
|
||||||
let renderedShape1 = t1 |> renderToShape(renderParams);
|
| (_, Error(e2)) => Error(e2)
|
||||||
let renderedShape2 = t2 |> renderToShape(renderParams);
|
| (Ok(`DistData(`RenderedShape(rs1))), Ok(`DistData(`RenderedShape(rs2)))) => Ok(`DistData(`RenderedShape(Distributions.Shape.combine(~knownIntegralSumsFn=(a, b) => Some(a +. b), (+.), rs1, rs2))))
|
||||||
|
| _ => Error("Could not perform pointwise addition.")
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
// TODO: figure out integral, diff between pointwiseAdd and pointwiseProduct and other stuff
|
let pointwiseMultiply = (operationToDistData, t1, t2) => {
|
||||||
// Distributions.Shape.reduce(func, renderedShape1, renderedShape2);
|
// TODO: construct a function that we can easily sample from, to construct
|
||||||
|
// a RenderedShape. Use the xMin and xMax of the rendered shapes to tell the sampling function where to look.
|
||||||
|
Error("Pointwise multiplication not yet supported.");
|
||||||
|
};
|
||||||
|
|
||||||
Error("Pointwise operations currently not supported.")
|
let evaluateToDistData = (pointwiseOp, operationToDistData, t1, t2) => {
|
||||||
|
switch (pointwiseOp) {
|
||||||
|
| `Add => pointwiseAdd(operationToDistData, t1, t2)
|
||||||
|
| `Multiply => pointwiseMultiply(operationToDistData, t1, t2)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
module Truncate = {
|
module Truncate = {
|
||||||
module Simplify = {
|
module Simplify = {
|
||||||
let tryTruncatingNothing: simplifier = fun
|
let tryTruncatingNothing: simplifier =
|
||||||
| `Operation(`Truncate(None, None, `DistData(d))) => Ok(`DistData(d))
|
fun
|
||||||
| t => Ok(t);
|
| `Operation(`Truncate(None, None, `DistData(d))) =>
|
||||||
|
Ok(`DistData(d))
|
||||||
|
| t => Ok(t);
|
||||||
|
|
||||||
let tryTruncatingUniform: simplifier = fun
|
let tryTruncatingUniform: simplifier =
|
||||||
| `Operation(`Truncate(lc, rc, `DistData(`Symbolic(`Uniform(u))))) => {
|
fun
|
||||||
// just create a new Uniform distribution
|
| `Operation(`Truncate(lc, rc, `DistData(`Symbolic(`Uniform(u))))) => {
|
||||||
let newLow = max(E.O.default(neg_infinity, lc), u.low);
|
// just create a new Uniform distribution
|
||||||
let newHigh = min(E.O.default(infinity, rc), u.high);
|
let newLow = max(E.O.default(neg_infinity, lc), u.low);
|
||||||
Ok(`DistData(`Symbolic(`Uniform({low: newLow, high: newHigh}))));
|
let newHigh = min(E.O.default(infinity, rc), u.high);
|
||||||
}
|
Ok(
|
||||||
| t => Ok(t);
|
`DistData(`Symbolic(`Uniform({low: newLow, high: newHigh}))),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
| t => Ok(t);
|
||||||
|
|
||||||
let attempt = (leftCutoff, rightCutoff, t): result(treeNode, string) => {
|
let attempt = (leftCutoff, rightCutoff, t): result(treeNode, string) => {
|
||||||
let originalTreeNode = `Operation(`Truncate(leftCutoff, rightCutoff, t));
|
let originalTreeNode =
|
||||||
|
`Operation(`Truncate((leftCutoff, rightCutoff, t)));
|
||||||
|
|
||||||
originalTreeNode
|
originalTreeNode
|
||||||
|> tryTruncatingNothing
|
|> tryTruncatingNothing
|
||||||
|> E.R.bind(_, tryTruncatingUniform);
|
|> E.R.bind(_, tryTruncatingUniform);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
let evaluateNumerically = (leftCutoff, rightCutoff, renderParams, t) => {
|
let evaluateNumerically =
|
||||||
|
(leftCutoff, rightCutoff, operationToDistData, t) => {
|
||||||
// TODO: use named args in renderToShape; if we're lucky we can at least get the tail
|
// TODO: use named args in renderToShape; if we're lucky we can at least get the tail
|
||||||
// of a distribution we otherwise wouldn't get at all
|
// of a distribution we otherwise wouldn't get at all
|
||||||
let renderedShape = t |> renderToShape(renderParams);
|
let renderedShape = operationToDistData(`Render(t));
|
||||||
|
|
||||||
E.R.bind(renderedShape, rs => {
|
switch (renderedShape) {
|
||||||
let truncatedShape = rs |> Distributions.Shape.truncate(leftCutoff, rightCutoff);
|
| Ok(`DistData(`RenderedShape(rs))) =>
|
||||||
|
let truncatedShape =
|
||||||
|
rs |> Distributions.Shape.T.truncate(leftCutoff, rightCutoff);
|
||||||
Ok(`DistData(`RenderedShape(rs)));
|
Ok(`DistData(`RenderedShape(rs)));
|
||||||
});
|
| Error(e1) => Error(e1)
|
||||||
|
| _ => Error("Could not truncate distribution.")
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
let evaluateToDistData = (leftCutoff: option(float), rightCutoff: option(float), renderParams, t: treeNode): result(treeNode, string) => {
|
let evaluateToDistData =
|
||||||
|
(
|
||||||
|
leftCutoff: option(float),
|
||||||
|
rightCutoff: option(float),
|
||||||
|
operationToDistData,
|
||||||
|
t: treeNode,
|
||||||
|
)
|
||||||
|
: result(treeNode, string) => {
|
||||||
t
|
t
|
||||||
|> Simplify.attempt(leftCutoff, rightCutoff)
|
|> Simplify.attempt(leftCutoff, rightCutoff)
|
||||||
|> E.R.bind(
|
|> E.R.bind(
|
||||||
_,
|
_,
|
||||||
fun
|
fun
|
||||||
| `DistData(d) => Ok(`DistData(d)) // the analytical simplifaction worked, nice!
|
| `DistData(d) => Ok(`DistData(d)) // the analytical simplifaction worked, nice!
|
||||||
| `Operation(_) => evaluateNumerically(leftCutoff, rightCutoff, renderParams, t),
|
| `Operation(_) =>
|
||||||
|
evaluateNumerically(
|
||||||
|
leftCutoff,
|
||||||
|
rightCutoff,
|
||||||
|
operationToDistData,
|
||||||
|
t,
|
||||||
|
),
|
||||||
); // if not, run the convolution
|
); // if not, run the convolution
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
module Normalize = {
|
module Normalize = {
|
||||||
let rec evaluateToDistData = (renderParams, t: treeNode): result(treeNode, string) => {
|
let rec evaluateToDistData =
|
||||||
|
(operationToDistData, t: treeNode): result(treeNode, string) => {
|
||||||
switch (t) {
|
switch (t) {
|
||||||
| `DistData(`Symbolic(_)) => Ok(t)
|
| `DistData(`Symbolic(_)) => Ok(t)
|
||||||
| `DistData(`RenderedShape(s)) => {
|
| `DistData(`RenderedShape(s)) =>
|
||||||
let normalized = Distributions.Shape.normalize(s);
|
let normalized = Distributions.Shape.T.normalize(s);
|
||||||
Ok(`DistData(`RenderedShape(normalized)));
|
Ok(`DistData(`RenderedShape(normalized)));
|
||||||
}
|
| `Operation(op) =>
|
||||||
| `Operation(op) => E.R.bind(renderParams.operationToDistData(renderParams.sampleCount, op), evaluateToDistData(renderParams))
|
E.R.bind(
|
||||||
}
|
operationToDistData(op),
|
||||||
}
|
evaluateToDistData(operationToDistData),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
module FloatFromDist = {
|
module FloatFromDist = {
|
||||||
let evaluateFromSymbolic = (distToFloatOp: distToFloatOperation, s) => {
|
let evaluateFromSymbolic = (distToFloatOp: distToFloatOperation, s) => {
|
||||||
let value = switch (distToFloatOp) {
|
let value =
|
||||||
| `Pdf(f) => SymbolicDist.GenericDistFunctions.pdf(f, s)
|
switch (distToFloatOp) {
|
||||||
| `Cdf(f) => 0.0
|
| `Pdf(f) => Ok(SymbolicDist.GenericDistFunctions.pdf(f, s))
|
||||||
| `Inv(f) => SymbolicDist.GenericDistFunctions.inv(f, s)
|
| `Inv(f) => Ok(SymbolicDist.GenericDistFunctions.inv(f, s))
|
||||||
| `Sample => SymbolicDist.GenericDistFunctions.sample(s)
|
| `Sample => Ok(SymbolicDist.GenericDistFunctions.sample(s))
|
||||||
}
|
| `Mean => SymbolicDist.GenericDistFunctions.mean(s)
|
||||||
Ok(`DistData(`Symbolic(`Float(value))));
|
};
|
||||||
|
E.R.bind(value, v => Ok(`DistData(`Symbolic(`Float(v)))));
|
||||||
};
|
};
|
||||||
let evaluateFromRenderedShape = (distToFloatOp: distToFloatOperation, rs: DistTypes.shape): result(treeNode, string) => {
|
let evaluateFromRenderedShape =
|
||||||
// evaluate the pdf, cdf, get sample, etc. from the renderedShape rs
|
(distToFloatOp: distToFloatOperation, rs: DistTypes.shape)
|
||||||
// Should be a float like Ok(`DistData(`Symbolic(Float(0.0))));
|
: result(treeNode, string) => {
|
||||||
Error("Float from dist is not yet implemented.");
|
Ok(`DistData(`Symbolic(`Float(Distributions.Shape.T.mean(rs)))));
|
||||||
};
|
};
|
||||||
let rec evaluateToDistData = (distToFloatOp: distToFloatOperation, renderParams, t: treeNode): result(treeNode, string) => {
|
let rec evaluateToDistData =
|
||||||
|
(
|
||||||
|
distToFloatOp: distToFloatOperation,
|
||||||
|
operationToDistData,
|
||||||
|
t: treeNode,
|
||||||
|
)
|
||||||
|
: result(treeNode, string) => {
|
||||||
switch (t) {
|
switch (t) {
|
||||||
| `DistData(`Symbolic(s)) => evaluateFromSymbolic(distToFloatOp, s) // we want to evaluate the distToFloatOp on the symbolic dist
|
| `DistData(`Symbolic(s)) => evaluateFromSymbolic(distToFloatOp, s) // we want to evaluate the distToFloatOp on the symbolic dist
|
||||||
| `DistData(`RenderedShape(rs)) => evaluateFromRenderedShape(distToFloatOp, rs)
|
| `DistData(`RenderedShape(rs)) =>
|
||||||
| `Operation(op) => E.R.bind(renderParams.operationToDistData(renderParams.sampleCount, op), evaluateToDistData(distToFloatOp, renderParams))
|
evaluateFromRenderedShape(distToFloatOp, rs)
|
||||||
}
|
| `Operation(op) =>
|
||||||
}
|
E.R.bind(
|
||||||
|
operationToDistData(op),
|
||||||
|
evaluateToDistData(distToFloatOp, operationToDistData),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
module Render = {
|
module Render = {
|
||||||
let evaluateToRenderedShape = (renderParams, t: treeNode): result(t, string) => {
|
let rec evaluateToRenderedShape =
|
||||||
E.R.bind(renderToShape(renderParams, t), rs => Ok(`DistData(`RenderedShape(rs))));
|
(operationToDistData: operation => result(t, string), sampleCount: int, t: treeNode)
|
||||||
}
|
: result(t, string) => {
|
||||||
|
switch (t) {
|
||||||
|
| `DistData(`RenderedShape(s)) => Ok(`DistData(`RenderedShape(s))) // already a rendered shape, we're done here
|
||||||
|
| `DistData(`Symbolic(d)) =>
|
||||||
|
switch (d) {
|
||||||
|
| `Float(v) =>
|
||||||
|
Ok(
|
||||||
|
`DistData(
|
||||||
|
`RenderedShape(
|
||||||
|
Discrete(
|
||||||
|
Distributions.Discrete.make(
|
||||||
|
{xs: [|v|], ys: [|1.0|]},
|
||||||
|
Some(1.0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
| _ =>
|
||||||
|
let xs =
|
||||||
|
SymbolicDist.GenericDistFunctions.interpolateXs(
|
||||||
|
~xSelection=`ByWeight,
|
||||||
|
d,
|
||||||
|
sampleCount,
|
||||||
|
);
|
||||||
|
let ys =
|
||||||
|
xs |> E.A.fmap(x => SymbolicDist.GenericDistFunctions.pdf(x, d));
|
||||||
|
Ok(
|
||||||
|
`DistData(
|
||||||
|
`RenderedShape(
|
||||||
|
Continuous(
|
||||||
|
Distributions.Continuous.make(
|
||||||
|
`Linear,
|
||||||
|
{xs, ys},
|
||||||
|
Some(1.0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
| `Operation(op) =>
|
||||||
|
E.R.bind(
|
||||||
|
operationToDistData(op),
|
||||||
|
evaluateToRenderedShape(operationToDistData, sampleCount),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
let rec operationToDistData =
|
let rec operationToDistData =
|
||||||
(sampleCount: int, op: operation): result(t, string) => {
|
(sampleCount: int, op: operation): result(t, string) => {
|
||||||
|
|
||||||
// the functions that convert the Operation nodes to DistData nodes need to
|
// the functions that convert the Operation nodes to DistData nodes need to
|
||||||
// have a way to call this function on their children, if their children are themselves Operation nodes.
|
// have a way to call this function on their children, if their children are themselves Operation nodes.
|
||||||
|
|
||||||
let renderParams: renderParams = {
|
|
||||||
operationToDistData: operationToDistData,
|
|
||||||
sampleCount: sampleCount,
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (op) {
|
switch (op) {
|
||||||
| `StandardOperation(standardOp, t1, t2) =>
|
| `StandardOperation(standardOp, t1, t2) =>
|
||||||
StandardOperation.evaluateToDistData(
|
StandardOperation.evaluateToDistData(
|
||||||
standardOp, renderParams, t1, t2 // we want to give it the option to render or simply leave it as is
|
standardOp,
|
||||||
|
operationToDistData(sampleCount),
|
||||||
|
t1,
|
||||||
|
t2 // we want to give it the option to render or simply leave it as is
|
||||||
)
|
)
|
||||||
| `PointwiseOperation(pointwiseOp, t1, t2) =>
|
| `PointwiseOperation(pointwiseOp, t1, t2) =>
|
||||||
PointwiseOperation.evaluateToDistData(
|
PointwiseOperation.evaluateToDistData(
|
||||||
pointwiseOp,
|
pointwiseOp,
|
||||||
renderParams,
|
operationToDistData(sampleCount),
|
||||||
t1,
|
t1,
|
||||||
t2,
|
t2,
|
||||||
)
|
)
|
||||||
| `ScaleOperation(scaleOp, t, scaleBy) =>
|
| `ScaleOperation(scaleOp, t, scaleBy) =>
|
||||||
ScaleOperation.evaluateToDistData(scaleOp, renderParams, t, scaleBy)
|
ScaleOperation.evaluateToDistData(
|
||||||
| `Truncate(leftCutoff, rightCutoff, t) => Truncate.evaluateToDistData(leftCutoff, rightCutoff, renderParams, t)
|
scaleOp,
|
||||||
| `FloatFromDist(distToFloatOp, t) => FloatFromDist.evaluateToDistData(distToFloatOp, renderParams, t)
|
operationToDistData(sampleCount),
|
||||||
| `Normalize(t) => Normalize.evaluateToDistData(renderParams, t)
|
t,
|
||||||
| `Render(t) => Render.evaluateToRenderedShape(renderParams, t)
|
scaleBy,
|
||||||
|
)
|
||||||
|
| `Truncate(leftCutoff, rightCutoff, t) =>
|
||||||
|
Truncate.evaluateToDistData(
|
||||||
|
leftCutoff,
|
||||||
|
rightCutoff,
|
||||||
|
operationToDistData(sampleCount),
|
||||||
|
t,
|
||||||
|
)
|
||||||
|
| `FloatFromDist(distToFloatOp, t) =>
|
||||||
|
FloatFromDist.evaluateToDistData(distToFloatOp, operationToDistData(sampleCount), t)
|
||||||
|
| `Normalize(t) => Normalize.evaluateToDistData(operationToDistData(sampleCount), t)
|
||||||
|
| `Render(t) =>
|
||||||
|
Render.evaluateToRenderedShape(operationToDistData(sampleCount), sampleCount, t)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -372,7 +484,8 @@ module TreeNode = {
|
||||||
};
|
};
|
||||||
|
|
||||||
let rec toString = (t: t): string => {
|
let rec toString = (t: t): string => {
|
||||||
let stringFromStandardOperation = fun
|
let stringFromStandardOperation =
|
||||||
|
fun
|
||||||
| `Add => " + "
|
| `Add => " + "
|
||||||
| `Subtract => " - "
|
| `Subtract => " - "
|
||||||
| `Multiply => " * "
|
| `Multiply => " * "
|
||||||
|
@ -384,31 +497,53 @@ module TreeNode = {
|
||||||
| `Add => " .+ "
|
| `Add => " .+ "
|
||||||
| `Multiply => " .* ";
|
| `Multiply => " .* ";
|
||||||
|
|
||||||
|
let stringFromFloatFromDistOperation =
|
||||||
|
fun
|
||||||
|
| `Pdf(f) => "pdf(x=$f, "
|
||||||
|
| `Inv(f) => "inv(c=$f, "
|
||||||
|
| `Sample => "sample("
|
||||||
|
| `Mean => "mean(";
|
||||||
|
|
||||||
|
|
||||||
switch (t) {
|
switch (t) {
|
||||||
| `DistData(`Symbolic(d)) => SymbolicDist.GenericDistFunctions.toString(d)
|
| `DistData(`Symbolic(d)) =>
|
||||||
|
SymbolicDist.GenericDistFunctions.toString(d)
|
||||||
| `DistData(`RenderedShape(s)) => "[shape]"
|
| `DistData(`RenderedShape(s)) => "[shape]"
|
||||||
| `Operation(`StandardOperation(op, t1, t2)) => toString(t1) ++ stringFromStandardOperation(op) ++ toString(t2)
|
| `Operation(`StandardOperation(op, t1, t2)) =>
|
||||||
| `Operation(`PointwiseOperation(op, t1, t2)) => toString(t1) ++ stringFromPointwiseOperation(op) ++ toString(t2)
|
toString(t1) ++ stringFromStandardOperation(op) ++ toString(t2)
|
||||||
| `Operation(`ScaleOperation(_scaleOp, t, scaleBy)) => toString(t) ++ " @ " ++ toString(scaleBy)
|
| `Operation(`PointwiseOperation(op, t1, t2)) =>
|
||||||
|
toString(t1) ++ stringFromPointwiseOperation(op) ++ toString(t2)
|
||||||
|
| `Operation(`ScaleOperation(_scaleOp, t, scaleBy)) =>
|
||||||
|
toString(t) ++ " @ " ++ toString(scaleBy)
|
||||||
| `Operation(`Normalize(t)) => "normalize(" ++ toString(t) ++ ")"
|
| `Operation(`Normalize(t)) => "normalize(" ++ toString(t) ++ ")"
|
||||||
| `Operation(`Truncate(lc, rc, t)) => "truncate(" ++ toString(t) ++ ", " ++ E.O.dimap(string_of_float, () => "-inf", lc) ++ ", " ++ E.O.dimap(string_of_float, () => "inf", rc) ++ ")"
|
| `Operation(`FloatFromDist(floatFromDistOp, t)) => stringFromFloatFromDistOperation(floatFromDistOp) ++ toString(t) ++ ")"
|
||||||
|
| `Operation(`Truncate(lc, rc, t)) =>
|
||||||
|
"truncate("
|
||||||
|
++ toString(t)
|
||||||
|
++ ", "
|
||||||
|
++ E.O.dimap(Js.Float.toString, () => "-inf", lc)
|
||||||
|
++ ", "
|
||||||
|
++ E.O.dimap(Js.Float.toString, () => "inf", rc)
|
||||||
|
++ ")"
|
||||||
| `Operation(`Render(t)) => toString(t)
|
| `Operation(`Render(t)) => toString(t)
|
||||||
}
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
let toShape = (sampleCount: int, treeNode: treeNode) => {
|
let toShape = (sampleCount: int, treeNode: treeNode) => {
|
||||||
let renderResult = TreeNode.toDistData(`Operation(`Render(treeNode)), sampleCount);
|
let renderResult =
|
||||||
|
TreeNode.toDistData(`Operation(`Render(treeNode)), sampleCount);
|
||||||
|
|
||||||
switch (renderResult) {
|
switch (renderResult) {
|
||||||
| Ok(`DistData(`RenderedShape(rs))) => {
|
| Ok(`DistData(`RenderedShape(rs))) =>
|
||||||
let continuous = Distributions.Shape.T.toContinuous(rs);
|
let continuous = Distributions.Shape.T.toContinuous(rs);
|
||||||
let discrete = Distributions.Shape.T.toDiscrete(rs);
|
let discrete = Distributions.Shape.T.toDiscrete(rs);
|
||||||
let shape = MixedShapeBuilder.buildSimple(~continuous, ~discrete);
|
let shape = MixedShapeBuilder.buildSimple(~continuous, ~discrete);
|
||||||
shape |> E.O.toExt("");
|
shape |> E.O.toExt("");
|
||||||
}
|
|
||||||
| Ok(_) => E.O.toExn("Rendering failed.", None)
|
| Ok(_) => E.O.toExn("Rendering failed.", None)
|
||||||
| Error(message) => E.O.toExn("No shape found!", None)
|
| Error(message) => E.O.toExn("No shape found, error: " ++ message, None)
|
||||||
}
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let toString = (treeNode: treeNode) =>
|
||||||
|
TreeNode.toString(treeNode);
|
||||||
|
|
|
@ -22,7 +22,7 @@ let propValue = (t: Prop.Value.t) => {
|
||||||
RenderTypes.DistPlusRenderer.make(
|
RenderTypes.DistPlusRenderer.make(
|
||||||
~distPlusIngredients=r,
|
~distPlusIngredients=r,
|
||||||
~recommendedLength=10000,
|
~recommendedLength=10000,
|
||||||
~shouldTruncate=true,
|
~shouldDownsample=true,
|
||||||
(),
|
(),
|
||||||
)
|
)
|
||||||
|> DistPlusRenderer.run
|
|> DistPlusRenderer.run
|
||||||
|
@ -105,4 +105,4 @@ module ModelForm = {
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue
Block a user