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.|],
};
open Shape;
open Shape /* )*/;
describe("Shape", () =>
describe("XYShape", () => {
test("#ySum", () =>
expect(XYShape.ySum(shape)) |> toEqual(19.0)
);
test("#yFOo", () =>
expect(Discrete.integrate(shape)) |> toEqual(step)
);
})
);
// describe("Shape", () =>
// describe("XYShape", () => {
// test("#ySum", () =>
// expect(XYShape.ySum(shape)) |> toEqual(19.0)
// );
// test("#yFOo", () =>
// expect(Discrete.integrate(shape)) |> toEqual(step)
// );
// })

View File

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

View File

@ -22,12 +22,19 @@ type yPoint =
| Continuous(float)
| Discrete(float);
let yPointCont = (y: yPoint) =>
switch (y) {
| Continuous(f) => f
| Mixed({continuous}) => continuous
| _ => 0.0
};
module YPoint = {
type t = yPoint;
let toContinuousValue = (t: t) =>
switch (t) {
| 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 = {
type t;
@ -64,19 +71,31 @@ module Continuous =
Dist({
type t = DistributionTypes.continuousShape;
type integral = DistributionTypes.continuousShape;
let shape = (t: t) => t.shape;
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
let integralSum = (~cache, t) =>
t |> integral(~cache) |> Shape.XYShape.ySum;
let minX = Shape.XYShape.minX;
let maxX = Shape.XYShape.maxX;
let pointwiseFmap = Shape.XYShape.pointwiseMap;
t |> integral(~cache) |> shape |> Shape.XYShape.ySum;
let minX = (t: t) => t |> shape |> Shape.XYShape.minX;
let maxX = (t: t) => t |> shape |> Shape.XYShape.maxX;
let pointwiseFmap = (fn, t: t) =>
t
|> shape
|> Shape.XYShape.pointwiseMap(fn)
|> Shape.Continuous.fromShape;
let shape = (t: t): DistributionTypes.shape => Continuous(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) =>
t |> integral(~cache) |> CdfLibrary.Distribution.findY(f);
t |> integral(~cache) |> Shape.Continuous.findY(f);
});
module Discrete =
@ -109,7 +128,8 @@ module Mixed =
(f, {discrete, continuous, discreteProbabilityMassFraction}: t) =>
Mixed({
continuous:
CdfLibrary.Distribution.findY(f, continuous)
Continuous.xToY(f, continuous)
|> YPoint.toContinuousValue
|> (e => e *. (1. -. discreteProbabilityMassFraction)),
discrete:
Shape.Discrete.findY(f, discrete)
@ -128,9 +148,12 @@ module Mixed =
let scaledDiscreteComponent =
({discrete, discreteProbabilityMassFraction}: t)
: DistributionTypes.continuousShape =>
Discrete.pointwiseFmap(
f => f *. discreteProbabilityMassFraction,
discrete,
Shape.Continuous.make(
Discrete.pointwiseFmap(
f => f *. discreteProbabilityMassFraction,
discrete,
),
`Stepwise,
);
// TODO: Add these two directly, once interpolation is added.
@ -173,7 +196,9 @@ module Mixed =
(fn, {discrete, continuous, discreteProbabilityMassFraction}: t): t => {
{
discrete: Shape.XYShape.pointwiseMap(fn, discrete),
continuous: Shape.XYShape.pointwiseMap(fn, continuous),
continuous:
continuous
|> Shape.Continuous.shapeMap(Shape.XYShape.pointwiseMap(fn)),
discreteProbabilityMassFraction,
};
};
@ -244,11 +269,11 @@ module WithMetadata =
let pointwiseFmap = (fn, {shape, _} as t: t): t =>
fromShape(Shape.pointwiseFmap(fn, shape), t);
let integral = (~cache, t: t) =>
let integral = (~cache as _, t: 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));
let integralXtoY = (~cache, f, t) => {
let integralXtoY = (~cache as _, f, t) => {
3.0;
};
});

View File

@ -14,9 +14,24 @@ type xyShape = {
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 mixedShape = {

View File

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

View File

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

View File

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

View File

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