diff --git a/src/distPlus/symbolic/MathJsParser.re b/src/distPlus/symbolic/MathJsParser.re index 6dba65e2..f145253e 100644 --- a/src/distPlus/symbolic/MathJsParser.re +++ b/src/distPlus/symbolic/MathJsParser.re @@ -91,19 +91,19 @@ module MathAdtToDistDst = { let normal: array(arg) => result(SymbolicDist.distTree, string) = fun | [|Value(mean), Value(stdev)|] => - Ok(`Distribution(`Normal({mean, stdev}))) + Ok(`Simple(`Normal({mean, stdev}))) | _ => Error("Wrong number of variables in normal distribution"); let lognormal: array(arg) => result(SymbolicDist.distTree, string) = fun - | [|Value(mu), Value(sigma)|] => Ok(`Distribution(`Lognormal({mu, sigma}))) + | [|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(`Distribution(SymbolicDist.Lognormal.fromMeanAndStdev(mean, stdev))) + Ok(`Simple(SymbolicDist.Lognormal.fromMeanAndStdev(mean, stdev))) | (_, _, Some(Value(mu)), Some(Value(sigma))) => - Ok(`Distribution(`Lognormal({mu, sigma}))) + Ok(`Simple(`Lognormal({mu, sigma}))) | _ => Error("Lognormal distribution would need mean and stdev") }; } @@ -112,10 +112,10 @@ module MathAdtToDistDst = { let to_: array(arg) => result(SymbolicDist.distTree, string) = fun | [|Value(low), Value(high)|] when low <= 0.0 && low < high=> { - Ok(`Distribution(SymbolicDist.Normal.from90PercentCI(low, high))); + Ok(`Simple(SymbolicDist.Normal.from90PercentCI(low, high))); } | [|Value(low), Value(high)|] when low < high => { - Ok(`Distribution(SymbolicDist.Lognormal.from90PercentCI(low, high))); + Ok(`Simple(SymbolicDist.Lognormal.from90PercentCI(low, high))); } | [|Value(_), Value(_)|] => Error("Low value must be less than high value.") @@ -123,29 +123,29 @@ module MathAdtToDistDst = { let uniform: array(arg) => result(SymbolicDist.distTree, string) = fun - | [|Value(low), Value(high)|] => Ok(`Distribution(`Uniform({low, high}))) + | [|Value(low), Value(high)|] => Ok(`Simple(`Uniform({low, high}))) | _ => Error("Wrong number of variables in lognormal distribution"); let beta: array(arg) => result(SymbolicDist.distTree, string) = fun - | [|Value(alpha), Value(beta)|] => Ok(`Distribution(`Beta({alpha, beta}))) + | [|Value(alpha), Value(beta)|] => Ok(`Simple(`Beta({alpha, beta}))) | _ => Error("Wrong number of variables in lognormal distribution"); let exponential: array(arg) => result(SymbolicDist.distTree, string) = fun - | [|Value(rate)|] => Ok(`Distribution(`Exponential({rate: rate}))) + | [|Value(rate)|] => Ok(`Simple(`Exponential({rate: rate}))) | _ => Error("Wrong number of variables in Exponential distribution"); let cauchy: array(arg) => result(SymbolicDist.distTree, string) = fun | [|Value(local), Value(scale)|] => - Ok(`Distribution(`Cauchy({local, scale}))) + Ok(`Simple(`Cauchy({local, scale}))) | _ => Error("Wrong number of variables in cauchy distribution"); let triangular: array(arg) => result(SymbolicDist.distTree, string) = fun | [|Value(low), Value(medium), Value(high)|] => - Ok(`Distribution(`Triangular({low, medium, high}))) + Ok(`Simple(`Triangular({low, medium, high}))) | _ => Error("Wrong number of variables in triangle distribution"); let multiModal = @@ -158,7 +158,7 @@ module MathAdtToDistDst = { args |> E.A.fmap( fun - | Ok(`Distribution(d)) => Ok(`Distribution(d)) + | Ok(`Simple(d)) => Ok(`Simple(d)) | Ok(`Combination(t1, t2, op)) => Ok(`Combination(t1, t2, op)) | Ok(`PointwiseSum(t1, t2)) => Ok(`PointwiseSum(t1, t2)) | Ok(`PointwiseProduct(t1, t2)) => Ok(`PointwiseProduct(t1, t2)) @@ -182,7 +182,7 @@ module MathAdtToDistDst = { |> E.A.fmapi((index, t) => { let w = weights |> E.A.get(_, index) |> E.O.default(1.0); - `VerticalScaling(t, `Distribution(`Float(w))) + `VerticalScaling(t, `Simple(`Float(w))) }); let pointwiseSum = components @@ -197,7 +197,6 @@ module MathAdtToDistDst = { }; let arrayParser = (args:array(arg)):result(SymbolicDist.distTree, string) => { - Js.log2("SAMPLING NOW!", args); let samples = args |> E.A.fmap( fun @@ -213,7 +212,7 @@ module MathAdtToDistDst = { SymbolicDist.ContinuousShape.make(_pdf, cdf) }); switch(shape){ - | Some(s) => Ok(`Distribution(`ContinuousShape(s))) + | Some(s) => Ok(`Simple(`ContinuousShape(s))) | None => Error("Rendering did not work") } } @@ -231,7 +230,7 @@ module MathAdtToDistDst = { | Fn({name: "exponential", args}) => exponential(args) | Fn({name: "cauchy", args}) => cauchy(args) | Fn({name: "triangular", args}) => triangular(args) - | Value(f) => Ok(`Distribution(`Float(f))) + | Value(f) => Ok(`Simple(`Float(f))) | Fn({name: "mm", args}) => { let weights = args @@ -284,7 +283,7 @@ module MathAdtToDistDst = { args |> E.A.fmap(functionParser) |> (fun - | [|Ok(l), Ok(`Distribution(`Float(0.0)))|] => Error("Division by zero") + | [|Ok(l), Ok(`Simple(`Float(0.0)))|] => Error("Division by zero") | [|Ok(l), Ok(r)|] => Ok(`Combination(l, r, `DivideOperation)) | _ => Error("Division needs two operands")) } @@ -299,14 +298,14 @@ module MathAdtToDistDst = { args |> E.A.fmap(functionParser) |> (fun - | [|Ok(l), Ok(`Distribution(`Float(r)))|] => Ok(`LeftTruncate(l, r)) + | [|Ok(l), Ok(`Simple(`Float(r)))|] => Ok(`LeftTruncate(l, r)) | _ => Error("leftTruncate needs two arguments: the expression and the cutoff")) } | Fn({name: "rightTruncate", args}) => { args |> E.A.fmap(functionParser) |> (fun - | [|Ok(l), Ok(`Distribution(`Float(r)))|] => Ok(`RightTruncate(l, r)) + | [|Ok(l), Ok(`Simple(`Float(r)))|] => Ok(`RightTruncate(l, r)) | _ => Error("rightTruncate needs two arguments: the expression and the cutoff")) } | Fn({name}) => Error(name ++ ": function not supported") @@ -320,7 +319,7 @@ module MathAdtToDistDst = { |> ( fun | Fn(_) => functionParser(r) - | Value(r) => Ok(`Distribution(`Float(r))) + | Value(r) => Ok(`Simple(`Float(r))) | Array(r) => arrayParser(r) | Symbol(_) => Error("Symbol not valid as top level") | Object(_) => Error("Object not valid as top level") diff --git a/src/distPlus/symbolic/SymbolicDist.re b/src/distPlus/symbolic/SymbolicDist.re index 7f0fbd7b..48bc7ad5 100644 --- a/src/distPlus/symbolic/SymbolicDist.re +++ b/src/distPlus/symbolic/SymbolicDist.re @@ -61,7 +61,7 @@ type operation = [ ]; type distTree = [ - | `Distribution(dist) + | `Simple(dist) | `Combination(distTree, distTree, operation) | `PointwiseSum(distTree, distTree) | `PointwiseProduct(distTree, distTree) @@ -315,13 +315,13 @@ module GenericSimple = { module DistTree = { type nodeResult = [ - | `Distribution(dist) + | `Simple(dist) // RenderedShape: continuous xyShape, discrete xyShape, total value. | `RenderedShape(DistTypes.continuousShape, DistTypes.discreteShape, integral) ]; let evaluateDistribution = (d: dist): nodeResult => { - `Distribution(d) + `Simple(d) }; // This is a performance bottleneck! @@ -406,7 +406,7 @@ module DistTree = { // First, deal with the discrete-discrete convolution: let (ddxs, ddys) = jsDiscreteCombinationConvolve(sd1.xs, sd1.ys, sd2.xs, sd2.ys, func); - let ddxy: DistTypes.discreteShape = {xs: cdxs, ys: cdys}; + let ddxy: DistTypes.discreteShape = {xs: ddxs, ys: ddys}; // Then, do the other three: let downsample = (sc: DistTypes.continuousShape) => { @@ -415,8 +415,7 @@ module DistTree = { scSqLength > 10. ? Distributions.Continuous.T.truncate(int_of_float(scSqLength), sc) : sc; }; - let combinePointConvolutionResults = ccs - |> E.A.fmap(s => { + let combinePointConvolutionResults = ca => ca |> E.A.fmap(s => { // s is an array of (x, y) objects let (xs, ys) = Belt.Array.unzip(s); Distributions.Continuous.make(`Linear, {xs, ys}); @@ -440,46 +439,46 @@ module DistTree = { let func = funcFromOp(op); switch ((et1, et2, op)) { /* Known cases: replace symbolic with symbolic distribution */ - | (`Distribution(`Float(v1)), `Distribution(`Float(v2)), _) => { - `Distribution(`Float(func(v1, v2))) + | (`Simple(`Float(v1)), `Simple(`Float(v2)), _) => { + `Simple(`Float(func(v1, v2))) } - | (`Distribution(`Normal(n2)), `Distribution(`Float(v1)), `AddOperation) - | (`Distribution(`Float(v1)), `Distribution(`Normal(n2)), `AddOperation) => { + | (`Simple(`Normal(n2)), `Simple(`Float(v1)), `AddOperation) + | (`Simple(`Float(v1)), `Simple(`Normal(n2)), `AddOperation) => { let n: normal = {mean: v1 +. n2.mean, stdev: n2.stdev}; - `Distribution(`Normal(n)) + `Simple(`Normal(n)) } - | (`Distribution(`Normal(n1)), `Distribution(`Normal(n2)), `AddOperation) => { + | (`Simple(`Normal(n1)), `Simple(`Normal(n2)), `AddOperation) => { let n: normal = {mean: n1.mean +. n2.mean, stdev: sqrt(n1.stdev ** 2. +. n2.stdev ** 2.)}; - `Distribution(`Normal(n)); + `Simple(`Normal(n)); } - | (`Distribution(`Normal(n1)), `Distribution(`Normal(n2)), `SubtractOperation) => { + | (`Simple(`Normal(n1)), `Simple(`Normal(n2)), `SubtractOperation) => { let n: normal = {mean: n1.mean -. n2.mean, stdev: sqrt(n1.stdev ** 2. +. n2.stdev ** 2.)}; - `Distribution(`Normal(n)); + `Simple(`Normal(n)); } - | (`Distribution(`Lognormal(l1)), `Distribution(`Lognormal(l2)), `MultiplyOperation) => { + | (`Simple(`Lognormal(l1)), `Simple(`Lognormal(l2)), `MultiplyOperation) => { let l: lognormal = {mu: l1.mu +. l2.mu, sigma: l1.sigma +. l2.sigma}; - `Distribution(`Lognormal(l)); + `Simple(`Lognormal(l)); } - | (`Distribution(`Lognormal(l1)), `Distribution(`Lognormal(l2)), `DivideOperation) => { + | (`Simple(`Lognormal(l1)), `Simple(`Lognormal(l2)), `DivideOperation) => { let l: lognormal = {mu: l1.mu -. l2.mu, sigma: l1.sigma +. l2.sigma}; - `Distribution(`Lognormal(l)); + `Simple(`Lognormal(l)); } /* General cases: convolve the XYShapes */ - | (`Distribution(d1), `Distribution(d2), _) => { + | (`Simple(d1), `Simple(d2), _) => { let (sc1, sd1) = renderDistributionToXYShape(d1, n); let (sc2, sd2) = renderDistributionToXYShape(d2, n); let (sc, sd) = combinationDistributionOfXYShapes(sc1, sd1, sc2, sd2, func); `RenderedShape(sc, sd, 1.0) } - | (`Distribution(d2), `RenderedShape(sc1, sd1, i1), _) - | (`RenderedShape(sc1, sd1, i1), `Distribution(d2), _) => { + | (`Simple(d1), `RenderedShape(sc2, sd2, i2), _) + | (`RenderedShape(sc2, sd2, i2), `Simple(d1), _) => { let (sc1, sd1) = renderDistributionToXYShape(d1, n); let (sc, sd) = combinationDistributionOfXYShapes(sc1, sd1, sc2, sd2, func); `RenderedShape(sc, sd, i2) @@ -495,24 +494,24 @@ module DistTree = { let evaluatePointwiseSum = (et1: nodeResult, et2: nodeResult, n: int) => { switch ((et1, et2)) { /* Known cases: */ - | (`Distribution(`Float(v1)), `Distribution(`Float(v2))) => { + | (`Simple(`Float(v1)), `Simple(`Float(v2))) => { v1 == v2 ? `RenderedShape(Distributions.Continuous.empty, Distributions.Discrete.make({xs: [|v1|], ys: [|2.|]}), 2.) : `RenderedShape(Distributions.Continuous.empty, Distributions.Discrete.empty, 0.) // TODO: add warning: shouldn't pointwise add scalars. } - | (`Distribution(`Float(v1)), `Distribution(d2)) - | (`Distribution(d2), `Distribution(`Float(v1))) => { + | (`Simple(`Float(v1)), `Simple(d2)) + | (`Simple(d2), `Simple(`Float(v1))) => { let sd1: DistTypes.xyShape = {xs: [|v1|], ys: [|1.|]}; let (sc2, sd2) = renderDistributionToXYShape(d2, n); `RenderedShape(sc2, Distributions.Discrete.reduce((+.), [|sd1, sd2|]), 2.) } - | (`Distribution(d1), `Distribution(d2)) => { + | (`Simple(d1), `Simple(d2)) => { let (sc1, sd1) = renderDistributionToXYShape(d1, n); let (sc2, sd2) = renderDistributionToXYShape(d2, n); `RenderedShape(Distributions.Continuous.reduce((+.), [|sc1, sc2|]), Distributions.Discrete.reduce((+.), [|sd1, sd2|]), 2.) } - | (`Distribution(d1), `RenderedShape(sc2, sd2, i2)) - | (`RenderedShape(sc2, sd2, i2), `Distribution(d1)) => { + | (`Simple(d1), `RenderedShape(sc2, sd2, i2)) + | (`RenderedShape(sc2, sd2, i2), `Simple(d1)) => { let (sc1, sd1) = renderDistributionToXYShape(d1, n); `RenderedShape(Distributions.Continuous.reduce((+.), [|sc1, sc2|]), Distributions.Discrete.reduce((+.), [|sd1, sd2|]), 1. +. i2) } @@ -525,42 +524,42 @@ module DistTree = { let evaluatePointwiseProduct = (et1: nodeResult, et2: nodeResult, n: int) => { switch ((et1, et2)) { /* Known cases: */ - | (`Distribution(`Float(v1)), `Distribution(`Float(v2))) => { + | (`Simple(`Float(v1)), `Simple(`Float(v2))) => { v1 == v2 ? `RenderedShape(Distributions.Continuous.empty, Distributions.Discrete.make({xs: [|v1|], ys: [|1.|]}), 1.) : `RenderedShape(Distributions.Continuous.empty, Distributions.Discrete.empty, 0.) // TODO: add warning: shouldn't pointwise multiply scalars. } - | (`Distribution(`Float(v1)), `Distribution(d2)) => { + | (`Simple(`Float(v1)), `Simple(d2)) => { // evaluate d2 at v1 let y = GenericSimple.pdf(v1, d2); `RenderedShape(Distributions.Continuous.empty, Distributions.Discrete.make({xs: [|v1|], ys: [|y|]}), y) } - | (`Distribution(d1), `Distribution(`Float(v2))) => { + | (`Simple(d1), `Simple(`Float(v2))) => { // evaluate d1 at v2 let y = GenericSimple.pdf(v2, d1); `RenderedShape(Distributions.Continuous.empty, Distributions.Discrete.make({xs: [|v2|], ys: [|y|]}), y) } - | (`Distribution(`Normal(n1)), `Distribution(`Normal(n2))) => { + | (`Simple(`Normal(n1)), `Simple(`Normal(n2))) => { let mean = (n1.mean *. n2.stdev**2. +. n2.mean *. n1.stdev**2.) /. (n1.stdev**2. +. n2.stdev**2.); let stdev = 1. /. ((1. /. n1.stdev**2.) +. (1. /. n2.stdev**2.)); let integral = 0; // TODO `RenderedShape(Distributions.Continuous.empty, Distributions.Discrete.empty, 0.) } /* General cases */ - | (`Distribution(d1), `Distribution(d2)) => { + | (`Simple(d1), `Simple(d2)) => { // NOT IMPLEMENTED YET // TODO: evaluate integral properly let (sc1, sd1) = renderDistributionToXYShape(d1, n); let (sc2, sd2) = renderDistributionToXYShape(d2, n); `RenderedShape(Distributions.Continuous.empty, Distributions.Discrete.empty, 0.) } - | (`Distribution(d1), `RenderedShape(sc2, sd2, i2)) => { + | (`Simple(d1), `RenderedShape(sc2, sd2, i2)) => { // NOT IMPLEMENTED YET // TODO: evaluate integral properly let (sc1, sd1) = renderDistributionToXYShape(d1, n); `RenderedShape(Distributions.Continuous.empty, Distributions.Discrete.empty, 0.) } - | (`RenderedShape(sc1, sd1, i1), `Distribution(d1)) => { + | (`RenderedShape(sc1, sd1, i1), `Simple(d1)) => { // NOT IMPLEMENTED YET // TODO: evaluate integral properly let (sc2, sd2) = renderDistributionToXYShape(d1, n); @@ -588,7 +587,7 @@ module DistTree = { `RenderedShape(scn, sdn, 1.) } - | `Distribution(d) => `Distribution(d) // any kind of atomic dist should already be normalized -- TODO: THIS IS ACTUALLY FALSE! E.g. pointwise product of normal * normal + | `Simple(d) => `Simple(d) // any kind of atomic dist should already be normalized -- TODO: THIS IS ACTUALLY FALSE! E.g. pointwise product of normal * normal } }; @@ -604,7 +603,7 @@ module DistTree = { }; switch (et) { - | `Distribution(d) => { + | `Simple(d) => { let (sc, sd) = renderDistributionToXYShape(d, n); let scc = sc |> Distributions.Continuous.shapeMap(cut); @@ -629,8 +628,8 @@ module DistTree = { let scale = (i: float, s: DistTypes.xyShape): DistTypes.xyShape => {xs: s.xs, ys: s.ys |> E.A.fmap(y => y *. i)}; switch ((et1, et2)) { - | (`Distribution(`Float(v)), `Distribution(d)) - | (`Distribution(d), `Distribution(`Float(v))) => { + | (`Simple(`Float(v)), `Simple(d)) + | (`Simple(d), `Simple(`Float(v))) => { let (sc, sd) = renderDistributionToXYShape(d, n); let scc = sc |> Distributions.Continuous.shapeMap(scale(v)); @@ -640,8 +639,8 @@ module DistTree = { `RenderedShape(scc, sdc, newIntegral); } - | (`Distribution(`Float(v)), `RenderedShape(sc, sd, i)) - | (`RenderedShape(sc, sd, i), `Distribution(`Float(v))) => { + | (`Simple(`Float(v)), `RenderedShape(sc, sd, i)) + | (`RenderedShape(sc, sd, i), `Simple(`Float(v))) => { let scc = sc |> Distributions.Continuous.shapeMap(scale(v)); let sdc = sd |> scale(v); @@ -655,7 +654,7 @@ module DistTree = { let renderNode = (et: nodeResult, n: int) => { switch (et) { - | `Distribution(d) => { + | `Simple(d) => { let (sc, sd) = renderDistributionToXYShape(d, n); `RenderedShape(sc, sd, 1.0); } @@ -666,7 +665,7 @@ module DistTree = { let rec evaluateNode = (treeNode: distTree, n: int): nodeResult => { // returns either a new symbolic distribution switch (treeNode) { - | `Distribution(d) => evaluateDistribution(d) + | `Simple(d) => evaluateDistribution(d) | `Combination(t1, t2, op) => evaluateCombinationDistribution(evaluateNode(t1, n), evaluateNode(t2, n), op, n) | `PointwiseSum(t1, t2) => evaluatePointwiseSum(evaluateNode(t1, n), evaluateNode(t2, n), n) | `PointwiseProduct(t1, t2) => evaluatePointwiseProduct(evaluateNode(t1, n), evaluateNode(t2, n), n) @@ -682,7 +681,7 @@ module DistTree = { let treeShape = evaluateNode(`Render(`Normalize(treeNode)), n); switch (treeShape) { - | `Distribution(_) => E.O.toExn("No shape found!", None) + | `Simple(_) => E.O.toExn("No shape found!", None) | `RenderedShape(sc, sd, _) => { let shape = MixedShapeBuilder.buildSimple(~continuous=Some(sc), ~discrete=sd); @@ -701,7 +700,7 @@ module DistTree = { }; switch (treeNode) { - | `Distribution(d) => GenericSimple.toString(d) + | `Simple(d) => GenericSimple.toString(d) | `Combination(t1, t2, op) => toString(t1) ++ stringFromOp(op) ++ toString(t2) | `PointwiseSum(t1, t2) => toString(t1) ++ " .+ " ++ toString(t2) | `PointwiseProduct(t1, t2) => toString(t1) ++ " .* " ++ toString(t2)