diff --git a/src/components/charts/CdfChart__Plain.re b/src/components/charts/CdfChart__Plain.re index 3e3cee80..62c7a05b 100644 --- a/src/components/charts/CdfChart__Plain.re +++ b/src/components/charts/CdfChart__Plain.re @@ -45,7 +45,7 @@ let make = ?minX ?scale ?timeScale - discrete={discrete |> E.O.fmap(Shape.Discrete.toJs)} + discrete={discrete |> E.O.fmap(XYShape.toJs)} height marginBottom=50 marginTop=0 diff --git a/src/core/DistFunctor.re b/src/core/DistFunctor.re index d9171e67..3f04be51 100644 --- a/src/core/DistFunctor.re +++ b/src/core/DistFunctor.re @@ -49,6 +49,12 @@ module Dist = (T: dist) => { let xToY = T.integralXtoY; let sum = T.integralSum; }; + + // This is suboptimal because it could get the cache but doesn't here. + let scaleToIntegralSum = (~intendedSum=1.0, t: t) => { + let scale = intendedSum /. Integral.sum(~cache=None, t); + scaleBy(~scale, t); + }; }; module Continuous = { @@ -123,6 +129,31 @@ module Discrete = { }; module Mixed = { + let make = + (~continuous, ~discrete, ~discreteProbabilityMassFraction) + : DistributionTypes.mixedShape => { + continuous, + discrete, + discreteProbabilityMassFraction, + }; + + let clean = + (t: DistributionTypes.mixedShape): option(DistributionTypes.shape) => { + switch (t) { + | { + continuous: {xyShape: {xs: [||], ys: [||]}}, + discrete: {xs: [||], ys: [||]}, + } => + None + | {discrete: {xs: [|_|], ys: [|_|]}} => None + | {continuous, discrete: {xs: [||], ys: [||]}} => + Some(Continuous(continuous)) + | {continuous: {xyShape: {xs: [||], ys: [||]}}, discrete} => + Some(Discrete(discrete)) + | shape => Some(Mixed(shape)) + }; + }; + module T = Dist({ type t = DistributionTypes.mixedShape; @@ -312,24 +343,24 @@ module WithMetadata = { 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; + let shapeFn = (fn, t: t) => t |> toShape |> fn; + let toContinuous = shapeFn(Shape.T.toContinuous); + let toDiscrete = shapeFn(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 xToY = f => shapeFn(Shape.T.xToY(f)); + let minX = shapeFn(Shape.T.minX); + let maxX = shapeFn(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)); + Shape.T.Integral.sum(~cache=Some(t.integralCache), toShape(t)); // TODO: Fix this below, obviously. Adjust for limit. - let integralXtoY = (~cache as _, f, t) => { - 1337.0; + let integralXtoY = (~cache as _, f, t: t) => { + Shape.T.Integral.xToY(~cache=Some(t.integralCache), f, toShape(t)); }; }); }; \ No newline at end of file diff --git a/src/core/GenericDistribution.re b/src/core/GenericDistribution.re index a94b7a2b..a34bb581 100644 --- a/src/core/GenericDistribution.re +++ b/src/core/GenericDistribution.re @@ -49,7 +49,7 @@ let renderIfNeeded = switch (t.generationSource) { | GuesstimatorString(s) => Guesstimator.stringToMixedShape(~string=s, ~sampleCount, ()) - |> E.O.bind(_, Shape.Mixed.clean) + |> E.O.bind(_, DistFunctor.Mixed.clean) |> E.O.fmap((shape: DistributionTypes.shape) => make( ~generationSource=Shape(shape), @@ -71,13 +71,9 @@ let normalize = (t: genericDistribution): option(genericDistribution) => { }; let yIntegral = (t: DistributionTypes.genericDistribution, x) => { - let addInitialMass = n => n +. Domain.initialProbabilityMass(t.domain); - let normalize = n => n *. Domain.normalizeProbabilityMass(t.domain); switch (t) { | {generationSource: Shape(shape)} => - Shape.T.yIntegral(shape, x) - |> E.O.fmap(addInitialMass) - |> E.O.fmap(normalize) + Some(DistFunctor.Shape.T.Integral.xToY(~cache=None, x, shape)) | _ => None }; }; diff --git a/src/core/MixedShapeBuilder.re b/src/core/MixedShapeBuilder.re index af2e7e8b..b571f57a 100644 --- a/src/core/MixedShapeBuilder.re +++ b/src/core/MixedShapeBuilder.re @@ -17,7 +17,7 @@ let build = (~continuous, ~discrete, ~assumptions) => } => // TODO: Fix this, it's wrong :( Some( - Shape.Mixed.make( + DistFunctor.Mixed.make( ~continuous, ~discrete, ~discreteProbabilityMassFraction=r, @@ -30,7 +30,7 @@ let build = (~continuous, ~discrete, ~assumptions) => discreteProbabilityMass: Some(r), } => Some( - Shape.Mixed.make( + DistFunctor.Mixed.make( ~continuous, ~discrete, ~discreteProbabilityMassFraction=r, @@ -56,10 +56,12 @@ let build = (~continuous, ~discrete, ~assumptions) => discrete: ADDS_TO_CORRECT_PROBABILITY, discreteProbabilityMass: None, } => - let discreteProbabilityMassFraction = Shape.Discrete.ySum(discrete); - let discrete = Shape.Discrete.scaleYToTotal(1.0, discrete); + let discreteProbabilityMassFraction = + DistFunctor.Discrete.T.Integral.sum(~cache=None, discrete); + let discrete = + DistFunctor.Discrete.T.scaleToIntegralSum(~intendedSum=1.0, discrete); Some( - Shape.Mixed.make( + DistFunctor.Mixed.make( ~continuous, ~discrete, ~discreteProbabilityMassFraction, diff --git a/src/core/Shape.re b/src/core/Shape.re index 0cc1872d..801f23af 100644 --- a/src/core/Shape.re +++ b/src/core/Shape.re @@ -115,11 +115,11 @@ let max = (f1: option(float), f2: option(float)) => }; module Mixed = { - let make = (~continuous, ~discrete, ~discreteProbabilityMassFraction) => { - continuous, - discrete, - discreteProbabilityMassFraction, - }; + // let make = (~continuous, ~discrete, ~discreteProbabilityMassFraction) => { + // continuous, + // discrete, + // discreteProbabilityMassFraction, + // }; let minX = (t: DistributionTypes.mixedShape) => min(t.continuous |> Continuous.minX, t.discrete |> Discrete.minX); @@ -163,193 +163,135 @@ module Mixed = { | _ => None }; }; +} /* }*/; - let clean = (t: DistributionTypes.mixedShape) => - switch (t) { - | { - continuous: {xyShape: {xs: [||], ys: [||]}}, - discrete: {xs: [||], ys: [||]}, - } => - None - | {discrete: {xs: [|_|], ys: [|_|]}} => None - | {continuous, discrete: {xs: [||], ys: [||]}} => - Some(Continuous(continuous)) - | {continuous: {xyShape: {xs: [||], ys: [||]}}, discrete} => - Some(Discrete(discrete)) - | shape => Some(Mixed(shape)) - }; -}; +// module T = { +// type t = DistributionTypes.shape; -module T = { - type t = DistributionTypes.shape; +// let y = (t: t, x: float) => +// switch (t) { +// | Mixed(m) => `mixed(Mixed.findY(m, x)) +// | Discrete(discreteShape) => `discrete(Discrete.findY(x, discreteShape)) +// | Continuous(continuousShape) => +// `continuous(Continuous.findY(x, continuousShape)) +// }; - let y = (t: t, x: float) => - switch (t) { - | Mixed(m) => `mixed(Mixed.findY(m, x)) - | Discrete(discreteShape) => `discrete(Discrete.findY(x, discreteShape)) - | Continuous(continuousShape) => - `continuous(Continuous.findY(x, continuousShape)) - }; +// let yIntegral = (t: t, x: float) => +// switch (t) { +// | Mixed(m) => Mixed.findYIntegral(x, m) +// | Discrete(discreteShape) => +// Discrete.findIntegralY(x, discreteShape) |> E.O.some +// | Continuous(continuousShape) => +// Continuous.findIntegralY(x, continuousShape) +// }; - let yIntegral = (t: t, x: float) => - switch (t) { - | Mixed(m) => Mixed.findYIntegral(x, m) - | Discrete(discreteShape) => - Discrete.findIntegralY(x, discreteShape) |> E.O.some - | Continuous(continuousShape) => - Continuous.findIntegralY(x, continuousShape) - }; +// let minX = (t: t) => +// switch (t) { +// | Mixed(m) => Mixed.minX(m) +// | Discrete(discreteShape) => Discrete.minX(discreteShape) +// | Continuous(continuousShape) => Continuous.minX(continuousShape) +// }; - let minX = (t: t) => - switch (t) { - | Mixed(m) => Mixed.minX(m) - | Discrete(discreteShape) => Discrete.minX(discreteShape) - | Continuous(continuousShape) => Continuous.minX(continuousShape) - }; +// let maxX = (t: t) => +// switch (t) { +// | Mixed(m) => Mixed.maxX(m) +// | Discrete(discreteShape) => Discrete.maxX(discreteShape) +// | Continuous(continuousShape) => Continuous.maxX(continuousShape) +// }; - let maxX = (t: t) => - switch (t) { - | Mixed(m) => Mixed.maxX(m) - | Discrete(discreteShape) => Discrete.maxX(discreteShape) - | Continuous(continuousShape) => Continuous.maxX(continuousShape) - }; +// let discreteComponent = (t: t) => +// switch (t) { +// | Mixed({discrete}) => Some(discrete) +// | Discrete(d) => Some(d) +// | Continuous(_) => None +// }; - let discreteComponent = (t: t) => - switch (t) { - | Mixed({discrete}) => Some(discrete) - | Discrete(d) => Some(d) - | Continuous(_) => None - }; +// let continuousComponent = (t: t) => +// switch (t) { +// | Mixed({continuous}) => Some(continuous) +// | Continuous(c) => Some(c) +// | 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 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)) +// // }; +// // 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)) +// // }; +// // }; +// // }; +// }; - let continuousComponent = (t: t) => - switch (t) { - | Mixed({continuous}) => Some(continuous) - | Continuous(c) => Some(c) - | Discrete(_) => None - }; +// module PdfCdfShape = { +// type t = pdfCdfCombo; +// let pdf = (t: t) => +// switch (t.pdf) { +// | Mixed(pdf) => Mixed(pdf) +// | Discrete(pdf) => Discrete(pdf) +// | Continuous(pdf) => Continuous(pdf) +// }; +// let cdf = (t: t) => t.cdf; +// }; - // let scaledContinuousComponent = (t: t): option(continuousShape) => { - // switch (t) { - // | Mixed({continuous, discreteProbabilityMassFraction}) => - // Continuous.scalePdf( - // ~scaleTo=1.0 -. discreteProbabilityMassFraction, - // continuous, - // ) - // | Discrete(_) => None - // | Continuous(c) => Some(c) - // }; - // }; +// type distributionUnit = +// | UnspecifiedDistribution +// | TimeDistribution(TimeTypes.timeVector); - // let scaledDiscreteComponent = (t: t): option(discreteShape) => { - // switch (t) { - // | Mixed({discrete, discreteProbabilityMassFraction}) => - // Some(Discrete.scaleYToTotal(discreteProbabilityMassFraction, discrete)) - // | Discrete(d) => Some(d) - // | Continuous(_) => None - // }; - // }; +// type withLimitedDomain = { +// domain, +// dist: pdfCdfCombo, +// }; - // 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 WithLimitedDomain = { +// type t = withLimitedDomain; +// let dist = (t: t) => t.dist; +// let pdf = (t: t) => PdfCdfShape.pdf(t.dist); +// 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); +// }; - // 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)) - // }; - // }; - }; -}; - -module PdfCdfShape = { - type t = pdfCdfCombo; - let pdf = (t: t) => - switch (t.pdf) { - | Mixed(pdf) => Mixed(pdf) - | Discrete(pdf) => Discrete(pdf) - | Continuous(pdf) => Continuous(pdf) - }; - let cdf = (t: t) => t.cdf; -}; - -type distributionUnit = - | UnspecifiedDistribution - | TimeDistribution(TimeTypes.timeVector); - -type withLimitedDomain = { - domain, - dist: pdfCdfCombo, -}; - -module WithLimitedDomain = { - type t = withLimitedDomain; - let dist = (t: t) => t.dist; - let pdf = (t: t) => PdfCdfShape.pdf(t.dist); - 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); -}; - -type withTimeVector = { - timeVector: TimeTypes.timeVector, - dist: withLimitedDomain, -}; \ No newline at end of file +// type withTimeVector = { +// timeVector: TimeTypes.timeVector, +// dist: withLimitedDomain, \ No newline at end of file