squiggle/src/core/DistFunctor.re

297 lines
8.9 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
};
2020-02-22 12:51:25 +00:00
type mixedPoint = {
continuous: float,
discrete: float,
};
2020-02-22 12:51:25 +00:00
module MixedPoint = {
type t = mixedPoint;
let toContinuousValue = (t: t) => t.continuous;
let toDiscreteValue = (t: t) => t.discrete;
let makeContinuous = (continuous: float): t => {continuous, discrete: 0.0};
let makeDiscrete = (discrete: float): t => {continuous: 0.0, discrete};
let fmap = (fn, t: t) => {
continuous: fn(t.continuous),
discrete: fn(t.discrete),
};
let combine2 = (fn, c: t, d: t): t => {
continuous: fn(c.continuous, d.continuous),
discrete: fn(c.discrete, d.discrete),
};
let add = combine2((a, b) => a +. b);
2020-02-22 11:07:17 +00:00
};
2020-02-22 10:10:10 +00:00
module type dist = {
type t;
let minX: t => option(float);
let maxX: t => option(float);
let pointwiseFmap: (float => float, t) => t;
2020-02-22 12:51:25 +00:00
let xToY: (float, t) => mixedPoint;
let shape: t => DistributionTypes.shape;
2020-02-22 10:17:51 +00:00
type integral;
2020-02-22 10:10:10 +00:00
let integral: (~cache: option(integral), t) => integral;
let integralSum: (~cache: option(integral), t) => float;
2020-02-22 10:17:51 +00:00
let integralXtoY: (~cache: option(integral), float, 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 xToY = T.xToY;
let shape = T.shape;
2020-02-22 10:17:51 +00:00
module Integral = {
type t = T.integral;
let get = T.integral;
let xToY = T.integralXtoY;
let sum = T.integralSum;
};
};
module Continuous =
Dist({
type t = DistributionTypes.continuousShape;
type integral = DistributionTypes.continuousShape;
2020-02-22 12:51:25 +00:00
let make = (shape, interpolation): t => {shape, interpolation};
let fromShape = shape => make(shape, `Linear);
let shape = (t: t) => t.shape;
let shapeFn = (fn, t: t) => t |> shape |> fn;
2020-02-22 11:07:17 +00:00
let shape = (t: t) => t.shape;
2020-02-22 10:10:10 +00:00
let integral = (~cache, t) =>
2020-02-22 11:07:17 +00:00
cache
|> E.O.default(
t
|> shape
2020-02-22 12:51:25 +00:00
|> XYShape.Range.integrateWithTriangles
2020-02-22 11:07:17 +00:00
|> E.O.toExt("")
2020-02-22 12:51:25 +00:00
|> fromShape,
2020-02-22 11:07:17 +00:00
);
2020-02-22 10:10:10 +00:00
// This seems wrong, we really want the ending bit, I'd assume
let integralSum = (~cache, t) =>
2020-02-22 12:51:25 +00:00
t |> integral(~cache) |> shape |> XYShape.ySum;
let minX = shapeFn(XYShape.minX);
let maxX = shapeFn(XYShape.maxX);
2020-02-22 11:07:17 +00:00
let pointwiseFmap = (fn, t: t) =>
2020-02-22 12:51:25 +00:00
t |> shape |> XYShape.pointwiseMap(fn) |> fromShape;
let shape = (t: t): DistributionTypes.shape => Continuous(t);
let xToY = (f, t) =>
2020-02-22 12:51:25 +00:00
shapeFn(CdfLibrary.Distribution.findY(f), t)
|> MixedPoint.makeContinuous;
2020-02-22 10:17:51 +00:00
let integralXtoY = (~cache, f, t) =>
2020-02-22 12:51:25 +00:00
t |> integral(~cache) |> shapeFn(CdfLibrary.Distribution.findY(f));
});
module Discrete =
Dist({
type t = DistributionTypes.discreteShape;
type integral = DistributionTypes.continuousShape;
2020-02-22 10:17:51 +00:00
let integral = (~cache, t) =>
2020-02-22 12:51:25 +00:00
cache
|> E.O.default(t |> XYShape.accumulateYs |> Shape.Continuous.fromShape);
let integralSum = (~cache, t) => t |> XYShape.ySum;
let minX = XYShape.minX;
let maxX = XYShape.maxX;
let pointwiseFmap = XYShape.pointwiseMap;
let shape = (t: t): DistributionTypes.shape => Discrete(t);
let xToY = (f, t) =>
2020-02-22 12:51:25 +00:00
CdfLibrary.Distribution.findY(f, t) |> MixedPoint.makeDiscrete;
2020-02-22 10:17:51 +00:00
let integralXtoY = (~cache, f, t) =>
2020-02-22 12:51:25 +00:00
t |> 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 =
2020-02-22 12:51:25 +00:00
(f, {discrete, continuous, discreteProbabilityMassFraction}: t) => {
let c =
continuous
|> Continuous.xToY(f)
|> MixedPoint.fmap(e => e *. (1. -. discreteProbabilityMassFraction));
let d =
discrete
|> Discrete.xToY(f)
|> MixedPoint.fmap(e => e *. discreteProbabilityMassFraction);
MixedPoint.add(c, d);
};
let scaledContinuousComponent =
({continuous, discreteProbabilityMassFraction}: t)
: option(DistributionTypes.continuousShape) => {
Shape.Continuous.scalePdf(
~scaleTo=1.0 -. discreteProbabilityMassFraction,
continuous,
);
};
let scaledDiscreteComponent =
({discrete, discreteProbabilityMassFraction}: t)
: DistributionTypes.continuousShape =>
2020-02-22 11:07:17 +00:00
Shape.Continuous.make(
Discrete.pointwiseFmap(
f => f *. discreteProbabilityMassFraction,
discrete,
),
`Stepwise,
);
// TODO: Add these two directly, once interpolation is added.
2020-02-22 10:10:10 +00:00
let integral = (~cache, t) => {
// let cont = scaledContinuousComponent(t);
// let discrete = scaledDiscreteComponent(t);
2020-02-22 10:10:10 +00:00
switch (cache) {
| Some(cache) => cache
| None => scaledContinuousComponent(t) |> E.O.toExt("")
};
};
let integralSum =
2020-02-22 10:10:10 +00:00
(~cache, {discrete, continuous, discreteProbabilityMassFraction}: t) => {
switch (cache) {
| Some(cache) => 3.0
| None =>
2020-02-22 10:17:51 +00:00
Discrete.Integral.sum(~cache=None, discrete)
2020-02-22 10:10:10 +00:00
*. discreteProbabilityMassFraction
2020-02-22 10:17:51 +00:00
+. Continuous.Integral.sum(~cache=None, continuous)
2020-02-22 10:10:10 +00:00
*. (1.0 -. discreteProbabilityMassFraction)
};
};
2020-02-22 10:17:51 +00:00
let integralXtoY =
2020-02-22 10:10:10 +00:00
(
~cache,
f,
{discrete, continuous, discreteProbabilityMassFraction}: t,
) => {
2020-02-22 10:17:51 +00:00
let cont = Continuous.Integral.xToY(~cache, f, continuous);
let discrete = Discrete.Integral.xToY(~cache, f, discrete);
discrete
*. discreteProbabilityMassFraction
+. cont
*. (1.0 -. discreteProbabilityMassFraction);
};
let pointwiseFmap =
(fn, {discrete, continuous, discreteProbabilityMassFraction}: t): t => {
{
2020-02-22 12:51:25 +00:00
discrete: Discrete.pointwiseFmap(fn, discrete),
continuous: Continuous.pointwiseFmap(fn, continuous),
discreteProbabilityMassFraction,
};
};
});
module Shape =
Dist({
type t = DistributionTypes.shape;
type integral = DistributionTypes.continuousShape;
2020-02-22 12:51:25 +00:00
let mapToAll = (t: t, (fn1, fn2, fn3)) =>
switch (t) {
| Mixed(m) => fn1(m)
| Discrete(m) => fn2(m)
| Continuous(m) => fn3(m)
};
let fmap = (t: t, (fn1, fn2, fn3)): t =>
switch (t) {
| Mixed(m) => Mixed(fn1(m))
| Discrete(m) => Discrete(fn2(m))
| Continuous(m) => Continuous(fn3(m))
};
let xToY = (f, t) =>
2020-02-22 12:51:25 +00:00
mapToAll(t, (Mixed.xToY(f), Discrete.xToY(f), Continuous.xToY(f)));
let shape = (t: t) => t;
let minX = (t: t) =>
2020-02-22 12:51:25 +00:00
mapToAll(t, (Mixed.minX, Discrete.minX, Continuous.minX));
2020-02-22 10:10:10 +00:00
let integral = (~cache, t: t) =>
2020-02-22 12:51:25 +00:00
mapToAll(
t,
2020-02-22 10:10:10 +00:00
(
2020-02-22 10:17:51 +00:00
Mixed.Integral.get(~cache),
Discrete.Integral.get(~cache),
Continuous.Integral.get(~cache),
2020-02-22 10:10:10 +00:00
),
);
2020-02-22 10:10:10 +00:00
let integralSum = (~cache, t: t) =>
2020-02-22 12:51:25 +00:00
mapToAll(
t,
2020-02-22 10:10:10 +00:00
(
2020-02-22 10:17:51 +00:00
Mixed.Integral.sum(~cache),
Discrete.Integral.sum(~cache),
Continuous.Integral.sum(~cache),
2020-02-22 10:10:10 +00:00
),
);
2020-02-22 10:17:51 +00:00
let integralXtoY = (~cache, f, t) => {
2020-02-22 12:51:25 +00:00
mapToAll(
t,
(
2020-02-22 10:17:51 +00:00
Mixed.Integral.xToY(~cache, f),
Discrete.Integral.xToY(~cache, f),
Continuous.Integral.xToY(~cache, f),
),
);
};
let maxX = (t: t) =>
2020-02-22 12:51:25 +00:00
mapToAll(t, (Mixed.minX, Discrete.minX, Continuous.minX));
let pointwiseFmap = (fn, t: t) =>
2020-02-22 12:51:25 +00:00
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);
2020-02-22 11:07:17 +00:00
let integral = (~cache as _, t: t) =>
2020-02-22 10:10:10 +00:00
fromShape(Continuous(t.integralCache), t);
2020-02-22 11:07:17 +00:00
let integralSum = (~cache as _, t: t) =>
2020-02-22 10:17:51 +00:00
t |> shape |> Shape.Integral.sum(~cache=Some(t.integralCache));
2020-02-22 12:51:25 +00:00
// TODO: Fix this below, obviously.
2020-02-22 11:07:17 +00:00
let integralXtoY = (~cache as _, f, t) => {
2020-02-22 12:51:25 +00:00
1337.0;
};
});