squiggle/src/core/DistFunctor.re

218 lines
6.7 KiB
ReasonML
Raw Normal View History

let min = (f1: option(float), f2: option(float)) =>
switch (f1, f2) {
| (Some(f1), Some(f2)) => Some(f1 < f2 ? f1 : f2)
| (Some(f1), None) => Some(f1)
| (None, Some(f2)) => Some(f2)
| (None, None) => None
};
let max = (f1: option(float), f2: option(float)) =>
switch (f1, f2) {
| (Some(f1), Some(f2)) => Some(f1 > f2 ? f1 : f2)
| (Some(f1), None) => Some(f1)
| (None, Some(f2)) => Some(f2)
| (None, None) => None
};
type yPoint =
| Mixed({
continuous: float,
discrete: float,
})
| Continuous(float)
| Discrete(float);
module type dist = {
type t;
type integral;
let minX: t => option(float);
let maxX: t => option(float);
let pointwiseFmap: (float => float, t) => t;
let xToY: (float, t) => yPoint;
let xToIntegralY: (float, t) => float;
let shape: t => DistributionTypes.shape;
let integral: t => integral;
let integralSum: t => float;
};
module Dist = (T: dist) => {
type t = T.t;
type integral = T.integral;
let minX = T.minX;
let maxX = T.maxX;
let pointwiseFmap = T.pointwiseFmap;
let xToIntegralY = T.xToIntegralY;
let xToY = T.xToY;
let shape = T.shape;
let integral = T.integral;
let integralSum = T.integralSum;
};
module Continuous =
Dist({
type t = DistributionTypes.continuousShape;
type integral = DistributionTypes.continuousShape;
let integral = t =>
t |> Shape.XYShape.Range.integrateWithTriangles |> E.O.toExt("");
let integralSum = t => t |> integral |> Shape.XYShape.ySum;
let minX = Shape.XYShape.minX;
let maxX = Shape.XYShape.maxX;
let pointwiseFmap = Shape.XYShape.pointwiseMap;
let shape = (t: t): DistributionTypes.shape => Continuous(t);
let xToY = (f, t) =>
CdfLibrary.Distribution.findY(f, t) |> (e => Continuous(e));
let xToIntegralY = (f, t) =>
t |> integral |> CdfLibrary.Distribution.findY(f);
});
module Discrete =
Dist({
type t = DistributionTypes.discreteShape;
type integral = DistributionTypes.continuousShape;
let integral = t => t |> Shape.Discrete.integrate;
let integralSum = t => t |> Shape.XYShape.ySum;
let minX = Shape.XYShape.minX;
let maxX = Shape.XYShape.maxX;
let pointwiseFmap = Shape.XYShape.pointwiseMap;
let shape = (t: t): DistributionTypes.shape => Discrete(t);
let xToY = (f, t) =>
CdfLibrary.Distribution.findY(f, t) |> (e => Discrete(e));
let xToIntegralY = (f, t) =>
t |> Shape.XYShape.accumulateYs |> CdfLibrary.Distribution.findY(f);
});
module Mixed =
Dist({
type t = DistributionTypes.mixedShape;
type integral = DistributionTypes.continuousShape;
let minX = ({continuous, discrete}: t) =>
min(Continuous.minX(continuous), Discrete.minX(discrete));
let maxX = ({continuous, discrete}: t) =>
max(Continuous.maxX(continuous), Discrete.maxX(discrete));
let shape = (t: t): DistributionTypes.shape => Mixed(t);
let xToY =
(f, {discrete, continuous, discreteProbabilityMassFraction}: t) =>
Mixed({
continuous:
CdfLibrary.Distribution.findY(f, continuous)
|> (e => e *. (1. -. discreteProbabilityMassFraction)),
discrete:
Shape.Discrete.findY(f, discrete)
|> (e => e *. discreteProbabilityMassFraction),
});
let scaledContinuousComponent =
({continuous, discreteProbabilityMassFraction}: t)
: option(DistributionTypes.continuousShape) => {
Shape.Continuous.scalePdf(
~scaleTo=1.0 -. discreteProbabilityMassFraction,
continuous,
);
};
let scaledDiscreteComponent =
({discrete, discreteProbabilityMassFraction}: t)
: DistributionTypes.continuousShape =>
Discrete.pointwiseFmap(
f => f *. discreteProbabilityMassFraction,
discrete,
);
// TODO: Add these two directly, once interpolation is added.
let integral = t => {
// let cont = scaledContinuousComponent(t);
// let discrete = scaledDiscreteComponent(t);
scaledContinuousComponent(t) |> E.O.toExt("");
};
let integralSum =
({discrete, continuous, discreteProbabilityMassFraction}: t) => {
Discrete.integralSum(discrete)
*. discreteProbabilityMassFraction
+. Continuous.integralSum(continuous)
*. (1.0 -. discreteProbabilityMassFraction);
};
let xToIntegralY =
(f, {discrete, continuous, discreteProbabilityMassFraction}: t) => {
let cont = Continuous.xToIntegralY(f, continuous);
let discrete = Discrete.xToIntegralY(f, discrete);
discrete
*. discreteProbabilityMassFraction
+. cont
*. (1.0 -. discreteProbabilityMassFraction);
};
let pointwiseFmap =
(fn, {discrete, continuous, discreteProbabilityMassFraction}: t): t => {
{
discrete: Shape.XYShape.pointwiseMap(fn, discrete),
continuous: Shape.XYShape.pointwiseMap(fn, continuous),
discreteProbabilityMassFraction,
};
};
});
module Shape =
Dist({
type t = DistributionTypes.shape;
type integral = DistributionTypes.continuousShape;
let xToY = (f, t) =>
Shape.T.mapToAll(
t,
(Mixed.xToY(f), Discrete.xToY(f), Continuous.xToY(f)),
);
let shape = (t: t) => t;
let minX = (t: t) =>
Shape.T.mapToAll(t, (Mixed.minX, Discrete.minX, Continuous.minX));
let integral = (t: t) =>
Shape.T.mapToAll(
t,
(Mixed.integral, Discrete.integral, Continuous.integral),
);
let integralSum = (t: t) =>
Shape.T.mapToAll(
t,
(Mixed.integralSum, Discrete.integralSum, Continuous.integralSum),
);
let xToIntegralY = (f, t) => {
Shape.T.mapToAll(
t,
(
Mixed.xToIntegralY(f),
Discrete.xToIntegralY(f),
Continuous.xToIntegralY(f),
),
);
};
let maxX = (t: t) =>
Shape.T.mapToAll(t, (Mixed.minX, Discrete.minX, Continuous.minX));
let pointwiseFmap = (fn, t: t) =>
Shape.T.fmap(
t,
(
Mixed.pointwiseFmap(fn),
Discrete.pointwiseFmap(fn),
Continuous.pointwiseFmap(fn),
),
);
});
module WithMetadata =
Dist({
type t = DistributionTypes.complexPower;
type integral = DistributionTypes.complexPower;
let shape = ({shape, _}: t) => shape;
let xToY = (f, t: t) => t |> shape |> Shape.xToY(f);
let minX = (t: t) => t |> shape |> Shape.minX;
let maxX = (t: t) => t |> shape |> Shape.maxX;
let fromShape = (shape, t): t => DistributionTypes.update(~shape, t);
let pointwiseFmap = (fn, {shape, _} as t: t): t =>
fromShape(Shape.pointwiseFmap(fn, shape), t);
let integral = (t: t) => fromShape(Continuous(t.integralCache), t);
let integralSum = (t: t) => t |> shape |> Shape.integralSum;
let xToIntegralY = (f, t) => {
3.0;
};
});