Minor refactor
This commit is contained in:
parent
64eef2b169
commit
dceea9c6b5
|
@ -1,11 +0,0 @@
|
||||||
open Jest;
|
|
||||||
open Expect;
|
|
||||||
|
|
||||||
describe("Shape", () => {
|
|
||||||
describe("Continuous", () => {
|
|
||||||
test("", () => {
|
|
||||||
Js.log(Jstat.Jstat.normal);
|
|
||||||
expect(Jstat.Jstat.normal##pdf(3.0, 3.0, 3.0)) |> toEqual(1.0);
|
|
||||||
})
|
|
||||||
})
|
|
||||||
});
|
|
|
@ -1,13 +0,0 @@
|
||||||
open Jest;
|
|
||||||
open Expect;
|
|
||||||
|
|
||||||
describe("Shape", () => {
|
|
||||||
describe("Parser", () => {
|
|
||||||
test("", () => {
|
|
||||||
let parsed1 = MathJsParser.fromString("mm(normal(0,1), normal(10,1))");
|
|
||||||
Js.log(parsed1 |> E.R.fmap(Jstat.toString));
|
|
||||||
Js.log(parsed1 |> E.R.fmap(Jstat.toShape(20)));
|
|
||||||
expect(1.0) |> toEqual(1.0);
|
|
||||||
})
|
|
||||||
})
|
|
||||||
});
|
|
|
@ -37,13 +37,13 @@ module DemoDist = {
|
||||||
let parsed1 = MathJsParser.fromString(guesstimatorString);
|
let parsed1 = MathJsParser.fromString(guesstimatorString);
|
||||||
let shape =
|
let shape =
|
||||||
switch (parsed1) {
|
switch (parsed1) {
|
||||||
| Ok(r) => Some(Jstat.toShape(10000, r))
|
| Ok(r) => Some(SymbolicDist.toShape(10000, r))
|
||||||
| _ => None
|
| _ => None
|
||||||
};
|
};
|
||||||
|
|
||||||
let str =
|
let str =
|
||||||
switch (parsed1) {
|
switch (parsed1) {
|
||||||
| Ok(r) => Jstat.toString(r)
|
| Ok(r) => SymbolicDist.toString(r)
|
||||||
| Error(e) => e
|
| Error(e) => e
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,189 +1,33 @@
|
||||||
// Todo: Another way of doing this is with [@bs.scope "normal"], which may be more elegant
|
// Todo: Another way of doing this is with [@bs.scope "normal"], which may be more elegant
|
||||||
module Jstat = {
|
|
||||||
type normal = {
|
|
||||||
.
|
|
||||||
[@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 lognormal = {
|
|
||||||
.
|
|
||||||
[@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 uniform = {
|
|
||||||
.
|
|
||||||
[@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,
|
|
||||||
};
|
|
||||||
[@bs.module "jStat"] external normal: normal = "normal";
|
|
||||||
[@bs.module "jStat"] external lognormal: lognormal = "lognormal";
|
|
||||||
[@bs.module "jStat"] external uniform: uniform = "uniform";
|
|
||||||
};
|
|
||||||
|
|
||||||
type normal = {
|
type normal = {
|
||||||
mean: float,
|
.
|
||||||
stdev: float,
|
[@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 lognormal = {
|
type lognormal = {
|
||||||
mu: float,
|
.
|
||||||
sigma: float,
|
[@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 uniform = {
|
type uniform = {
|
||||||
low: float,
|
.
|
||||||
high: float,
|
[@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 beta = {
|
||||||
module Normal = {
|
.
|
||||||
type t = normal;
|
[@bs.meth] "pdf": (float, float, float) => float,
|
||||||
let pdf = (x, t: t) => Jstat.normal##pdf(x, t.mean, t.stdev);
|
[@bs.meth] "cdf": (float, float, float) => float,
|
||||||
let inv = (p, t: t) => Jstat.normal##inv(p, t.mean, t.stdev);
|
[@bs.meth] "inv": (float, float, float) => float,
|
||||||
let sample = (t: t) => Jstat.normal##sample(t.mean, t.stdev);
|
[@bs.meth] "sample": (float, float) => float,
|
||||||
let toString = ({mean, stdev}: t) => {j|Normal($mean,$stdev)|j};
|
|
||||||
};
|
};
|
||||||
|
[@bs.module "jStat"] external normal: normal = "normal";
|
||||||
module Lognormal = {
|
[@bs.module "jStat"] external lognormal: lognormal = "lognormal";
|
||||||
type t = lognormal;
|
[@bs.module "jStat"] external uniform: uniform = "uniform";
|
||||||
let pdf = (x, t: t) => Jstat.lognormal##pdf(x, t.mu, t.sigma);
|
[@bs.module "jStat"] external beta: beta = "beta";
|
||||||
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 toString = ({mu, sigma}: t) => {j|Lognormal($mu,$sigma)|j};
|
|
||||||
};
|
|
||||||
|
|
||||||
module Uniform = {
|
|
||||||
type t = uniform;
|
|
||||||
let pdf = (x, t: t) => Jstat.uniform##pdf(x, 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 toString = ({low, high}: t) => {j|Uniform($low,$high)|j};
|
|
||||||
};
|
|
||||||
|
|
||||||
type dist = [
|
|
||||||
| `Normal(normal)
|
|
||||||
| `Lognormal(lognormal)
|
|
||||||
| `Uniform(uniform)
|
|
||||||
];
|
|
||||||
|
|
||||||
module Mixed = {
|
|
||||||
let pdf = (x, dist) =>
|
|
||||||
switch (dist) {
|
|
||||||
| `Normal(n) => Normal.pdf(x, n)
|
|
||||||
| `Lognormal(n) => Lognormal.pdf(x, n)
|
|
||||||
| `Uniform(n) => Uniform.pdf(x, n)
|
|
||||||
};
|
|
||||||
|
|
||||||
let inv = (x, dist) =>
|
|
||||||
switch (dist) {
|
|
||||||
| `Normal(n) => Normal.inv(x, n)
|
|
||||||
| `Lognormal(n) => Lognormal.inv(x, n)
|
|
||||||
| `Uniform(n) => Uniform.inv(x, n)
|
|
||||||
};
|
|
||||||
|
|
||||||
let sample = dist =>
|
|
||||||
switch (dist) {
|
|
||||||
| `Normal(n) => Normal.sample(n)
|
|
||||||
| `Lognormal(n) => Lognormal.sample(n)
|
|
||||||
| `Uniform(n) => Uniform.sample(n)
|
|
||||||
};
|
|
||||||
|
|
||||||
let toString = dist =>
|
|
||||||
switch (dist) {
|
|
||||||
| `Normal(n) => Normal.toString(n)
|
|
||||||
| `Lognormal(n) => Lognormal.toString(n)
|
|
||||||
| `Uniform(n) => Uniform.toString(n)
|
|
||||||
};
|
|
||||||
|
|
||||||
let min = dist =>
|
|
||||||
switch (dist) {
|
|
||||||
| `Normal(n) => Normal.inv(0.0001, n)
|
|
||||||
| `Lognormal(n) => Lognormal.inv(0.0001, n)
|
|
||||||
| `Uniform({low}) => low
|
|
||||||
};
|
|
||||||
|
|
||||||
let max = dist =>
|
|
||||||
switch (dist) {
|
|
||||||
| `Normal(n) => Normal.inv(0.9999, n)
|
|
||||||
| `Lognormal(n) => Lognormal.inv(0.9999, n)
|
|
||||||
| `Uniform({high}) => high
|
|
||||||
};
|
|
||||||
|
|
||||||
// will space linear
|
|
||||||
let toShape =
|
|
||||||
(~xSelection: [ | `Linear | `ByWeight]=`Linear, dist: dist, sampleCount) => {
|
|
||||||
let xs =
|
|
||||||
switch (xSelection) {
|
|
||||||
| `Linear => Functions.range(min(dist), max(dist), sampleCount)
|
|
||||||
| `ByWeight =>
|
|
||||||
Functions.range(0.00001, 0.99999, sampleCount)
|
|
||||||
|> E.A.fmap(x => inv(x, dist))
|
|
||||||
};
|
|
||||||
let ys = xs |> E.A.fmap(r => pdf(r, dist));
|
|
||||||
XYShape.T.fromArrays(xs, ys);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// module PointwiseCombination = {
|
|
||||||
// type math = Multiply | Add | Exponent | Power;
|
|
||||||
// let fn = fun
|
|
||||||
// | Multiply => 3.0
|
|
||||||
// | Add => 4.0
|
|
||||||
// }
|
|
||||||
|
|
||||||
module PointwiseAddDistributionsWeighted = {
|
|
||||||
type t = array((dist, float));
|
|
||||||
|
|
||||||
let normalizeWeights = (dists: t) => {
|
|
||||||
let total = dists |> E.A.fmap(snd) |> Functions.sum;
|
|
||||||
dists |> E.A.fmap(((a, b)) => (a, b /. total));
|
|
||||||
};
|
|
||||||
|
|
||||||
let pdf = (dists: t, x: float) =>
|
|
||||||
dists |> E.A.fmap(((e, w)) => Mixed.pdf(x, e) *. w) |> Functions.sum;
|
|
||||||
|
|
||||||
let min = (dists: t) =>
|
|
||||||
dists |> E.A.fmap(d => d |> fst |> Mixed.min) |> Functions.min;
|
|
||||||
|
|
||||||
let max = (dists: t) =>
|
|
||||||
dists |> E.A.fmap(d => d |> fst |> Mixed.max) |> Functions.max;
|
|
||||||
|
|
||||||
let toShape = (dists: t, sampleCount: int) => {
|
|
||||||
let xs = Functions.range(min(dists), max(dists), sampleCount);
|
|
||||||
let ys = xs |> E.A.fmap(pdf(dists));
|
|
||||||
XYShape.T.fromArrays(xs, ys);
|
|
||||||
};
|
|
||||||
|
|
||||||
let toString = (dists: t) => {
|
|
||||||
let distString =
|
|
||||||
dists
|
|
||||||
|> E.A.fmap(d => Mixed.toString(fst(d)))
|
|
||||||
|> Js.Array.joinWith(",");
|
|
||||||
{j|pointwideAdded($distString)|j};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
type bigDist = [
|
|
||||||
| `Dist(dist)
|
|
||||||
| `PointwiseCombination(PointwiseAddDistributionsWeighted.t)
|
|
||||||
];
|
|
||||||
|
|
||||||
let toString = (r: bigDist) =>
|
|
||||||
r
|
|
||||||
|> (
|
|
||||||
fun
|
|
||||||
| `Dist(d) => Mixed.toString(d)
|
|
||||||
| `PointwiseCombination(d) =>
|
|
||||||
PointwiseAddDistributionsWeighted.toString(d)
|
|
||||||
);
|
|
||||||
|
|
||||||
let toShape = n =>
|
|
||||||
fun
|
|
||||||
| `Dist(d) => Mixed.toShape(~xSelection=`ByWeight, d, n)
|
|
||||||
| `PointwiseCombination(d) =>
|
|
||||||
PointwiseAddDistributionsWeighted.toShape(d, n);
|
|
|
@ -1,110 +1,187 @@
|
||||||
open Jstat;
|
module MathJsonToMathJsAdt = {
|
||||||
|
type arg =
|
||||||
|
| Symbol(string)
|
||||||
|
| Value(float)
|
||||||
|
| Fn(fn)
|
||||||
|
| Array(array(arg))
|
||||||
|
| Object(Js.Dict.t(arg))
|
||||||
|
and fn = {
|
||||||
|
name: string,
|
||||||
|
args: array(arg),
|
||||||
|
};
|
||||||
|
|
||||||
type arg =
|
let rec run = (j: Js.Json.t) =>
|
||||||
| Symbol(string)
|
Json.Decode.(
|
||||||
| Value(float)
|
switch (field("mathjs", string, j)) {
|
||||||
| Fn(fn)
|
| "FunctionNode" =>
|
||||||
| Array(array(arg))
|
let args = j |> field("args", array(run));
|
||||||
and fn = {
|
Some(
|
||||||
name: string,
|
Fn({
|
||||||
args: array(arg),
|
name: j |> field("fn", field("name", string)),
|
||||||
|
args: args |> E.A.O.concatSomes,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
| "OperatorNode" =>
|
||||||
|
let args = j |> field("args", array(run));
|
||||||
|
Some(
|
||||||
|
Fn({
|
||||||
|
name: j |> field("fn", string),
|
||||||
|
args: args |> E.A.O.concatSomes,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
| "ConstantNode" =>
|
||||||
|
optional(field("value", Json.Decode.float), j)
|
||||||
|
|> E.O.fmap(r => Value(r))
|
||||||
|
| "ObjectNode" =>
|
||||||
|
let properties = j |> field("properties", dict(run));
|
||||||
|
Js.Dict.entries(properties)
|
||||||
|
|> E.A.fmap(((key, value)) => value |> E.O.fmap(v => (key, v)))
|
||||||
|
|> E.A.O.concatSomes
|
||||||
|
|> Js.Dict.fromArray
|
||||||
|
|> (r => Some(Object(r)));
|
||||||
|
| "ArrayNode" =>
|
||||||
|
let items = field("items", array(run), j);
|
||||||
|
Some(Array(items |> E.A.O.concatSomes));
|
||||||
|
| "SymbolNode" => Some(Symbol(field("name", string, j)))
|
||||||
|
| n =>
|
||||||
|
Js.log3("Couldn't parse mathjs node", j, n);
|
||||||
|
None;
|
||||||
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
let rec parseMathjs = (j: Js.Json.t) =>
|
module MathAdtToDistDst = {
|
||||||
Json.Decode.(
|
open MathJsonToMathJsAdt;
|
||||||
switch (field("mathjs", string, j)) {
|
|
||||||
| "FunctionNode" =>
|
|
||||||
let args = j |> field("args", array(parseMathjs));
|
|
||||||
Some(
|
|
||||||
Fn({
|
|
||||||
name: j |> field("fn", field("name", string)),
|
|
||||||
args: args |> E.A.O.concatSomes,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
| "OperatorNode" =>
|
|
||||||
let args = j |> field("args", array(parseMathjs));
|
|
||||||
Some(
|
|
||||||
Fn({
|
|
||||||
name: j |> field("fn", string),
|
|
||||||
args: args |> E.A.O.concatSomes,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
| "ConstantNode" => Some(Value(field("value", Json.Decode.float, j)))
|
|
||||||
| "ArrayNode" =>
|
|
||||||
let items = field("items", array(parseMathjs), j);
|
|
||||||
Some(Array(items |> E.A.O.concatSomes));
|
|
||||||
| "SymbolNode" => Some(Symbol(field("name", string, j)))
|
|
||||||
| n =>
|
|
||||||
Js.log2("Couldn't parse mathjs node", j);
|
|
||||||
None;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// let logHigh = math.log(high);
|
module MathAdtCleaner = {
|
||||||
// let logLow = math.log(low);
|
let transformWithSymbol = (f: float, s: string) =>
|
||||||
|
switch (s) {
|
||||||
|
| "K"
|
||||||
|
| "k" => f *. 1000.
|
||||||
|
| "M"
|
||||||
|
| "m" => f *. 1000000.
|
||||||
|
| "B"
|
||||||
|
| "b" => f *. 1000000000.
|
||||||
|
| "T"
|
||||||
|
| "t" => f *. 1000000000000.
|
||||||
|
| _ => f
|
||||||
|
};
|
||||||
|
|
||||||
// let mean = (math.mean(logHigh, logLow)).toFixed(3);
|
let rec run =
|
||||||
// let stdev = ((logHigh-logLow) / (2*1.645)).toFixed(3);
|
fun
|
||||||
|
| Fn({name: "multiply", args: [|Value(f), Symbol(s)|]}) =>
|
||||||
|
Value(transformWithSymbol(f, s))
|
||||||
|
| Fn({name, args}) => Fn({name, args: args |> E.A.fmap(run)})
|
||||||
|
| Array(args) => Array(args |> E.A.fmap(run))
|
||||||
|
| Symbol(s) => Symbol(s)
|
||||||
|
| Value(v) => Value(v)
|
||||||
|
| Object(v) =>
|
||||||
|
Object(
|
||||||
|
v
|
||||||
|
|> Js.Dict.entries
|
||||||
|
|> E.A.fmap(((key, value)) => (key, run(value)))
|
||||||
|
|> Js.Dict.fromArray,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
let normal: array(arg) => result(bigDist, string) =
|
let normal: array(arg) => result(SymbolicDist.bigDist, string) =
|
||||||
fun
|
|
||||||
| [|Value(mean), Value(stdev)|] => Ok(`Dist(`Normal({mean, stdev})))
|
|
||||||
| _ => Error("Wrong number of variables in normal distribution");
|
|
||||||
|
|
||||||
let lognormal: array(arg) => result(bigDist, string) =
|
|
||||||
fun
|
|
||||||
| [|Value(mu), Value(sigma)|] => Ok(`Dist(`Lognormal({mu, sigma})))
|
|
||||||
| _ => Error("Wrong number of variables in lognormal distribution");
|
|
||||||
|
|
||||||
let to_: array(arg) => result(bigDist, string) =
|
|
||||||
fun
|
|
||||||
| [|Value(low), Value(high)|] => {
|
|
||||||
let logLow = Js.Math.log(low);
|
|
||||||
let logHigh = Js.Math.log(high);
|
|
||||||
let mu = Functions.mean([|logLow, logHigh|]);
|
|
||||||
let sigma = (logHigh -. logLow) /. (2.0 *. 1.645);
|
|
||||||
Ok(`Dist(`Lognormal({mu, sigma})));
|
|
||||||
}
|
|
||||||
| _ => Error("Wrong number of variables in lognormal distribution");
|
|
||||||
|
|
||||||
let uniform: array(arg) => result(bigDist, string) =
|
|
||||||
fun
|
|
||||||
| [|Value(low), Value(high)|] => Ok(`Dist(`Uniform({low, high})))
|
|
||||||
| _ => Error("Wrong number of variables in lognormal distribution");
|
|
||||||
|
|
||||||
let rec toValue = (r): result(bigDist, string) =>
|
|
||||||
r
|
|
||||||
|> (
|
|
||||||
fun
|
fun
|
||||||
| Value(_) => Error("Top level can't be value")
|
| [|Value(mean), Value(stdev)|] =>
|
||||||
| Fn({name: "normal", args}) => normal(args)
|
Ok(`Simple(`Normal({mean, stdev})))
|
||||||
| Fn({name: "lognormal", args}) => lognormal(args)
|
| _ => Error("Wrong number of variables in normal distribution");
|
||||||
| Fn({name: "uniform", args}) => uniform(args)
|
|
||||||
| Fn({name: "to", args}) => to_(args)
|
|
||||||
| Fn({name: "mm", args}) => {
|
|
||||||
let dists: array(dist) =
|
|
||||||
args
|
|
||||||
|> E.A.fmap(toValue)
|
|
||||||
|> E.A.fmap(
|
|
||||||
fun
|
|
||||||
| Ok(`Dist(n)) => Some(n)
|
|
||||||
| _ => None,
|
|
||||||
)
|
|
||||||
|> E.A.O.concatSomes;
|
|
||||||
|
|
||||||
let inputs = dists |> E.A.fmap(r => (r, 1.0));
|
let lognormal: array(arg) => result(SymbolicDist.bigDist, string) =
|
||||||
Ok(`PointwiseCombination(inputs));
|
fun
|
||||||
|
| [|Value(mu), Value(sigma)|] => Ok(`Simple(`Lognormal({mu, sigma})))
|
||||||
|
| [|Object(o)|] => {
|
||||||
|
let g = Js.Dict.get(o);
|
||||||
|
switch (g("mean"), g("stdev"), g("mu"), g("sigma")) {
|
||||||
|
| (Some(Value(mean)), Some(Value(stdev)), _, _) =>
|
||||||
|
Ok(`Simple(SymbolicDist.Lognormal.fromMeanAndStdev(mean, stdev)))
|
||||||
|
| (_, _, Some(Value(mu)), Some(Value(sigma))) =>
|
||||||
|
Ok(`Simple(`Lognormal({mu, sigma})))
|
||||||
|
| _ => Error("Lognormal distribution would need mean and stdev")
|
||||||
|
};
|
||||||
}
|
}
|
||||||
| Fn({name}) => Error(name ++ ": name not found")
|
| _ => Error("Wrong number of variables in lognormal distribution");
|
||||||
| Array(_) => Error("Array not valid as top level")
|
|
||||||
| Symbol(_) => Error("Symbol not valid as top level")
|
|
||||||
);
|
|
||||||
|
|
||||||
let fromString = str =>
|
let to_: array(arg) => result(SymbolicDist.bigDist, string) =
|
||||||
Mathjs.parseMath(str)
|
fun
|
||||||
|> E.R.bind(_, r =>
|
| [|Value(low), Value(high)|] => {
|
||||||
switch (parseMathjs(r)) {
|
Ok(`Simple(SymbolicDist.Lognormal.from90PercentCI(low, high)));
|
||||||
| Some(r) => toValue(r)
|
}
|
||||||
| None => Error("Second parse failed")
|
| _ => Error("Wrong number of variables in lognormal distribution");
|
||||||
}
|
|
||||||
);
|
let uniform: array(arg) => result(SymbolicDist.bigDist, string) =
|
||||||
|
fun
|
||||||
|
| [|Value(low), Value(high)|] => Ok(`Simple(`Uniform({low, high})))
|
||||||
|
| _ => Error("Wrong number of variables in lognormal distribution");
|
||||||
|
|
||||||
|
let beta: array(arg) => result(SymbolicDist.bigDist, string) =
|
||||||
|
fun
|
||||||
|
| [|Value(alpha), Value(beta)|] => Ok(`Simple(`Beta({alpha, beta})))
|
||||||
|
| _ => Error("Wrong number of variables in lognormal distribution");
|
||||||
|
|
||||||
|
let multiModal = (args: array(result(SymbolicDist.bigDist, string))) => {
|
||||||
|
let dists =
|
||||||
|
args
|
||||||
|
|> E.A.fmap(
|
||||||
|
fun
|
||||||
|
| Ok(`Simple(n)) => Some(n)
|
||||||
|
| _ => None,
|
||||||
|
)
|
||||||
|
|> E.A.O.concatSomes;
|
||||||
|
switch (dists |> E.A.length) {
|
||||||
|
| 0 => Error("Multimodals need at least one input")
|
||||||
|
| _ =>
|
||||||
|
dists
|
||||||
|
|> E.A.fmap(r => (r, 1.0))
|
||||||
|
|> (r => Ok(`PointwiseCombination(r)))
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
let rec functionParser = (r): result(SymbolicDist.bigDist, string) =>
|
||||||
|
r
|
||||||
|
|> (
|
||||||
|
fun
|
||||||
|
| Fn({name: "normal", args}) => normal(args)
|
||||||
|
| Fn({name: "lognormal", args}) => lognormal(args)
|
||||||
|
| Fn({name: "uniform", args}) => uniform(args)
|
||||||
|
| Fn({name: "beta", args}) => beta(args)
|
||||||
|
| Fn({name: "to", args}) => to_(args)
|
||||||
|
| Fn({name: "mm", args}) => {
|
||||||
|
let dists = args |> E.A.fmap(functionParser);
|
||||||
|
multiModal(dists);
|
||||||
|
}
|
||||||
|
| Fn({name}) => Error(name ++ ": name not found")
|
||||||
|
| _ => Error("This type not currently supported")
|
||||||
|
);
|
||||||
|
|
||||||
|
let topLevel = (r): result(SymbolicDist.bigDist, string) =>
|
||||||
|
r
|
||||||
|
|> (
|
||||||
|
fun
|
||||||
|
| Fn(_) => functionParser(r)
|
||||||
|
| Value(_) => Error("Top level can't be value")
|
||||||
|
| Array(_) => Error("Array not valid as top level")
|
||||||
|
| Symbol(_) => Error("Symbol not valid as top level")
|
||||||
|
| Object(_) => Error("Object not valid as top level")
|
||||||
|
);
|
||||||
|
|
||||||
|
let run = (r): result(SymbolicDist.bigDist, string) =>
|
||||||
|
r |> MathAdtCleaner.run |> topLevel;
|
||||||
|
};
|
||||||
|
|
||||||
|
let fromString = str => {
|
||||||
|
let mathJsToJson = Mathjs.parseMath(str);
|
||||||
|
let mathJsParse =
|
||||||
|
E.R.bind(mathJsToJson, r =>
|
||||||
|
switch (MathJsonToMathJsAdt.run(r)) {
|
||||||
|
| Some(r) => Ok(r)
|
||||||
|
| None => Error("MathJsParse Error")
|
||||||
|
}
|
||||||
|
);
|
||||||
|
let value = E.R.bind(mathJsParse, MathAdtToDistDst.run);
|
||||||
|
Js.log4("fromString", mathJsToJson, mathJsParse, value);
|
||||||
|
value;
|
||||||
|
};
|
196
src/symbolic/SymbolicDist.re
Normal file
196
src/symbolic/SymbolicDist.re
Normal file
|
@ -0,0 +1,196 @@
|
||||||
|
type normal = {
|
||||||
|
mean: float,
|
||||||
|
stdev: float,
|
||||||
|
};
|
||||||
|
|
||||||
|
type lognormal = {
|
||||||
|
mu: float,
|
||||||
|
sigma: float,
|
||||||
|
};
|
||||||
|
|
||||||
|
type uniform = {
|
||||||
|
low: float,
|
||||||
|
high: float,
|
||||||
|
};
|
||||||
|
|
||||||
|
type beta = {
|
||||||
|
alpha: float,
|
||||||
|
beta: float,
|
||||||
|
};
|
||||||
|
|
||||||
|
type dist = [
|
||||||
|
| `Normal(normal)
|
||||||
|
| `Beta(beta)
|
||||||
|
| `Lognormal(lognormal)
|
||||||
|
| `Uniform(uniform)
|
||||||
|
];
|
||||||
|
|
||||||
|
type pointwiseAdd = array((dist, float));
|
||||||
|
|
||||||
|
type bigDist = [ | `Simple(dist) | `PointwiseCombination(pointwiseAdd)];
|
||||||
|
|
||||||
|
module Normal = {
|
||||||
|
type t = normal;
|
||||||
|
let pdf = (x, t: t) => Jstat.normal##pdf(x, 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 toString = ({mean, stdev}: t) => {j|Normal($mean,$stdev)|j};
|
||||||
|
};
|
||||||
|
|
||||||
|
module Beta = {
|
||||||
|
type t = beta;
|
||||||
|
let pdf = (x, t: t) => Jstat.beta##pdf(x, 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 toString = ({alpha, beta}: t) => {j|Beta($alpha,$beta)|j};
|
||||||
|
};
|
||||||
|
|
||||||
|
module Lognormal = {
|
||||||
|
type t = lognormal;
|
||||||
|
let pdf = (x, t: t) => Jstat.lognormal##pdf(x, 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 toString = ({mu, sigma}: t) => {j|Lognormal($mu,$sigma)|j};
|
||||||
|
let from90PercentCI = (low, high) => {
|
||||||
|
let logLow = Js.Math.log(low);
|
||||||
|
let logHigh = Js.Math.log(high);
|
||||||
|
let mu = Functions.mean([|logLow, logHigh|]);
|
||||||
|
let sigma = (logHigh -. logLow) /. (2.0 *. 1.645);
|
||||||
|
`Lognormal({mu, sigma});
|
||||||
|
};
|
||||||
|
let fromMeanAndStdev = (mean, stdev) => {
|
||||||
|
let variance = Js.Math.pow_float(~base=stdev, ~exp=2.0);
|
||||||
|
let meanSquared = Js.Math.pow_float(~base=mean, ~exp=2.0);
|
||||||
|
let mu =
|
||||||
|
Js.Math.log(mean) -. 0.5 *. Js.Math.log(variance /. meanSquared +. 1.0);
|
||||||
|
let sigma =
|
||||||
|
Js.Math.pow_float(
|
||||||
|
~base=Js.Math.log(variance /. meanSquared +. 1.0),
|
||||||
|
~exp=0.5,
|
||||||
|
);
|
||||||
|
`Lognormal({mu, sigma});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
module Uniform = {
|
||||||
|
type t = uniform;
|
||||||
|
let pdf = (x, t: t) => Jstat.uniform##pdf(x, 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 toString = ({low, high}: t) => {j|Uniform($low,$high)|j};
|
||||||
|
};
|
||||||
|
|
||||||
|
module GenericSimple = {
|
||||||
|
let minCdfValue = 0.0001;
|
||||||
|
let maxCdfValue = 0.9999;
|
||||||
|
|
||||||
|
let pdf = (x, dist) =>
|
||||||
|
switch (dist) {
|
||||||
|
| `Normal(n) => Normal.pdf(x, n)
|
||||||
|
| `Lognormal(n) => Lognormal.pdf(x, n)
|
||||||
|
| `Uniform(n) => Uniform.pdf(x, n)
|
||||||
|
| `Beta(n) => Beta.pdf(x, n)
|
||||||
|
};
|
||||||
|
|
||||||
|
let inv = (x, dist) =>
|
||||||
|
switch (dist) {
|
||||||
|
| `Normal(n) => Normal.inv(x, n)
|
||||||
|
| `Lognormal(n) => Lognormal.inv(x, n)
|
||||||
|
| `Uniform(n) => Uniform.inv(x, n)
|
||||||
|
| `Beta(n) => Beta.inv(x, n)
|
||||||
|
};
|
||||||
|
|
||||||
|
let sample = dist =>
|
||||||
|
switch (dist) {
|
||||||
|
| `Normal(n) => Normal.sample(n)
|
||||||
|
| `Lognormal(n) => Lognormal.sample(n)
|
||||||
|
| `Uniform(n) => Uniform.sample(n)
|
||||||
|
| `Beta(n) => Beta.sample(n)
|
||||||
|
};
|
||||||
|
|
||||||
|
let toString = dist =>
|
||||||
|
switch (dist) {
|
||||||
|
| `Normal(n) => Normal.toString(n)
|
||||||
|
| `Lognormal(n) => Lognormal.toString(n)
|
||||||
|
| `Uniform(n) => Uniform.toString(n)
|
||||||
|
| `Beta(n) => Beta.toString(n)
|
||||||
|
};
|
||||||
|
|
||||||
|
let min = dist =>
|
||||||
|
switch (dist) {
|
||||||
|
| `Normal(n) => Normal.inv(minCdfValue, n)
|
||||||
|
| `Lognormal(n) => Lognormal.inv(minCdfValue, n)
|
||||||
|
| `Uniform({low}) => low
|
||||||
|
| `Beta(n) => Beta.inv(minCdfValue, n)
|
||||||
|
};
|
||||||
|
|
||||||
|
let max = dist =>
|
||||||
|
switch (dist) {
|
||||||
|
| `Normal(n) => Normal.inv(maxCdfValue, n)
|
||||||
|
| `Lognormal(n) => Lognormal.inv(maxCdfValue, n)
|
||||||
|
| `Beta(n) => Beta.inv(maxCdfValue, n)
|
||||||
|
| `Uniform({high}) => high
|
||||||
|
};
|
||||||
|
|
||||||
|
let toShape =
|
||||||
|
(~xSelection: [ | `Linear | `ByWeight]=`Linear, dist: dist, sampleCount) => {
|
||||||
|
let xs =
|
||||||
|
switch (xSelection) {
|
||||||
|
| `Linear => Functions.range(min(dist), max(dist), sampleCount)
|
||||||
|
| `ByWeight =>
|
||||||
|
Functions.range(minCdfValue, maxCdfValue, sampleCount)
|
||||||
|
|> E.A.fmap(x => inv(x, dist))
|
||||||
|
};
|
||||||
|
let ys = xs |> E.A.fmap(r => pdf(r, dist));
|
||||||
|
XYShape.T.fromArrays(xs, ys);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
module PointwiseAddDistributionsWeighted = {
|
||||||
|
type t = pointwiseAdd;
|
||||||
|
|
||||||
|
let normalizeWeights = (dists: t) => {
|
||||||
|
let total = dists |> E.A.fmap(snd) |> Functions.sum;
|
||||||
|
dists |> E.A.fmap(((a, b)) => (a, b /. total));
|
||||||
|
};
|
||||||
|
|
||||||
|
let pdf = (dists: t, x: float) =>
|
||||||
|
dists
|
||||||
|
|> E.A.fmap(((e, w)) => GenericSimple.pdf(x, e) *. w)
|
||||||
|
|> Functions.sum;
|
||||||
|
|
||||||
|
let min = (dists: t) =>
|
||||||
|
dists |> E.A.fmap(d => d |> fst |> GenericSimple.min) |> Functions.min;
|
||||||
|
|
||||||
|
let max = (dists: t) =>
|
||||||
|
dists |> E.A.fmap(d => d |> fst |> GenericSimple.max) |> Functions.max;
|
||||||
|
|
||||||
|
let toShape = (dists: t, sampleCount: int) => {
|
||||||
|
let xs = Functions.range(min(dists), max(dists), sampleCount);
|
||||||
|
let ys = xs |> E.A.fmap(pdf(dists));
|
||||||
|
XYShape.T.fromArrays(xs, ys);
|
||||||
|
};
|
||||||
|
|
||||||
|
let toString = (dists: t) => {
|
||||||
|
let distString =
|
||||||
|
dists
|
||||||
|
|> E.A.fmap(d => GenericSimple.toString(fst(d)))
|
||||||
|
|> Js.Array.joinWith(",");
|
||||||
|
{j|multimodal($distString)|j};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
let toString = (r: bigDist) =>
|
||||||
|
r
|
||||||
|
|> (
|
||||||
|
fun
|
||||||
|
| `Simple(d) => GenericSimple.toString(d)
|
||||||
|
| `PointwiseCombination(d) =>
|
||||||
|
PointwiseAddDistributionsWeighted.toString(d)
|
||||||
|
);
|
||||||
|
|
||||||
|
let toShape = n =>
|
||||||
|
fun
|
||||||
|
| `Simple(d) => GenericSimple.toShape(~xSelection=`ByWeight, d, n)
|
||||||
|
| `PointwiseCombination(d) =>
|
||||||
|
PointwiseAddDistributionsWeighted.toShape(d, n);
|
Loading…
Reference in New Issue
Block a user