Simple module-functor work

This commit is contained in:
Ozzie Gooen 2020-02-22 16:24:54 +00:00
parent da172bfa94
commit 0d2c53eca4
3 changed files with 327 additions and 270 deletions

View File

@ -14,38 +14,15 @@ let max = (f1: option(float), f2: option(float)) =>
| (None, None) => None | (None, None) => None
}; };
type mixedPoint = {
continuous: float,
discrete: float,
};
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);
};
module type dist = { module type dist = {
type t; type t;
let minX: t => option(float); let minX: t => option(float);
let maxX: t => option(float); let maxX: t => option(float);
let pointwiseFmap: (float => float, t) => t; let pointwiseFmap: (float => float, t) => t;
let xToY: (float, t) => mixedPoint; let xToY: (float, t) => DistributionTypes.mixedPoint;
let shape: t => DistributionTypes.shape; let toShape: t => DistributionTypes.shape;
let toContinuous: t => option(DistributionTypes.continuousShape);
let toDiscrete: t => option(DistributionTypes.discreteShape);
type integral; type integral;
let integral: (~cache: option(integral), t) => integral; let integral: (~cache: option(integral), t) => integral;
@ -60,7 +37,11 @@ module Dist = (T: dist) => {
let maxX = T.maxX; let maxX = T.maxX;
let pointwiseFmap = T.pointwiseFmap; let pointwiseFmap = T.pointwiseFmap;
let xToY = T.xToY; let xToY = T.xToY;
let shape = T.shape; let toShape = T.toShape;
let toContinuous = T.toContinuous;
let toDiscrete = T.toDiscrete;
let scaleBy = (~scale=1.0, t: t) =>
t |> pointwiseFmap((r: float) => r *. scale);
module Integral = { module Integral = {
type t = T.integral; type t = T.integral;
@ -70,228 +51,285 @@ module Dist = (T: dist) => {
}; };
}; };
module Continuous = module Continuous = {
Dist({ type t = DistributionTypes.continuousShape;
type t = DistributionTypes.continuousShape; let xyShape = (t: t) => t.xyShape;
type integral = DistributionTypes.continuousShape; let getShape = (t: t) => t.xyShape;
let make = (shape, interpolation): t => {shape, interpolation}; let interpolation = (t: t) => t.interpolation;
let fromShape = shape => make(shape, `Linear); let make = (xyShape, interpolation): t => {xyShape, interpolation};
let shape = (t: t) => t.shape; let fromShape = xyShape => make(xyShape, `Linear);
let shapeFn = (fn, t: t) => t |> shape |> fn; let shapeMap = (fn, {xyShape, interpolation}: t): t => {
let shape = (t: t) => t.shape; xyShape: fn(xyShape),
let integral = (~cache, t) => interpolation,
cache };
|> E.O.default( let oShapeMap =
t (fn, {xyShape, interpolation}: t)
|> shape : option(DistributionTypes.continuousShape) =>
|> XYShape.Range.integrateWithTriangles fn(xyShape) |> E.O.fmap(xyShape => make(xyShape, interpolation));
|> E.O.toExt("")
|> fromShape,
);
// This seems wrong, we really want the ending bit, I'd assume
let integralSum = (~cache, t) =>
t |> integral(~cache) |> shape |> XYShape.ySum;
let minX = shapeFn(XYShape.minX);
let maxX = shapeFn(XYShape.maxX);
let pointwiseFmap = (fn, t: t) =>
t |> shape |> XYShape.pointwiseMap(fn) |> fromShape;
let shape = (t: t): DistributionTypes.shape => Continuous(t);
let xToY = (f, t) =>
shapeFn(CdfLibrary.Distribution.findY(f), t)
|> MixedPoint.makeContinuous;
let integralXtoY = (~cache, f, t) =>
t |> integral(~cache) |> shapeFn(CdfLibrary.Distribution.findY(f));
});
module Discrete = module T =
Dist({ Dist({
type t = DistributionTypes.discreteShape; type t = DistributionTypes.continuousShape;
type integral = DistributionTypes.continuousShape; type integral = DistributionTypes.continuousShape;
let integral = (~cache, t) => let shapeFn = (fn, t: t) => t |> xyShape |> fn;
cache let integral = (~cache, t) =>
|> E.O.default(t |> XYShape.accumulateYs |> Shape.Continuous.fromShape); cache
let integralSum = (~cache, t) => t |> XYShape.ySum; |> E.O.default(
let minX = XYShape.minX; t
let maxX = XYShape.maxX; |> xyShape
let pointwiseFmap = XYShape.pointwiseMap; |> XYShape.Range.integrateWithTriangles
let shape = (t: t): DistributionTypes.shape => Discrete(t); |> E.O.toExt("")
let xToY = (f, t) => |> fromShape,
CdfLibrary.Distribution.findY(f, t) |> MixedPoint.makeDiscrete; );
let integralXtoY = (~cache, f, t) => // This seems wrong, we really want the ending bit, I'd assume
t |> XYShape.accumulateYs |> CdfLibrary.Distribution.findY(f); let integralSum = (~cache, t) =>
}); t |> integral(~cache) |> xyShape |> XYShape.ySum;
let minX = shapeFn(XYShape.minX);
let maxX = shapeFn(XYShape.maxX);
let pointwiseFmap = (fn, t: t) =>
t |> xyShape |> XYShape.pointwiseMap(fn) |> fromShape;
let toShape = (t: t): DistributionTypes.shape => Continuous(t);
let xToY = (f, t) =>
shapeFn(CdfLibrary.Distribution.findY(f), t)
|> DistributionTypes.MixedPoint.makeContinuous;
let integralXtoY = (~cache, f, t) =>
t |> integral(~cache) |> shapeFn(CdfLibrary.Distribution.findY(f));
let toContinuous = t => Some(t);
let toDiscrete = _ => None;
});
};
module Mixed = module Discrete = {
Dist({ module T =
type t = DistributionTypes.mixedShape; Dist({
type integral = DistributionTypes.continuousShape; type t = DistributionTypes.discreteShape;
let minX = ({continuous, discrete}: t) => type integral = DistributionTypes.continuousShape;
min(Continuous.minX(continuous), Discrete.minX(discrete)); let integral = (~cache, t) =>
let maxX = ({continuous, discrete}: t) => cache
max(Continuous.maxX(continuous), Discrete.maxX(discrete)); |> E.O.default(t |> XYShape.accumulateYs |> Continuous.fromShape);
let shape = (t: t): DistributionTypes.shape => Mixed(t); let integralSum = (~cache, t) => t |> XYShape.ySum;
let xToY = let minX = XYShape.minX;
(f, {discrete, continuous, discreteProbabilityMassFraction}: t) => { let maxX = XYShape.maxX;
let c = let pointwiseFmap = XYShape.pointwiseMap;
continuous let toShape = (t: t): DistributionTypes.shape => Discrete(t);
|> Continuous.xToY(f) let toContinuous = _ => None;
|> MixedPoint.fmap(e => e *. (1. -. discreteProbabilityMassFraction)); let toDiscrete = t => Some(t);
let d = let xToY = (f, t) =>
CdfLibrary.Distribution.findY(f, t)
|> DistributionTypes.MixedPoint.makeDiscrete;
let integralXtoY = (~cache, f, t) =>
t |> XYShape.accumulateYs |> CdfLibrary.Distribution.findY(f);
});
};
module Mixed = {
module T =
Dist({
type t = DistributionTypes.mixedShape;
type integral = DistributionTypes.continuousShape;
let minX = ({continuous, discrete}: t) =>
min(Continuous.T.minX(continuous), Discrete.T.minX(discrete));
let maxX = ({continuous, discrete}: t) =>
max(Continuous.T.maxX(continuous), Discrete.T.maxX(discrete));
let toShape = (t: t): DistributionTypes.shape => Mixed(t);
let toContinuous = ({continuous}: t) => Some(continuous);
let toDiscrete = ({discrete}: t) => Some(discrete);
let xToY =
(f, {discrete, continuous, discreteProbabilityMassFraction}: t) => {
let c =
continuous
|> Continuous.T.xToY(f)
|> DistributionTypes.MixedPoint.fmap(e =>
e *. (1. -. discreteProbabilityMassFraction)
);
let d =
discrete
|> Discrete.T.xToY(f)
|> DistributionTypes.MixedPoint.fmap(e =>
e *. discreteProbabilityMassFraction
);
DistributionTypes.MixedPoint.add(c, d);
};
// todo: FixMe
let scaledContinuousComponent =
({continuous, discreteProbabilityMassFraction}: t)
: option(DistributionTypes.continuousShape) =>
Some(continuous);
let scaledDiscreteComponent =
({discrete, discreteProbabilityMassFraction}: t)
: DistributionTypes.continuousShape =>
Continuous.make(
Discrete.T.pointwiseFmap(
f => f *. discreteProbabilityMassFraction,
discrete,
),
`Stepwise,
);
// TODO: Add these two directly, once interpolation is added.
let integral = (~cache, t) => {
// let cont = scaledContinuousComponent(t);
// let discrete = scaledDiscreteComponent(t);
switch (cache) {
| Some(cache) => cache
| None => scaledContinuousComponent(t) |> E.O.toExt("")
};
};
let integralSum =
(
~cache,
{discrete, continuous, discreteProbabilityMassFraction}: t,
) => {
switch (cache) {
| Some(cache) => 3.0
| None =>
Discrete.T.Integral.sum(~cache=None, discrete)
*. discreteProbabilityMassFraction
+. Continuous.T.Integral.sum(~cache=None, continuous)
*. (1.0 -. discreteProbabilityMassFraction)
};
};
let integralXtoY =
(
~cache,
f,
{discrete, continuous, discreteProbabilityMassFraction}: t,
) => {
let cont = Continuous.T.Integral.xToY(~cache, f, continuous);
let discrete = Discrete.T.Integral.xToY(~cache, f, discrete);
discrete 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 =>
Shape.Continuous.make(
Discrete.pointwiseFmap(
f => f *. discreteProbabilityMassFraction,
discrete,
),
`Stepwise,
);
// TODO: Add these two directly, once interpolation is added.
let integral = (~cache, t) => {
// let cont = scaledContinuousComponent(t);
// let discrete = scaledDiscreteComponent(t);
switch (cache) {
| Some(cache) => cache
| None => scaledContinuousComponent(t) |> E.O.toExt("")
};
};
let integralSum =
(~cache, {discrete, continuous, discreteProbabilityMassFraction}: t) => {
switch (cache) {
| Some(cache) => 3.0
| None =>
Discrete.Integral.sum(~cache=None, discrete)
*. discreteProbabilityMassFraction *. discreteProbabilityMassFraction
+. Continuous.Integral.sum(~cache=None, continuous) +. cont
*. (1.0 -. discreteProbabilityMassFraction) *. (1.0 -. discreteProbabilityMassFraction);
};
};
let integralXtoY =
(
~cache,
f,
{discrete, continuous, discreteProbabilityMassFraction}: t,
) => {
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 => {
{
discrete: Discrete.pointwiseFmap(fn, discrete),
continuous: Continuous.pointwiseFmap(fn, continuous),
discreteProbabilityMassFraction,
};
};
});
module Shape =
Dist({
type t = DistributionTypes.shape;
type integral = DistributionTypes.continuousShape;
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 => let pointwiseFmap =
switch (t) { (fn, {discrete, continuous, discreteProbabilityMassFraction}: t): t => {
| Mixed(m) => Mixed(fn1(m)) {
| Discrete(m) => Discrete(fn2(m)) discrete: Discrete.T.pointwiseFmap(fn, discrete),
| Continuous(m) => Continuous(fn3(m)) continuous: Continuous.T.pointwiseFmap(fn, continuous),
discreteProbabilityMassFraction,
};
}; };
});
};
let xToY = (f, t) => module Shape = {
mapToAll(t, (Mixed.xToY(f), Discrete.xToY(f), Continuous.xToY(f))); module T =
let shape = (t: t) => t; Dist({
let minX = (t: t) => type t = DistributionTypes.shape;
mapToAll(t, (Mixed.minX, Discrete.minX, Continuous.minX)); type integral = DistributionTypes.continuousShape;
let integral = (~cache, t: t) =>
mapToAll(
t,
(
Mixed.Integral.get(~cache),
Discrete.Integral.get(~cache),
Continuous.Integral.get(~cache),
),
);
let integralSum = (~cache, t: t) =>
mapToAll(
t,
(
Mixed.Integral.sum(~cache),
Discrete.Integral.sum(~cache),
Continuous.Integral.sum(~cache),
),
);
let integralXtoY = (~cache, f, t) => {
mapToAll(
t,
(
Mixed.Integral.xToY(~cache, f),
Discrete.Integral.xToY(~cache, f),
Continuous.Integral.xToY(~cache, f),
),
);
};
let maxX = (t: t) =>
mapToAll(t, (Mixed.minX, Discrete.minX, Continuous.minX));
let pointwiseFmap = (fn, t: t) =>
fmap(
t,
(
Mixed.pointwiseFmap(fn),
Discrete.pointwiseFmap(fn),
Continuous.pointwiseFmap(fn),
),
);
});
module WithMetadata = let mapToAll = (t: t, (fn1, fn2, fn3)) =>
Dist({ switch (t) {
type t = DistributionTypes.complexPower; | Mixed(m) => fn1(m)
type integral = DistributionTypes.complexPower; | Discrete(m) => fn2(m)
let shape = ({shape, _}: t) => shape; | Continuous(m) => fn3(m)
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 = (~cache as _, t: t) => let fmap = (t: t, (fn1, fn2, fn3)): t =>
fromShape(Continuous(t.integralCache), t); switch (t) {
let integralSum = (~cache as _, t: t) => | Mixed(m) => Mixed(fn1(m))
t |> shape |> Shape.Integral.sum(~cache=Some(t.integralCache)); | Discrete(m) => Discrete(fn2(m))
// TODO: Fix this below, obviously. | Continuous(m) => Continuous(fn3(m))
let integralXtoY = (~cache as _, f, t) => { };
1337.0;
}; let xToY = (f, t) =>
}); mapToAll(
t,
(Mixed.T.xToY(f), Discrete.T.xToY(f), Continuous.T.xToY(f)),
);
let toShape = (t: t) => t;
let toContinuous = (t: t) =>
mapToAll(
t,
(
Mixed.T.toContinuous,
Discrete.T.toContinuous,
Continuous.T.toContinuous,
),
);
let toDiscrete = (t: t) =>
mapToAll(
t,
(
Mixed.T.toDiscrete,
Discrete.T.toDiscrete,
Continuous.T.toDiscrete,
),
);
let minX = (t: t) =>
mapToAll(t, (Mixed.T.minX, Discrete.T.minX, Continuous.T.minX));
let integral = (~cache, t: t) =>
mapToAll(
t,
(
Mixed.T.Integral.get(~cache),
Discrete.T.Integral.get(~cache),
Continuous.T.Integral.get(~cache),
),
);
let integralSum = (~cache, t: t) =>
mapToAll(
t,
(
Mixed.T.Integral.sum(~cache),
Discrete.T.Integral.sum(~cache),
Continuous.T.Integral.sum(~cache),
),
);
let integralXtoY = (~cache, f, t) => {
mapToAll(
t,
(
Mixed.T.Integral.xToY(~cache, f),
Discrete.T.Integral.xToY(~cache, f),
Continuous.T.Integral.xToY(~cache, f),
),
);
};
let maxX = (t: t) =>
mapToAll(t, (Mixed.T.minX, Discrete.T.minX, Continuous.T.minX));
let pointwiseFmap = (fn, t: t) =>
fmap(
t,
(
Mixed.T.pointwiseFmap(fn),
Discrete.T.pointwiseFmap(fn),
Continuous.T.pointwiseFmap(fn),
),
);
});
};
module WithMetadata = {
module T =
Dist({
type t = DistributionTypes.complexPower;
type integral = DistributionTypes.complexPower;
let toShape = ({shape, _}: t) => shape;
let toContinuous = (t: t) => t |> toShape |> Shape.T.toContinuous;
let toDiscrete = (t: t) => t |> toShape |> Shape.T.toDiscrete;
// todo: adjust for limit, and the fact that total mass is lower.
let xToY = (f, t: t) => t |> toShape |> Shape.T.xToY(f);
let minX = (t: t) => t |> toShape |> Shape.T.minX;
let maxX = (t: t) => t |> toShape |> Shape.T.maxX;
let fromShape = (shape, t): t => DistributionTypes.update(~shape, t);
// todo: adjust for limit
let pointwiseFmap = (fn, {shape, _} as t: t): t =>
fromShape(Shape.T.pointwiseFmap(fn, shape), t);
let integral = (~cache as _, t: t) =>
fromShape(Continuous(t.integralCache), t);
let integralSum = (~cache as _, t: t) =>
t |> toShape |> Shape.T.Integral.sum(~cache=Some(t.integralCache));
// TODO: Fix this below, obviously. Adjust for limit.
let integralXtoY = (~cache as _, f, t) => {
1337.0;
};
});
};

View File

@ -17,21 +17,10 @@ type xyShape = {
type interpolationMethod = [ | `Stepwise | `Linear]; type interpolationMethod = [ | `Stepwise | `Linear];
type continuousShape = { type continuousShape = {
shape: xyShape, xyShape,
interpolation: interpolationMethod, interpolation: interpolationMethod,
}; };
module ContinuousShape = {
type t = continuousShape;
let shape = (t: t) => t.shape;
let interpolation = (t: t) => t.interpolation;
let make = (shape, interpolation) => {shape, interpolation};
let shapeMap = ({shape, interpolation}: t, fn) => {
shape: fn(shape),
interpolation,
};
};
type discreteShape = xyShape; type discreteShape = xyShape;
type mixedShape = { type mixedShape = {
@ -112,3 +101,28 @@ module DistributionUnit = {
| _ => Js.Null.fromOption(None) | _ => Js.Null.fromOption(None)
}; };
}; };
type mixedPoint = {
continuous: float,
discrete: float,
};
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);
};

View File

@ -6,18 +6,20 @@ type pointInRange =
module Continuous = { module Continuous = {
type t = continuousShape; type t = continuousShape;
let shape = (t: t) => t.shape; let xyShape = (t: t) => t.xyShape;
let getShape = (t: t) => t.shape; let getShape = (t: t) => t.xyShape;
let interpolation = (t: t) => t.interpolation; let interpolation = (t: t) => t.interpolation;
let make = (shape, interpolation) => {shape, interpolation}; let make = (xyShape, interpolation): t => {xyShape, interpolation};
let fromShape = shape => make(shape, `Linear); let fromShape = xyShape => make(xyShape, `Linear);
let shapeMap = (fn, {shape, interpolation}: t) => { let shapeMap = (fn, {xyShape, interpolation}: t): t => {
shape: fn(shape), xyShape: fn(xyShape),
interpolation, interpolation,
}; };
let oShapeMap = (fn, {shape, interpolation}: t) => let oShapeMap =
fn(shape) |> E.O.fmap(shape => {shape, interpolation}); (fn, {xyShape, interpolation}: t)
let shapeFn = (fn, t: t) => t |> shape |> fn; : option(DistributionTypes.continuousShape) =>
fn(xyShape) |> E.O.fmap(xyShape => make(xyShape, interpolation));
let shapeFn = (fn, t: t) => t |> xyShape |> fn;
let minX = shapeFn(XYShape.minX); let minX = shapeFn(XYShape.minX);
let maxX = shapeFn(XYShape.maxX); let maxX = shapeFn(XYShape.maxX);
let findX = y => shapeFn(CdfLibrary.Distribution.findX(y)); let findX = y => shapeFn(CdfLibrary.Distribution.findX(y));
@ -29,15 +31,18 @@ module Continuous = {
let findIntegralY = (f, t) => { let findIntegralY = (f, t) => {
t t
|> toCdf |> toCdf
|> E.O.fmap(shape) |> E.O.fmap(xyShape)
|> E.O.fmap(CdfLibrary.Distribution.findY(f)); |> E.O.fmap(CdfLibrary.Distribution.findY(f));
}; };
let normalizeCdf = (continuousShape: continuousShape) => let normalizeCdf = (continuousShape: continuousShape) =>
continuousShape |> shape |> XYShape.scaleCdfTo(~scaleTo=1.0) |> fromShape; continuousShape
|> xyShape
|> XYShape.scaleCdfTo(~scaleTo=1.0)
|> fromShape;
let scalePdf = (~scaleTo=1.0, continuousShape: continuousShape) => { let scalePdf = (~scaleTo=1.0, continuousShape: continuousShape) => {
switch (toCdf(continuousShape)) { switch (toCdf(continuousShape)) {
| Some({shape}) => | Some({xyShape}) =>
XYShape.scaleCdfTo(~scaleTo, shape) XYShape.scaleCdfTo(~scaleTo, xyShape)
|> XYShape.Range.derivative |> XYShape.Range.derivative
|> E.O.fmap(fromShape) |> E.O.fmap(fromShape)
| _ => None | _ => None
@ -162,14 +167,14 @@ module Mixed = {
let clean = (t: DistributionTypes.mixedShape) => let clean = (t: DistributionTypes.mixedShape) =>
switch (t) { switch (t) {
| { | {
continuous: {shape: {xs: [||], ys: [||]}}, continuous: {xyShape: {xs: [||], ys: [||]}},
discrete: {xs: [||], ys: [||]}, discrete: {xs: [||], ys: [||]},
} => } =>
None None
| {discrete: {xs: [|_|], ys: [|_|]}} => None | {discrete: {xs: [|_|], ys: [|_|]}} => None
| {continuous, discrete: {xs: [||], ys: [||]}} => | {continuous, discrete: {xs: [||], ys: [||]}} =>
Some(Continuous(continuous)) Some(Continuous(continuous))
| {continuous: {shape: {xs: [||], ys: [||]}}, discrete} => | {continuous: {xyShape: {xs: [||], ys: [||]}}, discrete} =>
Some(Discrete(discrete)) Some(Discrete(discrete))
| shape => Some(Mixed(shape)) | shape => Some(Mixed(shape))
}; };