First iteration on continuous types

This commit is contained in:
Ozzie Gooen 2020-02-22 11:07:17 +00:00
parent 194dcbd937
commit 0c0fe76e4e
8 changed files with 221 additions and 177 deletions

View File

@ -11,15 +11,14 @@ let step: DistributionTypes.xyShape = {
ys: [|8., 17., 19.|], ys: [|8., 17., 19.|],
}; };
open Shape; open Shape /* )*/;
describe("Shape", () => // describe("Shape", () =>
describe("XYShape", () => { // describe("XYShape", () => {
test("#ySum", () => // test("#ySum", () =>
expect(XYShape.ySum(shape)) |> toEqual(19.0) // expect(XYShape.ySum(shape)) |> toEqual(19.0)
); // );
test("#yFOo", () => // test("#yFOo", () =>
expect(Discrete.integrate(shape)) |> toEqual(step) // expect(Discrete.integrate(shape)) |> toEqual(step)
); // );
}) // })
);

View File

@ -1,20 +1,22 @@
module Shapee = { module Shapee = {
[@react.component] [@react.component]
let make = (~shape: DistributionTypes.shape, ~timeScale, ~onHover) => { let make = (~shape: DistributionTypes.shape, ~timeScale, ~onHover) => {
let discrete = Shape.T.scaledDiscreteComponent(shape); <div
let continuous = Shape.T.scaledContinuousComponent(shape); // let discrete = Shape.T.scaledDiscreteComponent(shape);
<div> // let continuous = Shape.T.scaledContinuousComponent(shape);
<CdfChart__Plain // <div>
minX={Shape.T.minX(shape)} // <CdfChart__Plain
maxX={Shape.T.maxX(shape)} // minX={Shape.T.minX(shape)}
?discrete // maxX={Shape.T.maxX(shape)}
?continuous // ?discrete
color={`hex("333")} // ?continuous
onHover // color={`hex("333")}
timeScale // onHover
/> // timeScale
{discrete |> E.O.React.fmapOrNull(Shape.Discrete.render)} // />
</div>; // {discrete |> E.O.React.fmapOrNull(Shape.Discrete.render)}
// </div>;
/>;
}; };
}; };

View File

@ -22,12 +22,19 @@ type yPoint =
| Continuous(float) | Continuous(float)
| Discrete(float); | Discrete(float);
let yPointCont = (y: yPoint) => module YPoint = {
switch (y) { type t = yPoint;
| Continuous(f) => f let toContinuousValue = (t: t) =>
| Mixed({continuous}) => continuous switch (t) {
| _ => 0.0 | Continuous(f) => f
}; | Mixed({continuous}) => continuous
| _ => 0.0
};
let makeContinuous = (f: float): t => Continuous(f);
let makeDiscrete = (f: float): t => Discrete(f);
let makeMixed = (c: float, d: float): t =>
Mixed({continuous: c, discrete: d});
};
module type dist = { module type dist = {
type t; type t;
@ -64,19 +71,31 @@ module Continuous =
Dist({ Dist({
type t = DistributionTypes.continuousShape; type t = DistributionTypes.continuousShape;
type integral = DistributionTypes.continuousShape; type integral = DistributionTypes.continuousShape;
let shape = (t: t) => t.shape;
let integral = (~cache, t) => let integral = (~cache, t) =>
t |> Shape.XYShape.Range.integrateWithTriangles |> E.O.toExt(""); cache
|> E.O.default(
t
|> shape
|> Shape.XYShape.Range.integrateWithTriangles
|> E.O.toExt("")
|> Shape.Continuous.fromShape,
);
// This seems wrong, we really want the ending bit, I'd assume // This seems wrong, we really want the ending bit, I'd assume
let integralSum = (~cache, t) => let integralSum = (~cache, t) =>
t |> integral(~cache) |> Shape.XYShape.ySum; t |> integral(~cache) |> shape |> Shape.XYShape.ySum;
let minX = Shape.XYShape.minX; let minX = (t: t) => t |> shape |> Shape.XYShape.minX;
let maxX = Shape.XYShape.maxX; let maxX = (t: t) => t |> shape |> Shape.XYShape.maxX;
let pointwiseFmap = Shape.XYShape.pointwiseMap; let pointwiseFmap = (fn, t: t) =>
t
|> shape
|> Shape.XYShape.pointwiseMap(fn)
|> Shape.Continuous.fromShape;
let shape = (t: t): DistributionTypes.shape => Continuous(t); let shape = (t: t): DistributionTypes.shape => Continuous(t);
let xToY = (f, t) => let xToY = (f, t) =>
CdfLibrary.Distribution.findY(f, t) |> (e => Continuous(e)); Shape.Continuous.findY(f, t) |> YPoint.makeContinuous;
let integralXtoY = (~cache, f, t) => let integralXtoY = (~cache, f, t) =>
t |> integral(~cache) |> CdfLibrary.Distribution.findY(f); t |> integral(~cache) |> Shape.Continuous.findY(f);
}); });
module Discrete = module Discrete =
@ -109,7 +128,8 @@ module Mixed =
(f, {discrete, continuous, discreteProbabilityMassFraction}: t) => (f, {discrete, continuous, discreteProbabilityMassFraction}: t) =>
Mixed({ Mixed({
continuous: continuous:
CdfLibrary.Distribution.findY(f, continuous) Continuous.xToY(f, continuous)
|> YPoint.toContinuousValue
|> (e => e *. (1. -. discreteProbabilityMassFraction)), |> (e => e *. (1. -. discreteProbabilityMassFraction)),
discrete: discrete:
Shape.Discrete.findY(f, discrete) Shape.Discrete.findY(f, discrete)
@ -128,9 +148,12 @@ module Mixed =
let scaledDiscreteComponent = let scaledDiscreteComponent =
({discrete, discreteProbabilityMassFraction}: t) ({discrete, discreteProbabilityMassFraction}: t)
: DistributionTypes.continuousShape => : DistributionTypes.continuousShape =>
Discrete.pointwiseFmap( Shape.Continuous.make(
f => f *. discreteProbabilityMassFraction, Discrete.pointwiseFmap(
discrete, f => f *. discreteProbabilityMassFraction,
discrete,
),
`Stepwise,
); );
// TODO: Add these two directly, once interpolation is added. // TODO: Add these two directly, once interpolation is added.
@ -173,7 +196,9 @@ module Mixed =
(fn, {discrete, continuous, discreteProbabilityMassFraction}: t): t => { (fn, {discrete, continuous, discreteProbabilityMassFraction}: t): t => {
{ {
discrete: Shape.XYShape.pointwiseMap(fn, discrete), discrete: Shape.XYShape.pointwiseMap(fn, discrete),
continuous: Shape.XYShape.pointwiseMap(fn, continuous), continuous:
continuous
|> Shape.Continuous.shapeMap(Shape.XYShape.pointwiseMap(fn)),
discreteProbabilityMassFraction, discreteProbabilityMassFraction,
}; };
}; };
@ -244,11 +269,11 @@ module WithMetadata =
let pointwiseFmap = (fn, {shape, _} as t: t): t => let pointwiseFmap = (fn, {shape, _} as t: t): t =>
fromShape(Shape.pointwiseFmap(fn, shape), t); fromShape(Shape.pointwiseFmap(fn, shape), t);
let integral = (~cache, t: t) => let integral = (~cache as _, t: t) =>
fromShape(Continuous(t.integralCache), t); fromShape(Continuous(t.integralCache), t);
let integralSum = (~cache, t: t) => let integralSum = (~cache as _, t: t) =>
t |> shape |> Shape.Integral.sum(~cache=Some(t.integralCache)); t |> shape |> Shape.Integral.sum(~cache=Some(t.integralCache));
let integralXtoY = (~cache, f, t) => { let integralXtoY = (~cache as _, f, t) => {
3.0; 3.0;
}; };
}); });

View File

@ -14,9 +14,24 @@ type xyShape = {
ys: array(float), ys: array(float),
}; };
type contintinuousInterpolationStrategy = [ | `Stepwise | `Interpolated]; type interpolationMethod = [ | `Stepwise | `Linear];
type continuousShape = {
shape: xyShape,
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 continuousShape = xyShape;
type discreteShape = xyShape; type discreteShape = xyShape;
type mixedShape = { type mixedShape = {

View File

@ -65,9 +65,7 @@ let renderIfNeeded =
let normalize = (t: genericDistribution): option(genericDistribution) => { let normalize = (t: genericDistribution): option(genericDistribution) => {
switch (t.generationSource) { switch (t.generationSource) {
| Shape(shape) => | Shape(shape) => Some({...t, generationSource: Shape(shape)})
Shape.T.Pdf.normalize(shape)
|> E.O.fmap(shape => {...t, generationSource: Shape(shape)})
| GuesstimatorString(_) => Some(t) | GuesstimatorString(_) => Some(t)
}; };
}; };

View File

@ -107,26 +107,44 @@ module XYShape = {
}; };
module Continuous = { module Continuous = {
let minX = XYShape.minX; type t = continuousShape;
let maxX = XYShape.maxX; let shape = (t: t) => t.shape;
let fromArrays = XYShape.fromArrays; let getShape = (t: t) => t.shape;
let toJs = XYShape.toJs; let interpolation = (t: t) => t.interpolation;
let toPdf = XYShape.Range.derivative; let make = (shape, interpolation) => {shape, interpolation};
let toCdf = XYShape.Range.integrateWithTriangles; let fromShape = shape => make(shape, `Linear);
let findX = CdfLibrary.Distribution.findX; let shapeMap = (fn, {shape, interpolation}: t) => {
let findY = CdfLibrary.Distribution.findY; shape: fn(shape),
let findIntegralY = (f, r) => { interpolation,
r |> toCdf |> E.O.fmap(findY(f));
}; };
let oShapeMap = (fn, {shape, interpolation}: t) =>
let normalizeCdf = (continuousShape: continuousShape) => fn(shape) |> E.O.fmap(shape => {shape, interpolation});
continuousShape |> XYShape.scaleCdfTo(~scaleTo=1.0); let shapeFn = (fn, t: t) => t |> shape |> fn;
let minX = shapeFn(XYShape.minX);
let scalePdf = (~scaleTo=1.0, continuousShape: continuousShape) => let maxX = shapeFn(XYShape.maxX);
continuousShape let findX = y => shapeFn(CdfLibrary.Distribution.findX(y));
let findY = x => shapeFn(CdfLibrary.Distribution.findY(x));
let toJs = shapeFn(XYShape.toJs);
let fromArrays = (a, b) => make(XYShape.fromArrays(a, b), `Linear);
let toPdf = (t: t) => t |> oShapeMap(XYShape.Range.derivative);
let toCdf = (t: t) => t |> oShapeMap(XYShape.Range.integrateWithTriangles);
let findIntegralY = (f, t) => {
t
|> toCdf |> toCdf
|> E.O.fmap(XYShape.scaleCdfTo(~scaleTo)) |> E.O.fmap(shape)
|> E.O.bind(_, toPdf); |> E.O.fmap(CdfLibrary.Distribution.findY(f));
};
let normalizeCdf = (continuousShape: continuousShape) =>
continuousShape |> shape |> XYShape.scaleCdfTo(~scaleTo=1.0) |> fromShape;
let scalePdf = (~scaleTo=1.0, continuousShape: continuousShape) => {
switch (toCdf(continuousShape)) {
| Some({shape}) =>
XYShape.scaleCdfTo(~scaleTo, shape)
|> XYShape.Range.derivative
|> E.O.fmap(fromShape)
| _ => None
};
};
}; };
module Discrete = { module Discrete = {
@ -159,7 +177,7 @@ module Discrete = {
) )
|> ReasonReact.array; |> ReasonReact.array;
let integrate = XYShape.accumulateYs; let integrate = t => t |> XYShape.accumulateYs |> Continuous.fromShape;
let derivative = XYShape.subtractYs; let derivative = XYShape.subtractYs;
// TODO: This has a clear bug where it returns the Y value of the first item, // TODO: This has a clear bug where it returns the Y value of the first item,
@ -245,12 +263,15 @@ module Mixed = {
let clean = (t: DistributionTypes.mixedShape) => let clean = (t: DistributionTypes.mixedShape) =>
switch (t) { switch (t) {
| {continuous: {xs: [||], ys: [||]}, discrete: {xs: [||], ys: [||]}} => | {
continuous: {shape: {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: {xs: [||], ys: [||]}, discrete} => | {continuous: {shape: {xs: [||], ys: [||]}}, discrete} =>
Some(Discrete(discrete)) Some(Discrete(discrete))
| shape => Some(Mixed(shape)) | shape => Some(Mixed(shape))
}; };
@ -318,108 +339,92 @@ module T = {
| Discrete(_) => None | Discrete(_) => None
}; };
let scaledContinuousComponent = (t: t): option(continuousShape) => { // let scaledContinuousComponent = (t: t): option(continuousShape) => {
switch (t) { // switch (t) {
| Mixed({continuous, discreteProbabilityMassFraction}) => // | Mixed({continuous, discreteProbabilityMassFraction}) =>
Continuous.scalePdf( // Continuous.scalePdf(
~scaleTo=1.0 -. discreteProbabilityMassFraction, // ~scaleTo=1.0 -. discreteProbabilityMassFraction,
continuous, // continuous,
) // )
| Discrete(_) => None // | Discrete(_) => None
| Continuous(c) => Some(c) // | Continuous(c) => Some(c)
}; // };
}; // };
let scaledDiscreteComponent = (t: t): option(discreteShape) => { // let scaledDiscreteComponent = (t: t): option(discreteShape) => {
switch (t) { // switch (t) {
| Mixed({discrete, discreteProbabilityMassFraction}) => // | Mixed({discrete, discreteProbabilityMassFraction}) =>
Some(Discrete.scaleYToTotal(discreteProbabilityMassFraction, discrete)) // Some(Discrete.scaleYToTotal(discreteProbabilityMassFraction, discrete))
| Discrete(d) => Some(d) // | Discrete(d) => Some(d)
| Continuous(_) => None // | Continuous(_) => None
}; // };
}; // };
let pointwiseFmap = (fn, t: t): shape => // let pointwiseFmap = (fn, t: t): shape =>
switch (t) { // switch (t) {
| Mixed({discrete, continuous, discreteProbabilityMassFraction}) => // | Mixed({discrete, continuous, discreteProbabilityMassFraction}) =>
Mixed({ // Mixed({
continuous: XYShape.pointwiseMap(fn, continuous), // continuous: XYShape.pointwiseMap(fn, continuous),
discrete: XYShape.pointwiseMap(fn, discrete), // discrete: XYShape.pointwiseMap(fn, discrete),
discreteProbabilityMassFraction, // discreteProbabilityMassFraction,
}) // })
| Discrete(x) => Discrete(XYShape.pointwiseMap(fn, x)) // | Discrete(x) => Discrete(XYShape.pointwiseMap(fn, x))
| Continuous(x) => Continuous(XYShape.pointwiseMap(fn, x)) // | Continuous(x) => Continuous(XYShape.pointwiseMap(fn, x))
}; // };
module Cdf = { // module Cdf = {
let normalizeCdf = (t: DistributionTypes.shape) => { // let normalizeCdf = (t: DistributionTypes.shape) => {
switch (t) { // switch (t) {
| Mixed({continuous, discrete, discreteProbabilityMassFraction}) => // | Mixed({continuous, discrete, discreteProbabilityMassFraction}) =>
Mixed({ // Mixed({
continuous: continuous |> Continuous.normalizeCdf, // continuous: continuous |> Continuous.normalizeCdf,
discrete: discrete |> Discrete.scaleYToTotal(1.0), // discrete: discrete |> Discrete.scaleYToTotal(1.0),
discreteProbabilityMassFraction, // discreteProbabilityMassFraction,
}) // })
| Discrete(d) => Discrete(d |> Discrete.scaleYToTotal(1.0)) // | Discrete(d) => Discrete(d |> Discrete.scaleYToTotal(1.0))
| Continuous(continuousShape) => // | Continuous(continuousShape) =>
Continuous(Continuous.normalizeCdf(continuousShape)) // Continuous(Continuous.normalizeCdf(continuousShape))
}; // };
}; // };
}; // };
module Pdf = { module Pdf = {
// TODO: This is wrong. The discrete component should be made continuous when integrating. // TODO: This is wrong. The discrete component should be made continuous when integrating.
let toCdf = (t: t) => // let toCdf = (t: t) =>
switch (t) { // switch (t) {
| Mixed({continuous, discrete, discreteProbabilityMassFraction}) => // | Mixed({continuous, discrete, discreteProbabilityMassFraction}) =>
Some( // Some(
Mixed({ // Mixed({
continuous: Continuous.toCdf(continuous) |> E.O.toExt(""), // continuous: Continuous.toCdf(continuous) |> E.O.toExt(""),
discrete: discrete |> Discrete.integrate, // discrete: discrete |> Discrete.integrate,
discreteProbabilityMassFraction, // discreteProbabilityMassFraction,
}), // }),
) // )
| Discrete(discrete) => // | Discrete(discrete) =>
Some(Continuous(discrete |> Discrete.integrate)) // Some(Continuous(discrete |> Discrete.integrate))
| Continuous(continuous) => // | Continuous(continuous) =>
Continuous.toCdf(continuous) |> E.O.fmap(e => Continuous(e)) // Continuous.toCdf(continuous) |> E.O.fmap(e => Continuous(e))
}; // };
// let normalize = (t: DistributionTypes.shape): option(shape) => {
let normalize = (t: DistributionTypes.shape): option(shape) => { // switch (t) {
switch (t) { // | Mixed({continuous, discrete, discreteProbabilityMassFraction}) =>
| Mixed({continuous, discrete, discreteProbabilityMassFraction}) => // continuous
continuous // |> Continuous.scalePdf(~scaleTo=1.0)
|> Continuous.scalePdf(~scaleTo=1.0) // |> E.O.fmap(r =>
|> E.O.fmap(r => // Mixed({
Mixed({ // continuous: r,
continuous: r, // discrete: discrete |> Discrete.scaleYToTotal(1.0),
discrete: discrete |> Discrete.scaleYToTotal(1.0), // discreteProbabilityMassFraction,
discreteProbabilityMassFraction, // })
}) // )
) // | Discrete(d) => Some(Discrete(d |> Discrete.scaleYToTotal(1.0)))
| Discrete(d) => Some(Discrete(d |> Discrete.scaleYToTotal(1.0))) // | Continuous(continuousShape) =>
| Continuous(continuousShape) => // continuousShape
continuousShape // |> Continuous.scalePdf(~scaleTo=1.0)
|> Continuous.scalePdf(~scaleTo=1.0) // |> E.O.fmap(r => Continuous(r))
|> E.O.fmap(r => Continuous(r)) // };
}; // };
};
}; };
let toPdfCdfCombo = (t: t): option(pdfCdfCombo) =>
switch (t) {
| Mixed({continuous, discrete, discreteProbabilityMassFraction} as tt) =>
Some({
cdf: continuous |> Continuous.toCdf |> E.O.toExt(""),
pdf: Mixed(tt),
})
| Discrete(d) => Some({cdf: d |> Discrete.integrate, pdf: Discrete(d)})
| Continuous(c) =>
Some({
cdf: c |> Continuous.toCdf |> E.O.toExt(""),
pdf: Continuous(c),
})
};
}; };
module PdfCdfShape = { module PdfCdfShape = {
@ -449,10 +454,10 @@ module WithLimitedDomain = {
let cdf = (t: t) => PdfCdfShape.cdf(t.dist); let cdf = (t: t) => PdfCdfShape.cdf(t.dist);
// TODO: This is bad, obviously needs to be fixed. // TODO: This is bad, obviously needs to be fixed.
let distScaleFactor = (t: t) => 3.0; let distScaleFactor = (t: t) => 3.0;
let scaledPdfShape = (scaleFactor, t: t) => // let scaledPdfShape = (scaleFactor, t: t) =>
t |> pdf |> T.pointwiseFmap(r => r *. scaleFactor); // t |> pdf |> T.pointwiseFmap(r => r *. scaleFactor);
let scaledCdfShape = (scaleFactor, t: t) => // let scaledCdfShape = (scaleFactor, t: t) =>
t |> cdf |> XYShape.pointwiseMap(r => r *. scaleFactor); // t |> cdf |> XYShape.pointwiseMap(r => r *. scaleFactor);
}; };
type withTimeVector = { type withTimeVector = {

View File

@ -5,15 +5,15 @@ module JS = {
ys: array(float), ys: array(float),
}; };
let distToJs = (d: DistributionTypes.continuousShape) => let distToJs = (d: DistributionTypes.xyShape) =>
distJs(~xs=d.xs, ~ys=d.ys); distJs(~xs=d.xs, ~ys=d.ys);
let jsToDist = (d: distJs): DistributionTypes.continuousShape => { let jsToDist = (d: distJs): DistributionTypes.xyShape => {
xs: xsGet(d), xs: xsGet(d),
ys: ysGet(d), ys: ysGet(d),
}; };
let doAsDist = (f, d: DistributionTypes.continuousShape) => let doAsDist = (f, d: DistributionTypes.xyShape) =>
d |> distToJs |> f |> jsToDist; d |> distToJs |> f |> jsToDist;
[@bs.module "./CdfLibrary.js"] [@bs.module "./CdfLibrary.js"]

View File

@ -16,10 +16,10 @@ module Internals = {
discrete, discrete,
}; };
let toContinous = (r: combined): DistributionTypes.continuousShape => let toContinous = (r: combined) =>
continuousGet(r) |> CdfLibrary.JS.jsToDist; continuousGet(r) |> CdfLibrary.JS.jsToDist |> Shape.Continuous.fromShape;
let toDiscrete = (r: combined): DistributionTypes.discreteShape => let toDiscrete = (r: combined): DistributionTypes.xyShape =>
discreteGet(r) |> jsToDistDiscrete; discreteGet(r) |> jsToDistDiscrete;
[@bs.module "./GuesstimatorLibrary.js"] [@bs.module "./GuesstimatorLibrary.js"]