Add linear-time continuous XYShape combine function

This commit is contained in:
Sebastian Kosch 2020-07-07 15:24:30 -07:00
parent 8d09cf9beb
commit 2ddf0c02cb
4 changed files with 63 additions and 13 deletions

View File

@ -171,7 +171,7 @@ let make = () => {
~onSubmit=({state}) => {None},
~initialState={
//guesstimatorString: "mm(normal(-10, 2), uniform(18, 25), lognormal({mean: 10, stdev: 8}), triangular(31,40,50))",
guesstimatorString: "uniform(1,2) * uniform(2, 3)", // , triangular(30, 40, 60)
guesstimatorString: "mm(normal(-5,1), normal(0, 1), normal(10, 1), normal(11, 1), normal(16, 1))", // , triangular(30, 40, 60)
domainType: "Complete",
xPoint: "50.0",
xPoint2: "60.0",

View File

@ -98,7 +98,7 @@ module Continuous = {
let combinePointwise =
(
~knownIntegralSumsFn,
fn,
fn: (float => float => float),
t1: DistTypes.continuousShape,
t2: DistTypes.continuousShape,
)
@ -112,18 +112,15 @@ module Continuous = {
t2.knownIntegralSum,
);
let res = make(
make(
`Linear,
XYShape.PointwiseCombination.combine(
~xsSelection=ALL_XS,
~xToYSelection=XYShape.XtoY.linear,
~fn,
XYShape.PointwiseCombination.combineLinear(
~fn=(+.),
t1.xyShape,
t2.xyShape,
),
combinedIntegralSum,
);
res
};
let toLinear = (t: t): option(t) => {

View File

@ -175,6 +175,62 @@ module PointwiseCombination = {
| ALL_XS
| XS_EVENLY_DIVIDED(int);
let combineLinear = [%raw {| // : (float => float => float, T.t, T.t) => T.t
// This function combines two xyShapes by looping through both of them simultaneously.
// It always moves on to the next smallest x, whether that's in the first or second input's xs,
// and interpolates the value on the other side, thus accumulating xs and ys.
// In this implementation (unlike in XtoY.linear above), values outside of a shape's xs are considered 0.0 (instead of firstY or lastY).
// This is written in raw JS because this can still be a bottleneck, and using refs for the i and j indices is quite painful.
function(fn, t1, t2) {
let t1n = t1.xs.length;
let t2n = t2.xs.length;
let outX = [];
let outY = [];
let i = -1;
let j = -1;
while (i <= t1n - 1 && j <= t2n - 1) {
let x, ya, yb;
if (j == t2n - 1 && i < t1n - 1 ||
t1.xs[i+1] < t2.xs[j+1]) { // if a has to catch up to b, or if b is already done
i++;
x = t1.xs[i];
ya = t1.ys[i];
let bFraction = (x - (t2.xs[j] || x)) / ((t2.xs[j+1] || x) - (t2.xs[j] || 0));
yb = (t2.ys[j] || 0) * (1-bFraction) + (t2.ys[j+1] || 0) * bFraction;
} else if (i == t1n - 1 && j < t2n - 1 ||
t1.xs[i+1] > t2.xs[j+1]) { // if b has to catch up to a, or if a is already done
j++;
x = t2.xs[j];
yb = t2.ys[j];
let aFraction = (x - (t1.xs[i] || x)) / ((t1.xs[i+1] || x) - (t1.xs[i] || 0));
ya = (t1.ys[i] || 0) * (1-aFraction) + (t1.ys[i+1] || 0) * aFraction;
} else if (i < t1n - 1 && j < t2n && t1.xs[i+1] === t2.xs[j+1]) { // if they happen to be equal, move both ahead
i++;
j++;
x = t1.xs[i];
ya = t1.ys[i];
yb = t2.ys[j];
} else if (i === t1n - 1 && j === t2n - 1) {
// finished!
i = t1n;
j = t2n;
continue;
} else {
console.log("Error!", i, j);
}
outX.push(x);
outY.push(fn(ya, yb));
}
return {xs: outX, ys: outY};
}
|}];
let combine =
(
~xToYSelection: (float, T.t) => 'a,
@ -204,7 +260,7 @@ module PointwiseCombination = {
}
};
let combineLinear = combine(~xToYSelection=XtoY.linear);
//let combineLinear = combine(~xToYSelection=XtoY.linear);
let combineStepwise = combine(~xToYSelection=XtoY.stepwiseIncremental);
let combineIfAtX = combine(~xToYSelection=XtoY.stepwiseIfAtX);

View File

@ -168,11 +168,8 @@ module Normalize = {
let rec operationToLeaf =
(toLeaf, renderParams, t: node): result(node, string) => {
switch (t) {
| `RenderedDist(s) => {
Js.log2("normed", Distributions.Shape.T.normalize(s));
//Js.log2("normed integral", Distributions.Shape.T.normalize(s)));
| `RenderedDist(s) =>
Ok(`RenderedDist(Distributions.Shape.T.normalize(s)));
}
| `SymbolicDist(_) => Ok(t)
| _ =>
t