From 61645243f92dbafc6ab3af699f84b7dba243af36 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Sun, 16 Feb 2020 11:45:19 +0000 Subject: [PATCH] Simple refactoring too add functionality in Shape.re --- src/Experimental/LimitedDomainCdf.re | 33 ++++++ src/Experimental/TimeLimitedDomainCdf.re | 38 ++++++ src/LimitedDomainCdf.re | 43 ------- src/TimeLimitedDomainCdf.re | 46 ------- src/lib/DistributionTypes.re | 8 +- src/lib/MixedShapeBuilder.re | 63 ++++++++++ src/lib/Prop.re | 13 -- src/lib/Shape.re | 145 ++++++++--------------- src/lib/Types.re | 66 ----------- src/models/EAFunds.re | 10 +- src/models/GlobalCatastrophe.re | 5 +- src/utility/Guesstimator.re | 4 +- 12 files changed, 193 insertions(+), 281 deletions(-) create mode 100644 src/Experimental/LimitedDomainCdf.re create mode 100644 src/Experimental/TimeLimitedDomainCdf.re delete mode 100644 src/LimitedDomainCdf.re delete mode 100644 src/TimeLimitedDomainCdf.re create mode 100644 src/lib/MixedShapeBuilder.re diff --git a/src/Experimental/LimitedDomainCdf.re b/src/Experimental/LimitedDomainCdf.re new file mode 100644 index 00000000..126cf5d1 --- /dev/null +++ b/src/Experimental/LimitedDomainCdf.re @@ -0,0 +1,33 @@ +// type t = { + // distribution: Types.ContinuousDistribution.t, + // domainMaxX: float, + // }; + // let make = (~distribution, ~domainMaxX): t => {distribution, domainMaxX}; + // let fromCdf = + // ( + // cdf: Types.ContinuousDistribution.t, + // domainMaxX: float, + // probabilityAtMaxX: float, + // ) => { + // let distribution: Types.ContinuousDistribution.t = { + // xs: cdf.xs, + // ys: cdf.ys |> E.A.fmap(r => r *. probabilityAtMaxX), + // }; + // {distribution, domainMaxX}; + // }; + // let _lastElement = (a: array('a)) => + // switch (Belt.Array.size(a)) { + // | 0 => None + // | n => Belt.Array.get(a, n) + // }; + // let probabilityBeforeDomainMax = (t: t) => _lastElement(t.distribution.ys); + // let domainMaxX = (t: t) => t.domainMaxX /* CdfLibrary.Distribution.findX(yPoint, t.distribution)*/; + // let probabilityDistribution = (t: t) => + // t.distribution |> CdfLibrary.Distribution.toPdf; + // let probability = (t: t, xPoint: float) => + // CdfLibrary.Distribution.findY(xPoint, probabilityDistribution(t)); + // let probabilityInverse = (t: t, yPoint: float) => + // CdfLibrary.Distribution.findX(yPoint, probabilityDistribution(t)); + // let cumulativeProbability = (t: t, xPoint: float) => + // CdfLibrary.Distribution.findY(xPoint, t.distribution); + /* let cumulativeProbabilityInverse = (t: t, yPoint: float) =*/ \ No newline at end of file diff --git a/src/Experimental/TimeLimitedDomainCdf.re b/src/Experimental/TimeLimitedDomainCdf.re new file mode 100644 index 00000000..9c4c3b67 --- /dev/null +++ b/src/Experimental/TimeLimitedDomainCdf.re @@ -0,0 +1,38 @@ +// open TimeTypes; + // type t = { + // timeVector, + // limitedDomainCdf: LimitedDomainCdf.t, + // }; + // let make = + // ( + // ~timeVector: timeVector, + // ~distribution: Types.ContinuousDistribution.t, + // ~probabilityAtMaxX: float, + // ~maxX: [ | `time(MomentRe.Moment.t) | `x(float)], + // ) + // : t => { + // let domainMaxX = + // switch (maxX) { + // | `time(m) => TimePoint.fromMoment(timeVector, m) + // | `x(r) => r + // }; + // let limitedDomainCdf = + // LimitedDomainCdf.fromCdf(distribution, domainMaxX, probabilityAtMaxX); + // {timeVector, limitedDomainCdf}; + // }; + // let probabilityBeforeDomainMax = (t: t) => + // LimitedDomainCdf.probabilityBeforeDomainMax(t.limitedDomainCdf); + // let domainMaxX = (t: t) => + // LimitedDomainCdf.probabilityBeforeDomainMax(t.limitedDomainCdf) /* |> (r => RelativeTimePoint.toTime(t.timeVector, XValue(r)))*/; + // let probability = (t: t, m: MomentRe.Moment.t) => { + // RelativeTimePoint.toXValue(t.timeVector, Time(m)) + // |> LimitedDomainCdf.probability(t.limitedDomainCdf); + // }; + // let probabilityInverse = (t: t, y: float) => + // LimitedDomainCdf.probabilityInverse(t.limitedDomainCdf, y) + // |> (r => RelativeTimePoint.toTime(t.timeVector, XValue(r))); + // let cumulativeProbability = (t: t, m: MomentRe.Moment.t) => + // RelativeTimePoint.toXValue(t.timeVector, Time(m)) + // |> LimitedDomainCdf.cumulativeProbability(t.limitedDomainCdf); + // let cumulativeProbabilityInverse = (t: t, y: float) => + /* LimitedDomainCdf.cumulativeProbabilityInverse(t.limitedDomainCdf, y*/ \ No newline at end of file diff --git a/src/LimitedDomainCdf.re b/src/LimitedDomainCdf.re deleted file mode 100644 index 3eeeda59..00000000 --- a/src/LimitedDomainCdf.re +++ /dev/null @@ -1,43 +0,0 @@ -type t = { - distribution: Types.ContinuousDistribution.t, - domainMaxX: float, -}; - -let make = (~distribution, ~domainMaxX): t => {distribution, domainMaxX}; - -let fromCdf = - ( - cdf: Types.ContinuousDistribution.t, - domainMaxX: float, - probabilityAtMaxX: float, - ) => { - let distribution: Types.ContinuousDistribution.t = { - xs: cdf.xs, - ys: cdf.ys |> E.A.fmap(r => r *. probabilityAtMaxX), - }; - {distribution, domainMaxX}; -}; - -let _lastElement = (a: array('a)) => - switch (Belt.Array.size(a)) { - | 0 => None - | n => Belt.Array.get(a, n) - }; - -let probabilityBeforeDomainMax = (t: t) => _lastElement(t.distribution.ys); - -let domainMaxX = (t: t) => t.domainMaxX /* CdfLibrary.Distribution.findX(yPoint, t.distribution)*/; - -// let probabilityDistribution = (t: t) => -// t.distribution |> CdfLibrary.Distribution.toPdf; - -// let probability = (t: t, xPoint: float) => -// CdfLibrary.Distribution.findY(xPoint, probabilityDistribution(t)); - -// let probabilityInverse = (t: t, yPoint: float) => -// CdfLibrary.Distribution.findX(yPoint, probabilityDistribution(t)); - -// let cumulativeProbability = (t: t, xPoint: float) => -// CdfLibrary.Distribution.findY(xPoint, t.distribution); - -// let cumulativeProbabilityInverse = (t: t, yPoint: float) => \ No newline at end of file diff --git a/src/TimeLimitedDomainCdf.re b/src/TimeLimitedDomainCdf.re deleted file mode 100644 index aa8e08c9..00000000 --- a/src/TimeLimitedDomainCdf.re +++ /dev/null @@ -1,46 +0,0 @@ -open TimeTypes; - -type t = { - timeVector, - limitedDomainCdf: LimitedDomainCdf.t, -}; - -let make = - ( - ~timeVector: timeVector, - ~distribution: Types.ContinuousDistribution.t, - ~probabilityAtMaxX: float, - ~maxX: [ | `time(MomentRe.Moment.t) | `x(float)], - ) - : t => { - let domainMaxX = - switch (maxX) { - | `time(m) => TimePoint.fromMoment(timeVector, m) - | `x(r) => r - }; - let limitedDomainCdf = - LimitedDomainCdf.fromCdf(distribution, domainMaxX, probabilityAtMaxX); - {timeVector, limitedDomainCdf}; -}; - -let probabilityBeforeDomainMax = (t: t) => - LimitedDomainCdf.probabilityBeforeDomainMax(t.limitedDomainCdf); - -let domainMaxX = (t: t) => - LimitedDomainCdf.probabilityBeforeDomainMax(t.limitedDomainCdf) /* |> (r => RelativeTimePoint.toTime(t.timeVector, XValue(r)))*/; - -// let probability = (t: t, m: MomentRe.Moment.t) => { -// RelativeTimePoint.toXValue(t.timeVector, Time(m)) -// |> LimitedDomainCdf.probability(t.limitedDomainCdf); -// }; - -// let probabilityInverse = (t: t, y: float) => -// LimitedDomainCdf.probabilityInverse(t.limitedDomainCdf, y) -// |> (r => RelativeTimePoint.toTime(t.timeVector, XValue(r))); - -// let cumulativeProbability = (t: t, m: MomentRe.Moment.t) => -// RelativeTimePoint.toXValue(t.timeVector, Time(m)) -// |> LimitedDomainCdf.cumulativeProbability(t.limitedDomainCdf); - -// let cumulativeProbabilityInverse = (t: t, y: float) => -// LimitedDomainCdf.cumulativeProbabilityInverse(t.limitedDomainCdf, y) \ No newline at end of file diff --git a/src/lib/DistributionTypes.re b/src/lib/DistributionTypes.re index 498c33de..c8b4db27 100644 --- a/src/lib/DistributionTypes.re +++ b/src/lib/DistributionTypes.re @@ -9,15 +9,13 @@ type domain = | RightLimited(domainLimit) | LeftAndRightLimited(domainLimit, domainLimit); -type continuousShape = { +type xyShape = { xs: array(float), ys: array(float), }; +type continuousShape = xyShape; -type discreteShape = { - xs: array(float), - ys: array(float), -}; +type discreteShape = xyShape; type mixedShape = { continuous: continuousShape, diff --git a/src/lib/MixedShapeBuilder.re b/src/lib/MixedShapeBuilder.re new file mode 100644 index 00000000..9ed0ca0a --- /dev/null +++ b/src/lib/MixedShapeBuilder.re @@ -0,0 +1,63 @@ +type assumption = + | ADDS_TO_1 + | ADDS_TO_CORRECT_PROBABILITY; +type assumptions = { + continuous: assumption, + discrete: assumption, + discreteProbabilityMass: option(float), +}; +let build = (~continuous, ~discrete, ~assumptions) => + switch (assumptions) { + | { + continuous: ADDS_TO_CORRECT_PROBABILITY, + discrete: ADDS_TO_CORRECT_PROBABILITY, + discreteProbabilityMass: Some(r), + } => + // TODO: Fix this, it's wrong :( + Some( + Shape.Mixed.make( + ~continuous, + ~discrete, + ~discreteProbabilityMassFraction=r, + ), + ) + | { + continuous: ADDS_TO_1, + discrete: ADDS_TO_1, + discreteProbabilityMass: Some(r), + } => + Some( + Shape.Mixed.make( + ~continuous, + ~discrete, + ~discreteProbabilityMassFraction=r, + ), + ) + | { + continuous: ADDS_TO_1, + discrete: ADDS_TO_1, + discreteProbabilityMass: None, + } => + None + | { + continuous: ADDS_TO_CORRECT_PROBABILITY, + discrete: ADDS_TO_1, + discreteProbabilityMass: None, + } => + None + | { + continuous: ADDS_TO_1, + discrete: ADDS_TO_CORRECT_PROBABILITY, + discreteProbabilityMass: None, + } => + let discreteProbabilityMassFraction = Shape.Discrete.ySum(discrete); + let discrete = Shape.Discrete.scaleYToTotal(1.0, discrete); + Some( + Shape.Mixed.make( + ~continuous, + ~discrete, + ~discreteProbabilityMassFraction, + ), + ); + | _ => None + }; \ No newline at end of file diff --git a/src/lib/Prop.re b/src/lib/Prop.re index eac149ff..92827695 100644 --- a/src/lib/Prop.re +++ b/src/lib/Prop.re @@ -16,10 +16,6 @@ module Value = { | Probability(float) | Conditional(conditional) | GenericDistribution(DistributionTypes.genericDistribution) - | TimeLimitedDomainCdf(TimeLimitedDomainCdf.t) - | TimeLimitedDomainCdfLazy( - (string => Types.ContinuousDistribution.t) => TimeLimitedDomainCdf.t, - ) | ConditionalArray(array(conditional)) | FloatCdf(string); @@ -33,8 +29,6 @@ module Value = { | SelectSingle(r) => r | FloatCdf(r) => r | GenericDistribution(_) => "" - | TimeLimitedDomainCdf(_) => "" - | TimeLimitedDomainCdfLazy(_) => "" | Probability(r) => (r *. 100. |> Js.Float.toFixed) ++ "%" | DateTime(r) => r |> MomentRe.Moment.defaultFormat | FloatPoint(r) => r |> Js.Float.toFixed @@ -78,13 +72,6 @@ module Value = { | None => "Something went wrong" |> ReasonReact.string | _ =>
}; - | TimeLimitedDomainCdfLazy(_) =>
- | TimeLimitedDomainCdf(r) => - let cdf: Types.ContinuousDistribution.t = - r.limitedDomainCdf.distribution; - <> - Types.ContinuousDistribution.toJs} /> - ; | FloatCdf(_) =>
| Probability(r) => (r *. 100. |> Js.Float.toFixed) ++ "%" |> ReasonReact.string diff --git a/src/lib/Shape.re b/src/lib/Shape.re index c98500f8..a0a90b5c 100644 --- a/src/lib/Shape.re +++ b/src/lib/Shape.re @@ -1,10 +1,46 @@ open DistributionTypes; -module Continuous = { - let fromArrays = (xs, ys): continuousShape => {xs, ys}; - let toJs = (t: continuousShape) => { +let _lastElement = (a: array('a)) => + switch (Belt.Array.size(a)) { + | 0 => None + | n => Belt.Array.get(a, n) + }; + +module XYShape = { + type t = xyShape; + + let toJs = (t: t) => { {"xs": t.xs, "ys": t.ys}; }; + + let fmap = (t: t, y): t => {xs: t.xs, ys: t.ys |> E.A.fmap(y)}; + let yFold = (fn, t: t) => { + E.A.fold_left(fn, 0., t.ys); + }; + let ySum = yFold((a, b) => a +. b); + + let fromArrays = (xs, ys): t => {xs, ys}; + + let transverse = (fn, p: t) => { + let (xs, ys) = + Belt.Array.zip(p.xs, p.ys) + ->Belt.Array.reduce([||], (items, (x, y)) => + switch (_lastElement(items)) { + | Some((_, yLast)) => [|(x, fn(y, yLast))|] + | None => [|(x, y)|] + } + ) + |> Belt.Array.unzip; + fromArrays(xs, ys); + }; + + let derivative = transverse((aCurrent, aLast) => aCurrent -. aLast); + let integral = transverse((aCurrent, aLast) => aCurrent +. aLast); +}; + +module Continuous = { + let fromArrays = XYShape.fromArrays; + let toJs = XYShape.toJs; let toPdf = CdfLibrary.Distribution.toPdf; let toCdf = CdfLibrary.Distribution.toCdf; let findX = CdfLibrary.Distribution.findX; @@ -13,47 +49,14 @@ module Continuous = { module Discrete = { type t = discreteShape; - let fromArrays = (xs, ys): discreteShape => {xs, ys}; - let _lastElement = (a: array('a)) => - switch (Belt.Array.size(a)) { - | 0 => None - | n => Belt.Array.get(a, n) - }; - - let derivative = (p: t) => { - let (xs, ys) = - Belt.Array.zip(p.xs, p.ys) - ->Belt.Array.reduce([||], (items, (x, y)) => - switch (_lastElement(items)) { - | Some((_, yLast)) => [|(x, y -. yLast)|] - | None => [|(x, y)|] - } - ) - |> Belt.Array.unzip; - fromArrays(xs, ys); - }; - - let integral = (p: t) => { - let (xs, ys) = - Belt.Array.zip(p.xs, p.ys) - ->Belt.Array.reduce([||], (items, (x, y)) => - switch (_lastElement(items)) { - | Some((_, yLast)) => E.A.append(items, [|(x, y +. yLast)|]) - | None => [|(x, y)|] - } - ) - |> Belt.Array.unzip; - fromArrays(xs, ys); - }; - - let ySum = (t: t) => { - E.A.fold_left((a, b) => a +. b, 0., t.ys); - }; + let fromArrays = XYShape.fromArrays; + let toJs = XYShape.toJs; + let ySum = XYShape.ySum; + let zip = t => Belt.Array.zip(t.xs, t.ys); let scaleYToTotal = (totalDesired, t: t): t => { - let currentSum = ySum(t); - let difference = totalDesired /. currentSum; - {xs: t.xs, ys: t.ys |> E.A.fmap(y => y *. difference)}; + let difference = totalDesired /. ySum(t); + XYShape.fmap(t, y => y *. difference); }; let render = (t: t) => @@ -67,6 +70,12 @@ module Discrete = {
) |> ReasonReact.array; + + let findY = (x: float, t: t) => + switch (E.A.getBy(zip(t), ((ix, _)) => ix == x)) { + | Some((_, y)) => y + | None => 0. + }; }; module Mixed = { @@ -75,56 +84,4 @@ module Mixed = { discrete, discreteProbabilityMassFraction, }; - - module Builder = { - type assumption = - | ADDS_TO_1 - | ADDS_TO_CORRECT_PROBABILITY; - type assumptions = { - continuous: assumption, - discrete: assumption, - discreteProbabilityMass: option(float), - }; - let build = (~continuous, ~discrete, ~assumptions) => - switch (assumptions) { - | { - continuous: ADDS_TO_CORRECT_PROBABILITY, - discrete: ADDS_TO_CORRECT_PROBABILITY, - discreteProbabilityMass: Some(r), - } => - // TODO: Fix this, it's wrong :( - Some( - make(~continuous, ~discrete, ~discreteProbabilityMassFraction=r), - ) - | { - continuous: ADDS_TO_1, - discrete: ADDS_TO_1, - discreteProbabilityMass: Some(r), - } => - Some( - make(~continuous, ~discrete, ~discreteProbabilityMassFraction=r), - ) - | { - continuous: ADDS_TO_1, - discrete: ADDS_TO_1, - discreteProbabilityMass: None, - } => - None - | { - continuous: ADDS_TO_CORRECT_PROBABILITY, - discrete: ADDS_TO_1, - discreteProbabilityMass: None, - } => - None - | { - continuous: ADDS_TO_1, - discrete: ADDS_TO_CORRECT_PROBABILITY, - discreteProbabilityMass: None, - } => - let discreteProbabilityMassFraction = Discrete.ySum(discrete); - let discrete = Discrete.scaleYToTotal(1.0, discrete); - Some(make(~continuous, ~discrete, ~discreteProbabilityMassFraction)); - | _ => None - }; - }; }; \ No newline at end of file diff --git a/src/lib/Types.re b/src/lib/Types.re index dffde0f1..e69de29b 100644 --- a/src/lib/Types.re +++ b/src/lib/Types.re @@ -1,66 +0,0 @@ -module ContinuousDistribution = { - type t = { - xs: array(float), - ys: array(float), - }; - - let toJs = (t: t) => { - {"xs": t.xs, "ys": t.ys}; - }; - - let toComponentsDist = (d: t): ForetoldComponents.Types.Dist.t => { - xs: d.xs, - ys: d.ys, - }; - - type pdf = t; - type cdf = t; -}; - -module DiscreteDistribution = { - type t = { - xs: array(float), - ys: array(float), - }; - - let fromArray = (xs, ys) => {xs, ys}; - - let _lastElement = (a: array('a)) => - switch (Belt.Array.size(a)) { - | 0 => None - | n => Belt.Array.get(a, n) - }; - - let derivative = (p: t) => { - let (xs, ys) = - Belt.Array.zip(p.xs, p.ys) - ->Belt.Array.reduce([||], (items, (x, y)) => - switch (_lastElement(items)) { - | Some((_, yLast)) => [|(x, y -. yLast)|] - | None => [|(x, y)|] - } - ) - |> Belt.Array.unzip; - fromArray(xs, ys); - }; - - let integral = (p: t) => { - let (xs, ys) = - Belt.Array.zip(p.xs, p.ys) - ->Belt.Array.reduce([||], (items, (x, y)) => - switch (_lastElement(items)) { - | Some((_, yLast)) => [|(x, y +. yLast)|] - | None => [|(x, y)|] - } - ) - |> Belt.Array.unzip; - fromArray(xs, ys); - }; -}; - -module MixedDistribution = { - type t = { - discrete: DiscreteDistribution.t, - continuous: ContinuousDistribution.t, - }; -}; \ No newline at end of file diff --git a/src/models/EAFunds.re b/src/models/EAFunds.re index 4e9dbc87..2caf295c 100644 --- a/src/models/EAFunds.re +++ b/src/models/EAFunds.re @@ -129,15 +129,7 @@ module Model = { (), ); Prop.Value.GenericDistribution(genericDistribution); - | CHANCE_OF_EXISTENCE => - let lazyDistribution = r => - TimeLimitedDomainCdf.make( - ~timeVector={zero: currentDateTime, unit: `years}, - ~distribution=r(FloatCdf.logNormal(10., 2.)), - ~probabilityAtMaxX=0.7, - ~maxX=`x(200.), - ); - Prop.Value.TimeLimitedDomainCdfLazy(lazyDistribution); + | CHANCE_OF_EXISTENCE => Prop.Value.Probability(0.3) }; }; }; diff --git a/src/models/GlobalCatastrophe.re b/src/models/GlobalCatastrophe.re index 1f62dc49..d405758a 100644 --- a/src/models/GlobalCatastrophe.re +++ b/src/models/GlobalCatastrophe.re @@ -1,10 +1,9 @@ -// ~generationSource=GuesstimatorString(FloatCdf.logNormal(20., 3.)), +// ~generationSource=GuesstimatorString(, module Model = { let make = (currentDateTime: MomentRe.Moment.t) => { let genericDistribution = GenericDistribution.make( - ~generationSource= - GuesstimatorString("mm(floor(10 to 15), 20 to 30, [.5,.5])"), + ~generationSource=GuesstimatorString(FloatCdf.logNormal(20., 3.)), ~probabilityType=Cdf, ~domain=RightLimited({xPoint: 200., excludingProbabilityMass: 0.3}), ~unit=Time({zero: currentDateTime, unit: `years}), diff --git a/src/utility/Guesstimator.re b/src/utility/Guesstimator.re index 560ca798..c6ba158e 100644 --- a/src/utility/Guesstimator.re +++ b/src/utility/Guesstimator.re @@ -25,12 +25,12 @@ module Internals = { external toCombinedFormat: (string, int) => combined = "run"; let toMixedShape = (r: combined): option(DistributionTypes.mixedShape) => { - let assumptions: Shape.Mixed.Builder.assumptions = { + let assumptions: MixedShapeBuilder.assumptions = { continuous: ADDS_TO_1, discrete: ADDS_TO_CORRECT_PROBABILITY, discreteProbabilityMass: None, }; - Shape.Mixed.Builder.build( + MixedShapeBuilder.build( ~continuous=toContinous(r), ~discrete=toDiscrete(r), ~assumptions,