Very simple functionality with multimodals

This commit is contained in:
Ozzie Gooen 2020-04-01 14:52:13 +01:00
parent 34a2f28ff8
commit bc39ce9c67
6 changed files with 109 additions and 17 deletions

View File

@ -52,8 +52,7 @@ module DemoDist = {
|> E.O.fmap(shape => { |> E.O.fmap(shape => {
let distPlus = let distPlus =
Distributions.DistPlus.make( Distributions.DistPlus.make(
~shape= ~shape,
Continuous(Distributions.Continuous.make(`Linear, shape)),
~domain=Complete, ~domain=Complete,
~unit=UnspecifiedDistribution, ~unit=UnspecifiedDistribution,
~guesstimatorString=None, ~guesstimatorString=None,

View File

@ -142,6 +142,12 @@ module Discrete = {
t |> XYShape.T.zip |> XYShape.Zipped.sortByY; t |> XYShape.T.zip |> XYShape.Zipped.sortByY;
let sortedByX = (t: DistTypes.discreteShape) => let sortedByX = (t: DistTypes.discreteShape) =>
t |> XYShape.T.zip |> XYShape.Zipped.sortByX; t |> XYShape.T.zip |> XYShape.Zipped.sortByX;
let empty = XYShape.T.empty;
let combine = (fn, t1: DistTypes.discreteShape, t2: DistTypes.discreteShape): DistTypes.discreteShape => {
XYShape.Combine.combine(~xsSelection=ALL_XS, ~xToYSelection=XYShape.XtoY.stepwiseIfAtX, ~fn, t1, t2)
}
let _default0 = ((fn, a,b) => fn(E.O.default(0.0, a), E.O.default(0.0, b)));
let reduce = (fn, items) => items |> E.A.fold_left(combine(_default0((fn))), empty);
module T = module T =
Dist({ Dist({
type t = DistTypes.discreteShape; type t = DistTypes.discreteShape;

View File

@ -17,6 +17,7 @@ module T = {
type ts = array(xyShape); type ts = array(xyShape);
let xs = (t: t) => t.xs; let xs = (t: t) => t.xs;
let ys = (t: t) => t.ys; let ys = (t: t) => t.ys;
let empty = ({xs: [||], ys: [||]});
let minX = (t: t) => t |> xs |> E.A.Sorted.min |> extImp; let minX = (t: t) => t |> xs |> E.A.Sorted.min |> extImp;
let maxX = (t: t) => t |> xs |> E.A.Sorted.max |> extImp; let maxX = (t: t) => t |> xs |> E.A.Sorted.max |> extImp;
let firstY = (t: t) => t |> ys |> E.A.first |> extImp; let firstY = (t: t) => t |> ys |> E.A.first |> extImp;
@ -170,11 +171,6 @@ module Combine = {
| ALL_XS | ALL_XS
| XS_EVENLY_DIVIDED(int); | XS_EVENLY_DIVIDED(int);
type xToYSelection =
| LINEAR
| STEPWISE_INCREMENTAL
| STEPWISE_IF_AT_X;
let combine = let combine =
( (
~xToYSelection: (float, T.t) => 'a, ~xToYSelection: (float, T.t) => 'a,

View File

@ -48,6 +48,32 @@ type triangular = {
[@bs.meth] "inv": (float, float, float, float) => float, [@bs.meth] "inv": (float, float, float, float) => float,
[@bs.meth] "sample": (float, float, float) => float, [@bs.meth] "sample": (float, float, float) => float,
}; };
// Pareto doesn't have sample for some reason
type pareto = {
.
[@bs.meth] "pdf": (float, float, float) => float,
[@bs.meth] "cdf": (float, float, float) => float,
[@bs.meth] "inv": (float, float, float) => float,
};
type poisson = {
.
[@bs.meth] "pdf": (float, float) => float,
[@bs.meth] "cdf": (float, float) => float,
[@bs.meth] "sample": float => float,
};
type weibull = {
.
[@bs.meth] "pdf": (float, float, float) => float,
[@bs.meth] "cdf": (float, float, float) => float,
[@bs.meth] "inv": (float, float, float) => float,
[@bs.meth] "sample": (float, float) => float,
};
type binomial = {
.
[@bs.meth] "pdf": (float, float, float) => float,
[@bs.meth] "cdf": (float, float, float) => float,
};
[@bs.module "jstat"] external normal: normal = "normal"; [@bs.module "jstat"] external normal: normal = "normal";
[@bs.module "jstat"] external lognormal: lognormal = "lognormal"; [@bs.module "jstat"] external lognormal: lognormal = "lognormal";
[@bs.module "jstat"] external uniform: uniform = "uniform"; [@bs.module "jstat"] external uniform: uniform = "uniform";
@ -55,6 +81,10 @@ type triangular = {
[@bs.module "jstat"] external exponential: exponential = "exponential"; [@bs.module "jstat"] external exponential: exponential = "exponential";
[@bs.module "jstat"] external cauchy: cauchy = "cauchy"; [@bs.module "jstat"] external cauchy: cauchy = "cauchy";
[@bs.module "jstat"] external triangular: triangular = "triangular"; [@bs.module "jstat"] external triangular: triangular = "triangular";
[@bs.module "jstat"] external poisson: poisson = "poisson";
[@bs.module "jstat"] external pareto: pareto = "pareto";
[@bs.module "jstat"] external weibull: weibull = "weibull";
[@bs.module "jstat"] external binomial: binomial = "binomial";
[@bs.module "jstat"] external sum: array(float) => float = "sum"; [@bs.module "jstat"] external sum: array(float) => float = "sum";
[@bs.module "jstat"] external product: array(float) => float = "product"; [@bs.module "jstat"] external product: array(float) => float = "product";

View File

@ -177,6 +177,7 @@ module MathAdtToDistDst = {
| Fn({name: "exponential", args}) => exponential(args) | Fn({name: "exponential", args}) => exponential(args)
| Fn({name: "cauchy", args}) => cauchy(args) | Fn({name: "cauchy", args}) => cauchy(args)
| Fn({name: "triangular", args}) => triangular(args) | Fn({name: "triangular", args}) => triangular(args)
| Value(f) => Ok(`Simple(`Float(f)))
| Fn({name: "mm", args}) => { | Fn({name: "mm", args}) => {
let dists = args |> E.A.fmap(functionParser); let dists = args |> E.A.fmap(functionParser);
let weights = let weights =
@ -195,6 +196,7 @@ module MathAdtToDistDst = {
| _ => None, | _ => None,
) )
|> E.A.O.concatSomes; |> E.A.O.concatSomes;
Js.log3("Making dists", dists, weights);
multiModal(dists, weights); multiModal(dists, weights);
} }
| Fn({name}) => Error(name ++ ": function not supported") | Fn({name}) => Error(name ++ ": function not supported")

View File

@ -31,6 +31,8 @@ type triangular = {
high: float, high: float,
}; };
type contType = [ | `Continuous | `Discrete];
type dist = [ type dist = [
| `Normal(normal) | `Normal(normal)
| `Beta(beta) | `Beta(beta)
@ -39,6 +41,7 @@ type dist = [
| `Exponential(exponential) | `Exponential(exponential)
| `Cauchy(cauchy) | `Cauchy(cauchy)
| `Triangular(triangular) | `Triangular(triangular)
| `Float(float)
]; ];
type pointwiseAdd = array((dist, float)); type pointwiseAdd = array((dist, float));
@ -51,6 +54,7 @@ module Exponential = {
let inv = (p, t: t) => Jstat.exponential##inv(p, t.rate); let inv = (p, t: t) => Jstat.exponential##inv(p, t.rate);
let sample = (t: t) => Jstat.exponential##sample(t.rate); let sample = (t: t) => Jstat.exponential##sample(t.rate);
let toString = ({rate}: t) => {j|Exponential($rate)|j}; let toString = ({rate}: t) => {j|Exponential($rate)|j};
let contType: contType = `Continuous;
}; };
module Cauchy = { module Cauchy = {
@ -59,6 +63,7 @@ module Cauchy = {
let inv = (p, t: t) => Jstat.cauchy##inv(p, t.local, t.scale); let inv = (p, t: t) => Jstat.cauchy##inv(p, t.local, t.scale);
let sample = (t: t) => Jstat.cauchy##sample(t.local, t.scale); let sample = (t: t) => Jstat.cauchy##sample(t.local, t.scale);
let toString = ({local, scale}: t) => {j|Cauchy($local, $scale)|j}; let toString = ({local, scale}: t) => {j|Cauchy($local, $scale)|j};
let contType: contType = `Continuous;
}; };
module Triangular = { module Triangular = {
@ -67,6 +72,7 @@ module Triangular = {
let inv = (p, t: t) => Jstat.triangular##inv(p, t.low, t.high, t.medium); let inv = (p, t: t) => Jstat.triangular##inv(p, t.low, t.high, t.medium);
let sample = (t: t) => Jstat.triangular##sample(t.low, t.high, t.medium); let sample = (t: t) => Jstat.triangular##sample(t.low, t.high, t.medium);
let toString = ({low, medium, high}: t) => {j|Triangular($low, $medium, $high)|j}; let toString = ({low, medium, high}: t) => {j|Triangular($low, $medium, $high)|j};
let contType: contType = `Continuous;
}; };
module Normal = { module Normal = {
@ -75,6 +81,7 @@ module Normal = {
let inv = (p, t: t) => Jstat.normal##inv(p, t.mean, t.stdev); let inv = (p, t: t) => Jstat.normal##inv(p, t.mean, t.stdev);
let sample = (t: t) => Jstat.normal##sample(t.mean, t.stdev); let sample = (t: t) => Jstat.normal##sample(t.mean, t.stdev);
let toString = ({mean, stdev}: t) => {j|Normal($mean,$stdev)|j}; let toString = ({mean, stdev}: t) => {j|Normal($mean,$stdev)|j};
let contType: contType = `Continuous;
}; };
module Beta = { module Beta = {
@ -83,6 +90,7 @@ module Beta = {
let inv = (p, t: t) => Jstat.beta##inv(p, t.alpha, t.beta); let inv = (p, t: t) => Jstat.beta##inv(p, t.alpha, t.beta);
let sample = (t: t) => Jstat.beta##sample(t.alpha, t.beta); let sample = (t: t) => Jstat.beta##sample(t.alpha, t.beta);
let toString = ({alpha, beta}: t) => {j|Beta($alpha,$beta)|j}; let toString = ({alpha, beta}: t) => {j|Beta($alpha,$beta)|j};
let contType: contType = `Continuous;
}; };
module Lognormal = { module Lognormal = {
@ -91,6 +99,7 @@ module Lognormal = {
let inv = (p, t: t) => Jstat.lognormal##inv(p, t.mu, t.sigma); let inv = (p, t: t) => Jstat.lognormal##inv(p, t.mu, t.sigma);
let sample = (t: t) => Jstat.lognormal##sample(t.mu, t.sigma); let sample = (t: t) => Jstat.lognormal##sample(t.mu, t.sigma);
let toString = ({mu, sigma}: t) => {j|Lognormal($mu,$sigma)|j}; let toString = ({mu, sigma}: t) => {j|Lognormal($mu,$sigma)|j};
let contType: contType = `Continuous;
let from90PercentCI = (low, high) => { let from90PercentCI = (low, high) => {
let logLow = Js.Math.log(low); let logLow = Js.Math.log(low);
let logHigh = Js.Math.log(high); let logHigh = Js.Math.log(high);
@ -118,6 +127,16 @@ module Uniform = {
let inv = (p, t: t) => Jstat.uniform##inv(p, t.low, t.high); let inv = (p, t: t) => Jstat.uniform##inv(p, t.low, t.high);
let sample = (t: t) => Jstat.uniform##sample(t.low, t.high); let sample = (t: t) => Jstat.uniform##sample(t.low, t.high);
let toString = ({low, high}: t) => {j|Uniform($low,$high)|j}; let toString = ({low, high}: t) => {j|Uniform($low,$high)|j};
let contType: contType = `Continuous;
};
module Float = {
type t = float;
let pdf = (x, t: t) => x == t ? 1.0 : 0.0;
let inv = (p, t: t) => p < t ? 0.0 : 1.0;
let sample = (t: t) => t;
let toString = Js.Float.toString;
let contType: contType = `Discrete;
}; };
module GenericSimple = { module GenericSimple = {
@ -133,6 +152,19 @@ module GenericSimple = {
| `Lognormal(n) => Lognormal.pdf(x, n) | `Lognormal(n) => Lognormal.pdf(x, n)
| `Uniform(n) => Uniform.pdf(x, n) | `Uniform(n) => Uniform.pdf(x, n)
| `Beta(n) => Beta.pdf(x, n) | `Beta(n) => Beta.pdf(x, n)
| `Float(n) => Float.pdf(x, n)
};
let contType = (dist:dist):contType =>
switch (dist) {
| `Normal(_) => Normal.contType
| `Triangular(_) => Triangular.contType
| `Exponential(_) => Exponential.contType
| `Cauchy(_) => Cauchy.contType
| `Lognormal(_) => Lognormal.contType
| `Uniform(_) => Uniform.contType
| `Beta(_) => Beta.contType
| `Float(_) => Float.contType
}; };
let inv = (x, dist) => let inv = (x, dist) =>
@ -144,6 +176,7 @@ module GenericSimple = {
| `Lognormal(n) => Lognormal.inv(x, n) | `Lognormal(n) => Lognormal.inv(x, n)
| `Uniform(n) => Uniform.inv(x, n) | `Uniform(n) => Uniform.inv(x, n)
| `Beta(n) => Beta.inv(x, n) | `Beta(n) => Beta.inv(x, n)
| `Float(n) => Float.inv(x, n)
}; };
let sample: dist => float = let sample: dist => float =
@ -154,7 +187,8 @@ module GenericSimple = {
| `Cauchy(n) => Cauchy.sample(n) | `Cauchy(n) => Cauchy.sample(n)
| `Lognormal(n) => Lognormal.sample(n) | `Lognormal(n) => Lognormal.sample(n)
| `Uniform(n) => Uniform.sample(n) | `Uniform(n) => Uniform.sample(n)
| `Beta(n) => Beta.sample(n); | `Beta(n) => Beta.sample(n)
| `Float(n) => Float.sample(n);
let toString: dist => string = let toString: dist => string =
fun fun
@ -164,7 +198,8 @@ module GenericSimple = {
| `Normal(n) => Normal.toString(n) | `Normal(n) => Normal.toString(n)
| `Lognormal(n) => Lognormal.toString(n) | `Lognormal(n) => Lognormal.toString(n)
| `Uniform(n) => Uniform.toString(n) | `Uniform(n) => Uniform.toString(n)
| `Beta(n) => Beta.toString(n); | `Beta(n) => Beta.toString(n)
| `Float(n) => Float.toString(n);
let min: dist => float = let min: dist => float =
fun fun
@ -174,7 +209,8 @@ module GenericSimple = {
| `Normal(n) => Normal.inv(minCdfValue, n) | `Normal(n) => Normal.inv(minCdfValue, n)
| `Lognormal(n) => Lognormal.inv(minCdfValue, n) | `Lognormal(n) => Lognormal.inv(minCdfValue, n)
| `Uniform({low}) => low | `Uniform({low}) => low
| `Beta(n) => Beta.inv(minCdfValue, n); | `Beta(n) => Beta.inv(minCdfValue, n)
| `Float(n) => n;
let max: dist => float = let max: dist => float =
fun fun
@ -184,7 +220,8 @@ module GenericSimple = {
| `Normal(n) => Normal.inv(maxCdfValue, n) | `Normal(n) => Normal.inv(maxCdfValue, n)
| `Lognormal(n) => Lognormal.inv(maxCdfValue, n) | `Lognormal(n) => Lognormal.inv(maxCdfValue, n)
| `Beta(n) => Beta.inv(maxCdfValue, n) | `Beta(n) => Beta.inv(maxCdfValue, n)
| `Uniform({high}) => high; | `Uniform({high}) => high
| `Float(n) => n;
let interpolateXs = let interpolateXs =
(~xSelection: [ | `Linear | `ByWeight]=`Linear, dist: dist, sampleCount) => { (~xSelection: [ | `Linear | `ByWeight]=`Linear, dist: dist, sampleCount) => {
@ -197,10 +234,13 @@ module GenericSimple = {
}; };
let toShape = let toShape =
(~xSelection: [ | `Linear | `ByWeight]=`Linear, dist: dist, sampleCount) => { (~xSelection: [ | `Linear | `ByWeight]=`Linear, dist: dist, sampleCount)
: DistTypes.shape => {
let xs = interpolateXs(~xSelection, dist, sampleCount); let xs = interpolateXs(~xSelection, dist, sampleCount);
let ys = xs |> E.A.fmap(r => pdf(r, dist)); let ys = xs |> E.A.fmap(r => pdf(r, dist));
XYShape.T.fromArrays(xs, ys); XYShape.T.fromArrays(xs, ys)
|> Distributions.Continuous.make(`Linear, _)
|> Distributions.Continuous.T.toShape;
}; };
}; };
@ -212,7 +252,7 @@ module PointwiseAddDistributionsWeighted = {
dists |> E.A.fmap(((a, b)) => (a, b /. total)); dists |> E.A.fmap(((a, b)) => (a, b /. total));
}; };
let pdf = (dists: t, x: float) => let pdf = (x: float, dists: t) =>
dists dists
|> E.A.fmap(((e, w)) => GenericSimple.pdf(x, e) *. w) |> E.A.fmap(((e, w)) => GenericSimple.pdf(x, e) *. w)
|> E.A.Floats.sum; |> E.A.Floats.sum;
@ -223,7 +263,17 @@ module PointwiseAddDistributionsWeighted = {
let max = (dists: t) => let max = (dists: t) =>
dists |> E.A.fmap(d => d |> fst |> GenericSimple.max) |> E.A.max; dists |> E.A.fmap(d => d |> fst |> GenericSimple.max) |> E.A.max;
let toShape = (dists: t, sampleCount: int) => { let discreteShape = (dists:t, sampleCount: int) => {
let discrete = dists |> E.A.fmap((((r,e)) => r |> fun
| `Float(r) => Some((r,e))
| _ => None
)) |> E.A.O.concatSomes
|> E.A.fmap(((x, y)):DistTypes.xyShape => ({xs: [|x|], ys: [|y|]}))
|> Distributions.Discrete.reduce((+.))
discrete
}
let continuousShape = (dists:t, sampleCount: int) => {
let xs = let xs =
dists dists
|> E.A.fmap(r => |> E.A.fmap(r =>
@ -237,8 +287,17 @@ module PointwiseAddDistributionsWeighted = {
) )
|> E.A.concatMany; |> E.A.concatMany;
xs |> Array.fast_sort(compare); xs |> Array.fast_sort(compare);
let ys = xs |> E.A.fmap(pdf(dists)); let ys = xs |> E.A.fmap(pdf(_, dists));
XYShape.T.fromArrays(xs, ys); XYShape.T.fromArrays(xs, ys)
|> Distributions.Continuous.make(`Linear, _)
}
let toShape = (dists: t, sampleCount: int) => {
let normalized = normalizeWeights(dists);
let continuous = normalized |> E.A.filter(((r,_)) => GenericSimple.contType(r) == `Continuous) |> continuousShape(_, sampleCount);
let discrete = normalized |> E.A.filter(((r,_)) => GenericSimple.contType(r) == `Discrete) |> discreteShape(_, sampleCount);
let shape = MixedShapeBuilder.buildSimple(~continuous, ~discrete);
shape |> E.O.toExt("")
}; };
let toString = (dists: t) => { let toString = (dists: t) => {