From 0d2c53eca4f00cdd60100d9d50e34e6e016ce076 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Sat, 22 Feb 2020 16:24:54 +0000 Subject: [PATCH] Simple module-functor work --- src/core/DistFunctor.re | 524 ++++++++++++++++++---------------- src/core/DistributionTypes.re | 38 ++- src/core/Shape.re | 35 ++- 3 files changed, 327 insertions(+), 270 deletions(-) diff --git a/src/core/DistFunctor.re b/src/core/DistFunctor.re index 6a415da4..d9171e67 100644 --- a/src/core/DistFunctor.re +++ b/src/core/DistFunctor.re @@ -14,38 +14,15 @@ let max = (f1: option(float), f2: option(float)) => | (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 = { type t; let minX: t => option(float); let maxX: t => option(float); let pointwiseFmap: (float => float, t) => t; - let xToY: (float, t) => mixedPoint; - let shape: t => DistributionTypes.shape; + let xToY: (float, t) => DistributionTypes.mixedPoint; + let toShape: t => DistributionTypes.shape; + let toContinuous: t => option(DistributionTypes.continuousShape); + let toDiscrete: t => option(DistributionTypes.discreteShape); type integral; let integral: (~cache: option(integral), t) => integral; @@ -60,7 +37,11 @@ module Dist = (T: dist) => { let maxX = T.maxX; let pointwiseFmap = T.pointwiseFmap; 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 = { type t = T.integral; @@ -70,228 +51,285 @@ module Dist = (T: dist) => { }; }; -module Continuous = - Dist({ - type t = DistributionTypes.continuousShape; - type integral = DistributionTypes.continuousShape; - 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; - let shape = (t: t) => t.shape; - let integral = (~cache, t) => - cache - |> E.O.default( - t - |> shape - |> XYShape.Range.integrateWithTriangles - |> 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 Continuous = { + type t = DistributionTypes.continuousShape; + let xyShape = (t: t) => t.xyShape; + let getShape = (t: t) => t.xyShape; + let interpolation = (t: t) => t.interpolation; + let make = (xyShape, interpolation): t => {xyShape, interpolation}; + let fromShape = xyShape => make(xyShape, `Linear); + let shapeMap = (fn, {xyShape, interpolation}: t): t => { + xyShape: fn(xyShape), + interpolation, + }; + let oShapeMap = + (fn, {xyShape, interpolation}: t) + : option(DistributionTypes.continuousShape) => + fn(xyShape) |> E.O.fmap(xyShape => make(xyShape, interpolation)); -module Discrete = - Dist({ - type t = DistributionTypes.discreteShape; - type integral = DistributionTypes.continuousShape; - let integral = (~cache, t) => - 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) => - CdfLibrary.Distribution.findY(f, t) |> MixedPoint.makeDiscrete; - let integralXtoY = (~cache, f, t) => - t |> XYShape.accumulateYs |> CdfLibrary.Distribution.findY(f); - }); + module T = + Dist({ + type t = DistributionTypes.continuousShape; + type integral = DistributionTypes.continuousShape; + let shapeFn = (fn, t: t) => t |> xyShape |> fn; + let integral = (~cache, t) => + cache + |> E.O.default( + t + |> xyShape + |> XYShape.Range.integrateWithTriangles + |> E.O.toExt("") + |> fromShape, + ); + // This seems wrong, we really want the ending bit, I'd assume + 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 = - 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 = - (f, {discrete, continuous, discreteProbabilityMassFraction}: t) => { - let c = - continuous - |> Continuous.xToY(f) - |> MixedPoint.fmap(e => e *. (1. -. discreteProbabilityMassFraction)); - let d = +module Discrete = { + module T = + Dist({ + type t = DistributionTypes.discreteShape; + type integral = DistributionTypes.continuousShape; + let integral = (~cache, t) => + cache + |> E.O.default(t |> XYShape.accumulateYs |> Continuous.fromShape); + let integralSum = (~cache, t) => t |> XYShape.ySum; + let minX = XYShape.minX; + let maxX = XYShape.maxX; + let pointwiseFmap = XYShape.pointwiseMap; + let toShape = (t: t): DistributionTypes.shape => Discrete(t); + let toContinuous = _ => None; + let toDiscrete = t => Some(t); + 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.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 - +. Continuous.Integral.sum(~cache=None, continuous) - *. (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) + +. cont + *. (1.0 -. discreteProbabilityMassFraction); }; - 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 pointwiseFmap = + (fn, {discrete, continuous, discreteProbabilityMassFraction}: t): t => { + { + discrete: Discrete.T.pointwiseFmap(fn, discrete), + continuous: Continuous.T.pointwiseFmap(fn, continuous), + discreteProbabilityMassFraction, + }; }; + }); +}; - let xToY = (f, t) => - mapToAll(t, (Mixed.xToY(f), Discrete.xToY(f), Continuous.xToY(f))); - let shape = (t: t) => t; - let minX = (t: t) => - mapToAll(t, (Mixed.minX, Discrete.minX, Continuous.minX)); - 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 Shape = { + module T = + Dist({ + type t = DistributionTypes.shape; + type integral = DistributionTypes.continuousShape; -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); + let mapToAll = (t: t, (fn1, fn2, fn3)) => + switch (t) { + | Mixed(m) => fn1(m) + | Discrete(m) => fn2(m) + | Continuous(m) => fn3(m) + }; - let integral = (~cache as _, t: t) => - fromShape(Continuous(t.integralCache), t); - let integralSum = (~cache as _, t: t) => - t |> shape |> Shape.Integral.sum(~cache=Some(t.integralCache)); - // TODO: Fix this below, obviously. - let integralXtoY = (~cache as _, f, t) => { - 1337.0; - }; - }); \ No newline at end of file + 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) => + 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; + }; + }); +}; \ No newline at end of file diff --git a/src/core/DistributionTypes.re b/src/core/DistributionTypes.re index e941781c..9a3b5612 100644 --- a/src/core/DistributionTypes.re +++ b/src/core/DistributionTypes.re @@ -17,21 +17,10 @@ type xyShape = { type interpolationMethod = [ | `Stepwise | `Linear]; type continuousShape = { - shape: xyShape, + 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 discreteShape = xyShape; type mixedShape = { @@ -111,4 +100,29 @@ module DistributionUnit = { ) | _ => 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); }; \ No newline at end of file diff --git a/src/core/Shape.re b/src/core/Shape.re index fc414c37..0cc1872d 100644 --- a/src/core/Shape.re +++ b/src/core/Shape.re @@ -6,18 +6,20 @@ type pointInRange = module Continuous = { type t = continuousShape; - let shape = (t: t) => t.shape; - let getShape = (t: t) => t.shape; + let xyShape = (t: t) => t.xyShape; + let getShape = (t: t) => t.xyShape; 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), + let make = (xyShape, interpolation): t => {xyShape, interpolation}; + let fromShape = xyShape => make(xyShape, `Linear); + let shapeMap = (fn, {xyShape, interpolation}: t): t => { + xyShape: fn(xyShape), interpolation, }; - let oShapeMap = (fn, {shape, interpolation}: t) => - fn(shape) |> E.O.fmap(shape => {shape, interpolation}); - let shapeFn = (fn, t: t) => t |> shape |> fn; + let oShapeMap = + (fn, {xyShape, interpolation}: t) + : 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 maxX = shapeFn(XYShape.maxX); let findX = y => shapeFn(CdfLibrary.Distribution.findX(y)); @@ -29,15 +31,18 @@ module Continuous = { let findIntegralY = (f, t) => { t |> toCdf - |> E.O.fmap(shape) + |> E.O.fmap(xyShape) |> E.O.fmap(CdfLibrary.Distribution.findY(f)); }; 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) => { switch (toCdf(continuousShape)) { - | Some({shape}) => - XYShape.scaleCdfTo(~scaleTo, shape) + | Some({xyShape}) => + XYShape.scaleCdfTo(~scaleTo, xyShape) |> XYShape.Range.derivative |> E.O.fmap(fromShape) | _ => None @@ -162,14 +167,14 @@ module Mixed = { let clean = (t: DistributionTypes.mixedShape) => switch (t) { | { - continuous: {shape: {xs: [||], ys: [||]}}, + continuous: {xyShape: {xs: [||], ys: [||]}}, discrete: {xs: [||], ys: [||]}, } => None | {discrete: {xs: [|_|], ys: [|_|]}} => None | {continuous, discrete: {xs: [||], ys: [||]}} => Some(Continuous(continuous)) - | {continuous: {shape: {xs: [||], ys: [||]}}, discrete} => + | {continuous: {xyShape: {xs: [||], ys: [||]}}, discrete} => Some(Discrete(discrete)) | shape => Some(Mixed(shape)) };