diff --git a/showcase/entries/Continuous.re b/showcase/entries/Continuous.re index 8448d044..7357d5b6 100644 --- a/showcase/entries/Continuous.re +++ b/showcase/entries/Continuous.re @@ -12,7 +12,7 @@ let distributions = () =>

{"Basic Mixed Distribution" |> ReasonReact.string}

- {timeDist |> E.O.React.fmapOrNull(distPlus => )} + {timeDist |> E.O.React.fmapOrNull(distPlus => )}

{"Simple Continuous" |> ReasonReact.string}

; diff --git a/src/components/charts/DistPlusChart.re b/src/components/charts/DistPlusPlot.re similarity index 95% rename from src/components/charts/DistPlusChart.re rename to src/components/charts/DistPlusPlot.re index 4f914699..587901af 100644 --- a/src/components/charts/DistPlusChart.re +++ b/src/components/charts/DistPlusPlot.re @@ -10,7 +10,7 @@ module DistPlusChart = { let minX = T.minX(distPlus); let maxX = T.maxX(distPlus); let timeScale = distPlus.unit |> DistTypes.DistributionUnit.toJson; - T.toContinuous - |> E.O.fmap(Distributions.Continuous.toLinear) |> E.O.fmap(Distributions.Continuous.getShape); let minX = T.minX(integral); let maxX = T.maxX(integral); let timeScale = distPlus.unit |> DistTypes.DistributionUnit.toJson; - ReasonReact.wrapJsForReason( - ~reactClass=cdfChart, + ~reactClass=plot, ~props= makeProps( ~height?, @@ -102,7 +102,7 @@ let make = ~timeScale=?, ) => {
- { +let toDistPlus = (~sampleCount, t: distPlusIngredients): option(distPlus) => { let shape = Guesstimator.stringToMixedShape( ~string=t.guesstimatorString, diff --git a/src/distributions/Distributions.re b/src/distributions/Distributions.re index dcd4f395..5373254e 100644 --- a/src/distributions/Distributions.re +++ b/src/distributions/Distributions.re @@ -74,7 +74,7 @@ module Continuous = { }; let oShapeMap = (fn, {xyShape, interpolation}: t): option(DistTypes.continuousShape) => - fn(xyShape) |> E.O.fmap(xyShape => make(xyShape, interpolation)); + fn(xyShape) |> E.O.fmap(make(_, interpolation)); let toLinear = (t: t): t => switch (t) { @@ -90,7 +90,8 @@ module Continuous = { type t = DistTypes.continuousShape; type integral = DistTypes.continuousShape; let shapeFn = (fn, t: t) => t |> xyShape |> fn; - // TODO: Obviously fix this, it's terrible + // TODO: Obviously fix this, it's terrible. Use interpolation method here. + // TODO: Steps could be 1 value, interpolation needs at least 2. let integral = (~cache, t) => cache |> E.O.default( @@ -108,7 +109,7 @@ module Continuous = { let pointwiseFmap = (fn, t: t) => t |> xyShape |> XYShape.pointwiseMap(fn) |> fromShape; let toShape = (t: t): DistTypes.shape => Continuous(t); - // TODO: When Roman's PR comes in, fix this bit. + // TODO: When Roman's PR comes in, fix this bit. This depends on interpolation, obviously. let xToY = (f, t) => shapeFn(CdfLibrary.Distribution.findY(f), t) |> DistTypes.MixedPoint.makeContinuous; @@ -126,6 +127,8 @@ module Discrete = { Dist({ type t = DistTypes.discreteShape; type integral = DistTypes.continuousShape; + // todo: test this. Remove "stepstoContinuos-move elsewhere" + // todo: Make sure this works fine with one value. This is important for step functionality. let integral = (~cache, t) => cache |> E.O.default( @@ -138,6 +141,7 @@ module Discrete = { ); }, ); + // todo: Fix this with last element let integralSum = (~cache, t) => t |> XYShape.ySum; let minX = XYShape.minX; let maxX = XYShape.maxX; @@ -145,17 +149,20 @@ module Discrete = { let toShape = (t: t): DistTypes.shape => Discrete(t); let toContinuous = _ => None; let toDiscrete = t => Some(t); - let toScaledContinuous = t => None; + let toScaledContinuous = _ => None; let toScaledDiscrete = t => Some(t); + // todo: Fix this with code that work find recent value and use that instead. let xToY = (f, t) => CdfLibrary.Distribution.findY(f, t) |> DistTypes.MixedPoint.makeDiscrete; + // todo: This should use cache and/or same code as above. FindingY is more complex, should use interpolationType. let integralXtoY = (~cache, f, t) => t |> XYShape.accumulateYs |> CdfLibrary.Distribution.findY(f); }); }; module Mixed = { + type t = DistTypes.mixedShape; let make = (~continuous, ~discrete, ~discreteProbabilityMassFraction) : DistTypes.mixedShape => { @@ -171,7 +178,8 @@ module Mixed = { discrete: {xs: [||], ys: [||]}, } => None - | {discrete: {xs: [|_|], ys: [|_|]}} => None + | {continuous, discrete: {xs: [|_|], ys: [|_|]}} => + Some(Continuous(continuous)) | {continuous, discrete: {xs: [||], ys: [||]}} => Some(Continuous(continuous)) | {continuous: {xyShape: {xs: [||], ys: [||]}}, discrete} => @@ -180,6 +188,7 @@ module Mixed = { }; }; + // todo: Put into scaling module let scaleDiscreteFn = ({discreteProbabilityMassFraction}: DistTypes.mixedShape, f) => f *. discreteProbabilityMassFraction; @@ -188,6 +197,13 @@ module Mixed = { ({discreteProbabilityMassFraction}: DistTypes.mixedShape, f) => f *. (1.0 -. discreteProbabilityMassFraction); + let scaleContinuous = ({discreteProbabilityMassFraction}: t, continuous) => + continuous + |> Continuous.T.scaleBy(~scale=1.0 -. discreteProbabilityMassFraction); + + let scaleDiscrete = ({discreteProbabilityMassFraction}: t, disrete) => + disrete |> Discrete.T.scaleBy(~scale=discreteProbabilityMassFraction); + module T = Dist({ type t = DistTypes.mixedShape; @@ -211,14 +227,6 @@ module Mixed = { DistTypes.MixedPoint.add(c, d); }; - let scaleContinuous = - ({discreteProbabilityMassFraction}: t, continuous) => - continuous - |> Continuous.T.scaleBy(~scale=1.0 -. discreteProbabilityMassFraction); - - let scaleDiscrete = ({discreteProbabilityMassFraction}: t, disrete) => - disrete |> Discrete.T.scaleBy(~scale=discreteProbabilityMassFraction); - let toScaledContinuous = ({continuous} as t: t) => Some(scaleContinuous(t, continuous)); @@ -241,10 +249,17 @@ module Mixed = { let dist = discrete |> Discrete.T.Integral.get(~cache=None) + |> Continuous.toLinear |> Continuous.T.scaleBy( ~scale=discreteProbabilityMassFraction, ); - dist; + Continuous.make( + XYShape.combine( + Continuous.getShape(cont), + Continuous.getShape(dist), + ), + `Linear, + ); }, ); }; @@ -268,6 +283,7 @@ module Mixed = { scaleDiscreteFn(t, discrete) +. scaleContinuousFn(t, cont); }; + // TODO: This functionality is kinda weird, because it seems to assume the cdf adds to 1.0 elsewhere, which wouldn't happen here. let pointwiseFmap = (fn, {discrete, continuous, discreteProbabilityMassFraction}: t): t => { { @@ -285,6 +301,8 @@ module Shape = { type t = DistTypes.shape; type integral = DistTypes.continuousShape; + // todo: change order of arguments so t goes last. + // todo: Think of other name here? let mapToAll = (t: t, (fn1, fn2, fn3)) => switch (t) { | Mixed(m) => fn1(m) @@ -388,6 +406,9 @@ module Shape = { module DistPlus = { open DistTypes; + + type t = DistTypes.distPlus; + let make = ( ~shape, @@ -396,10 +417,11 @@ module DistPlus = { ~unit=UnspecifiedDistribution, (), ) - : distPlus => { + : t => { let integral = Shape.T.Integral.get(~cache=None, shape); {shape, domain, integralCache: integral, unit, guesstimatorString}; }; + let update = ( ~shape=?, @@ -407,7 +429,7 @@ module DistPlus = { ~domain=?, ~unit=?, ~guesstimatorString=?, - t: distPlus, + t: t, ) => { shape: E.O.default(t.shape, shape), integralCache: E.O.default(t.integralCache, integralCache), @@ -416,29 +438,32 @@ module DistPlus = { guesstimatorString: E.O.default(t.guesstimatorString, guesstimatorString), }; + let domainIncludedProbabilityMass = (t: t) => + Domain.includedProbabilityMass(t.domain); + + let domainIncludedProbabilityMassAdjustment = (t: t, f) => + f *. Domain.includedProbabilityMass(t.domain); + + let toShape = ({shape, _}: t) => shape; + + let shapeFn = (fn, {shape}: t) => fn(shape); + module T = Dist({ type t = DistTypes.distPlus; type integral = DistTypes.distPlus; - let toShape = ({shape, _}: t) => shape; - let shapeFn = (fn, t: t) => t |> toShape |> fn; + let toShape = toShape; let toContinuous = shapeFn(Shape.T.toContinuous); let toDiscrete = shapeFn(Shape.T.toDiscrete); // todo: Adjust for total mass. - let domainIncludedProbabilityMass = (t: t) => - Domain.includedProbabilityMass(t.domain); - - let domainIncludedProbabilityMassAdjustment = (t: t, f) => - f *. Domain.includedProbabilityMass(t.domain); - let toScaledContinuous = (t: t) => { t |> toShape |> Shape.T.toScaledContinuous |> E.O.fmap( - Continuous.T.pointwiseFmap(r => - r *. domainIncludedProbabilityMass(t) + Continuous.T.pointwiseFmap( + domainIncludedProbabilityMassAdjustment(t), ), ); }; @@ -475,9 +500,41 @@ module DistPlus = { let integralSum = (~cache as _, t: t) => Shape.T.Integral.sum(~cache=Some(t.integralCache), toShape(t)); - // TODO: Fix this below, obviously. Adjust for limit. + // TODO: Fix this below, obviously. Adjust for limits let integralXtoY = (~cache as _, f, t: t) => { Shape.T.Integral.xToY(~cache=Some(t.integralCache), f, toShape(t)); }; }); +}; + +module DistPlusTime = { + open DistTypes; + open DistPlus; + + type t = DistTypes.distPlus; + + let unitToJson = ({unit}: t) => unit |> DistTypes.DistributionUnit.toJson; + + let timeVector = ({unit}: t) => + switch (unit) { + | TimeDistribution(timeVector) => Some(timeVector) + | UnspecifiedDistribution => None + }; + + let timeInVectorToX = (f: TimeTypes.timeInVector, t: t) => { + let timeVector = t |> timeVector; + timeVector |> E.O.fmap(TimeTypes.RelativeTimePoint.toXValue(_, f)); + }; + + let xToY = (f: TimeTypes.timeInVector, t: t) => { + timeInVectorToX(f, t) |> E.O.fmap(DistPlus.T.xToY(_, t)); + }; + + module Integral = { + include DistPlus.T.Integral; + let xToY = (~cache as _, f: TimeTypes.timeInVector, t: t) => { + timeInVectorToX(f, t) + |> E.O.fmap(x => DistPlus.T.Integral.xToY(~cache=None, x, t)); + }; + }; }; \ No newline at end of file diff --git a/src/distributions/TimeTypes.re b/src/distributions/TimeTypes.re index 75e158e2..355e8ba5 100644 --- a/src/distributions/TimeTypes.re +++ b/src/distributions/TimeTypes.re @@ -50,11 +50,11 @@ module TimePoint = { MomentRe.diff(timeVector.zero, moment, timeVector.unit); }; -module RelativeTimePoint = { - type timeInVector = - | Time(MomentRe.Moment.t) - | XValue(float); +type timeInVector = + | Time(MomentRe.Moment.t) + | XValue(float); +module RelativeTimePoint = { let toTime = (timeVector: timeVector, timeInVector: timeInVector) => switch (timeInVector) { | Time(r) => r diff --git a/src/distributions/XYShape.re b/src/distributions/XYShape.re index 531282d4..a0e55724 100644 --- a/src/distributions/XYShape.re +++ b/src/distributions/XYShape.re @@ -23,6 +23,22 @@ let fromArray = ((xs, ys)): t => {xs, ys}; let fromArrays = (xs, ys): t => {xs, ys}; let pointwiseMap = (fn, t: t): t => {xs: t.xs, ys: t.ys |> E.A.fmap(fn)}; +let compare = (a: float, b: float) => a > b ? 1 : (-1); + +let comparePoints = ((x1: float, y1: float), (x2: float, y2: float)) => + switch (x1 == x2, y1 == y2) { + | (false, _) => compare(x1, x2) + | (true, false) => compare(y1, y2) + | (true, true) => (-1) + }; + +let combine = (t1: t, t2: t) => { + let totalLength = E.A.length(t1.xs) + E.A.length(t2.xs); + let array = Belt.Array.concat(zip(t1), zip(t2)); + Array.sort(comparePoints, array); + array |> Belt.Array.unzip |> fromArray; +}; + let intersperce = (t1: t, t2: t) => { let items: ref(array((float, float))) = ref([||]); let t1 = zip(t1); diff --git a/src/interface/FormBuilder.re b/src/interface/FormBuilder.re index 8849019a..55f1bf23 100644 --- a/src/interface/FormBuilder.re +++ b/src/interface/FormBuilder.re @@ -22,7 +22,7 @@ let propValue = (t: Prop.Value.t) => { DistPlusIngredients.toDistPlus(~sampleCount=1000, r); switch (newDistribution) { | Some(distribution) => -
+
| None => "Something went wrong" |> ReasonReact.string }; | FloatCdf(_) =>
diff --git a/src/models/EAFunds.re b/src/models/EAFunds.re index e1d6bbf4..d21e6d01 100644 --- a/src/models/EAFunds.re +++ b/src/models/EAFunds.re @@ -109,14 +109,20 @@ module Model = { // TODO: Fixe number that integral is calculated for let getGlobalCatastropheChance = dateTime => { - let model = GlobalCatastrophe.Model.make(dateTime); - switch (model) { - | Prop.Value.DistPlusIngredients(distPlusIngredients) => - distPlusIngredients - |> DistPlusIngredients.toDistPlus(~sampleCount=1000) - |> E.O.fmap(Distributions.DistPlus.T.Integral.xToY(~cache=None, 18.0)) - | _ => None - }; + GlobalCatastrophe.makeI(MomentRe.momentNow()) + |> DistPlusIngredients.toDistPlus(~sampleCount=1000) + |> E.O.bind( + _, + t => { + let foo = + Distributions.DistPlusTime.Integral.xToY( + ~cache=None, + Time(dateTime), + t, + ); + Some(0.5); + }, + ); }; let make = diff --git a/src/models/GlobalCatastrophe.re b/src/models/GlobalCatastrophe.re index d7002f73..82cbfc56 100644 --- a/src/models/GlobalCatastrophe.re +++ b/src/models/GlobalCatastrophe.re @@ -1,5 +1,12 @@ -let guesstimatorString = "20 to 80"; +let guesstimatorString = "floor(10 to 20)"; +let makeI = (currentDateTime: MomentRe.Moment.t) => { + DistPlusIngredients.make( + ~guesstimatorString, + ~unit=TimeDistribution({zero: currentDateTime, unit: `years}), + (), + ); +}; module Model = { let make = (currentDateTime: MomentRe.Moment.t) => { let distPlusIngredients = diff --git a/src/lib/E.re b/src/utility/E.re similarity index 100% rename from src/lib/E.re rename to src/utility/E.re diff --git a/src/lib/GuesstimatorDist.re b/src/utility/GuesstimatorDist.re similarity index 100% rename from src/lib/GuesstimatorDist.re rename to src/utility/GuesstimatorDist.re