From 0c0fe76e4e41d464249570eb814163e573d2f889 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Sat, 22 Feb 2020 11:07:17 +0000 Subject: [PATCH] First iteration on continuous types --- __tests__/Foo/Foo__Test.re | 21 +- .../charts/GenericDistributionChart.re | 30 ++- src/core/DistFunctor.re | 67 +++-- src/core/DistributionTypes.re | 19 +- src/core/GenericDistribution.re | 4 +- src/core/Shape.re | 245 +++++++++--------- src/utility/CdfLibrary.re | 6 +- src/utility/Guesstimator.re | 6 +- 8 files changed, 221 insertions(+), 177 deletions(-) diff --git a/__tests__/Foo/Foo__Test.re b/__tests__/Foo/Foo__Test.re index 4f574267..68b2ecf7 100644 --- a/__tests__/Foo/Foo__Test.re +++ b/__tests__/Foo/Foo__Test.re @@ -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) - ); - }) -); \ No newline at end of file +// describe("Shape", () => +// describe("XYShape", () => { +// test("#ySum", () => +// expect(XYShape.ySum(shape)) |> toEqual(19.0) +// ); +// test("#yFOo", () => +// expect(Discrete.integrate(shape)) |> toEqual(step) +// ); +// }) \ No newline at end of file diff --git a/src/components/charts/GenericDistributionChart.re b/src/components/charts/GenericDistributionChart.re index 3141cff9..e84d5a6b 100644 --- a/src/components/charts/GenericDistributionChart.re +++ b/src/components/charts/GenericDistributionChart.re @@ -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); -
- - {discrete |> E.O.React.fmapOrNull(Shape.Discrete.render)} -
; +
+ // + // {discrete |> E.O.React.fmapOrNull(Shape.Discrete.render)} + //
; + />; }; }; diff --git a/src/core/DistFunctor.re b/src/core/DistFunctor.re index 63f4e0df..92f5d302 100644 --- a/src/core/DistFunctor.re +++ b/src/core/DistFunctor.re @@ -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; }; }); \ No newline at end of file diff --git a/src/core/DistributionTypes.re b/src/core/DistributionTypes.re index 9c298f1f..e941781c 100644 --- a/src/core/DistributionTypes.re +++ b/src/core/DistributionTypes.re @@ -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 = { diff --git a/src/core/GenericDistribution.re b/src/core/GenericDistribution.re index cc5aa2a9..a94b7a2b 100644 --- a/src/core/GenericDistribution.re +++ b/src/core/GenericDistribution.re @@ -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) }; }; diff --git a/src/core/Shape.re b/src/core/Shape.re index c4c9c738..5bb82fa8 100644 --- a/src/core/Shape.re +++ b/src/core/Shape.re @@ -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 = { diff --git a/src/utility/CdfLibrary.re b/src/utility/CdfLibrary.re index 2354331c..aabbc092 100644 --- a/src/utility/CdfLibrary.re +++ b/src/utility/CdfLibrary.re @@ -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"] diff --git a/src/utility/Guesstimator.re b/src/utility/Guesstimator.re index 7407c36f..5729fb2b 100644 --- a/src/utility/Guesstimator.re +++ b/src/utility/Guesstimator.re @@ -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"]