Ozzie's suggestions

This commit is contained in:
Sebastian Kosch 2020-07-18 21:34:55 -07:00
parent 9cdcc85d6c
commit f3922761f4
13 changed files with 251 additions and 285 deletions

View File

@ -17,25 +17,25 @@ let toDiscretePointMassesFromTriangulars =
let n = s |> XYShape.T.length; let n = s |> XYShape.T.length;
// first, double up the leftmost and rightmost points: // first, double up the leftmost and rightmost points:
let {xs, ys}: XYShape.T.t = s; let {xs, ys}: XYShape.T.t = s;
let _ = Js.Array.unshift(xs[0], xs); Js.Array.unshift(xs[0], xs) |> ignore;
let _ = Js.Array.unshift(ys[0], ys); Js.Array.unshift(ys[0], ys) |> ignore;
let _ = Js.Array.push(xs[n - 1], xs); Js.Array.push(xs[n - 1], xs) |> ignore;
let _ = Js.Array.push(ys[n - 1], ys); Js.Array.push(ys[n - 1], ys) |> ignore;
let n = E.A.length(xs); let n = E.A.length(xs);
// squares and neighbourly products of the xs // squares and neighbourly products of the xs
let xsSq: array(float) = Belt.Array.makeUninitializedUnsafe(n); let xsSq: array(float) = Belt.Array.makeUninitializedUnsafe(n);
let xsProdN1: array(float) = Belt.Array.makeUninitializedUnsafe(n - 1); let xsProdN1: array(float) = Belt.Array.makeUninitializedUnsafe(n - 1);
let xsProdN2: array(float) = Belt.Array.makeUninitializedUnsafe(n - 2); let xsProdN2: array(float) = Belt.Array.makeUninitializedUnsafe(n - 2);
for (i in 0 to n - 1) { for (i in 0 to n - 1) {
let _ = Belt.Array.set(xsSq, i, xs[i] *. xs[i]); Belt.Array.set(xsSq, i, xs[i] *. xs[i]) |> ignore;
(); ();
}; };
for (i in 0 to n - 2) { for (i in 0 to n - 2) {
let _ = Belt.Array.set(xsProdN1, i, xs[i] *. xs[i + 1]); Belt.Array.set(xsProdN1, i, xs[i] *. xs[i + 1]) |> ignore;
(); ();
}; };
for (i in 0 to n - 3) { for (i in 0 to n - 3) {
let _ = Belt.Array.set(xsProdN2, i, xs[i] *. xs[i + 2]); Belt.Array.set(xsProdN2, i, xs[i] *. xs[i + 2]) |> ignore;
(); ();
}; };
// means and variances // means and variances
@ -45,12 +45,11 @@ let toDiscretePointMassesFromTriangulars =
if (inverse) { if (inverse) {
for (i in 1 to n - 2) { for (i in 1 to n - 2) {
let _ =
Belt.Array.set( Belt.Array.set(
masses, masses,
i - 1, i - 1,
(xs[i + 1] -. xs[i - 1]) *. ys[i] /. 2., (xs[i + 1] -. xs[i - 1]) *. ys[i] /. 2.,
); ) |> ignore;
// this only works when the whole triange is either on the left or on the right of zero // this only works when the whole triange is either on the left or on the right of zero
let a = xs[i - 1]; let a = xs[i - 1];
@ -71,30 +70,26 @@ let toDiscretePointMassesFromTriangulars =
-. inverseMean -. inverseMean
** 2.; ** 2.;
let _ = Belt.Array.set(means, i - 1, inverseMean); Belt.Array.set(means, i - 1, inverseMean) |> ignore;
let _ = Belt.Array.set(variances, i - 1, inverseVar); Belt.Array.set(variances, i - 1, inverseVar) |> ignore;
(); ();
}; };
{n: n - 2, masses, means, variances}; {n: n - 2, masses, means, variances};
} else { } else {
for (i in 1 to n - 2) { for (i in 1 to n - 2) {
// area of triangle = width * height / 2 // area of triangle = width * height / 2
let _ =
Belt.Array.set( Belt.Array.set(
masses, masses,
i - 1, i - 1,
(xs[i + 1] -. xs[i - 1]) *. ys[i] /. 2., (xs[i + 1] -. xs[i - 1]) *. ys[i] /. 2.,
); ) |> ignore;
// means of triangle = (a + b + c) / 3 // means of triangle = (a + b + c) / 3
let _ = Belt.Array.set(means, i - 1, (xs[i - 1] +. xs[i] +. xs[i + 1]) /. 3.) |> ignore;
Belt.Array.set(means, i - 1, (xs[i - 1] +. xs[i] +. xs[i + 1]) /. 3.);
// variance of triangle = (a^2 + b^2 + c^2 - ab - ac - bc) / 18 // variance of triangle = (a^2 + b^2 + c^2 - ab - ac - bc) / 18
let _ =
Belt.Array.set( Belt.Array.set(
variances, variances,
i - 1, i - 1,
@ -107,7 +102,7 @@ let toDiscretePointMassesFromTriangulars =
-. xsProdN2[i - 1] -. xsProdN2[i - 1]
) )
/. 18., /. 18.,
); ) |> ignore;
(); ();
}; };
{n: n - 2, masses, means, variances}; {n: n - 2, masses, means, variances};
@ -163,7 +158,7 @@ let combineShapesContinuousContinuous =
for (i in 0 to t1m.n - 1) { for (i in 0 to t1m.n - 1) {
for (j in 0 to t2m.n - 1) { for (j in 0 to t2m.n - 1) {
let k = i * t2m.n + j; let k = i * t2m.n + j;
let _ = Belt.Array.set(masses, k, t1m.masses[i] *. t2m.masses[j]); Belt.Array.set(masses, k, t1m.masses[i] *. t2m.masses[j]) |> ignore;
let mean = combineMeansFn(t1m.means[i], t2m.means[j]); let mean = combineMeansFn(t1m.means[i], t2m.means[j]);
let variance = let variance =
@ -173,8 +168,8 @@ let combineShapesContinuousContinuous =
t1m.means[i], t1m.means[i],
t2m.means[j], t2m.means[j],
); );
let _ = Belt.Array.set(means, k, mean); Belt.Array.set(means, k, mean) |> ignore;
let _ = Belt.Array.set(variances, k, variance); Belt.Array.set(variances, k, variance) |> ignore;
// update bounds // update bounds
let minX = mean -. 2. *. sqrt(variance) *. 1.644854; let minX = mean -. 2. *. sqrt(variance) *. 1.644854;
let maxX = mean +. 2. *. sqrt(variance) *. 1.644854; let maxX = mean +. 2. *. sqrt(variance) *. 1.644854;
@ -194,15 +189,15 @@ let combineShapesContinuousContinuous =
let outputYs: array(float) = Belt.Array.make(nOut, 0.0); let outputYs: array(float) = Belt.Array.make(nOut, 0.0);
// now, for each of the outputYs, accumulate from a Gaussian kernel over each input point. // now, for each of the outputYs, accumulate from a Gaussian kernel over each input point.
for (j in 0 to E.A.length(masses) - 1) { // go through all of the result points for (j in 0 to E.A.length(masses) - 1) { // go through all of the result points
let _ = if (variances[j] > 0. && masses[j] > 0.) { if (variances[j] > 0. && masses[j] > 0.) {
for (i in 0 to E.A.length(outputXs) - 1) { // go through all of the target points for (i in 0 to E.A.length(outputXs) - 1) { // go through all of the target points
let dx = outputXs[i] -. means[j]; let dx = outputXs[i] -. means[j];
let contribution = masses[j] *. exp(-. (dx ** 2.) /. (2. *. variances[j])) /. (sqrt(2. *. 3.14159276 *. variances[j])); let contribution = masses[j] *. exp(-. (dx ** 2.) /. (2. *. variances[j])) /. (sqrt(2. *. 3.14159276 *. variances[j]));
let _ = Belt.Array.set(outputYs, i, outputYs[i] +. contribution); Belt.Array.set(outputYs, i, outputYs[i] +. contribution) |> ignore;
(); ();
}; };
(); ();
}; } |> ignore;
(); ();
}; };
@ -219,26 +214,23 @@ let toDiscretePointMassesFromDiscrete = (s: DistTypes.xyShape): pointMassesWithM
let variances: array(float) = Belt.Array.makeUninitializedUnsafe(n); let variances: array(float) = Belt.Array.makeUninitializedUnsafe(n);
for (i in 0 to n - 1) { for (i in 0 to n - 1) {
let _ =
Belt.Array.set( Belt.Array.set(
masses, masses,
i, i,
ys[i] ys[i]
); ) |> ignore;
let _ =
Belt.Array.set( Belt.Array.set(
means, means,
i, i,
xs[i] xs[i]
); ) |> ignore;
let _ =
Belt.Array.set( Belt.Array.set(
variances, variances,
i, i,
0.0 0.0
); ) |> ignore;
(); ();
}; };
@ -246,11 +238,11 @@ let toDiscretePointMassesFromDiscrete = (s: DistTypes.xyShape): pointMassesWithM
}; };
let combineShapesContinuousDiscrete = let combineShapesContinuousDiscrete =
(op: ExpressionTypes.algebraicOperation, s1: DistTypes.xyShape, s2: DistTypes.xyShape) (op: ExpressionTypes.algebraicOperation, continuousShape: DistTypes.xyShape, discreteShape: DistTypes.xyShape)
: array(DistTypes.xyShape) => { : DistTypes.xyShape => {
let t1n = s1 |> XYShape.T.length; let t1n = continuousShape |> XYShape.T.length;
let t2n = s2 |> XYShape.T.length; let t2n = discreteShape |> XYShape.T.length;
// each x pair is added/subtracted // each x pair is added/subtracted
let fn = Operation.Algebraic.toFn(op); let fn = Operation.Algebraic.toFn(op);
@ -262,46 +254,45 @@ let combineShapesContinuousDiscrete =
| `Add | `Add
| `Subtract => { | `Subtract => {
for (j in 0 to t2n - 1) { for (j in 0 to t2n - 1) {
// for each one of the discrete points // creates a new continuous shape for each one of the discrete points, and collects them in outXYShapes.
// create a new distribution, as long as the original continuous one
let dxyShape: array((float, float)) = let dxyShape: array((float, float)) =
Belt.Array.makeUninitializedUnsafe(t1n); Belt.Array.makeUninitializedUnsafe(t1n);
for (i in 0 to t1n - 1) { for (i in 0 to t1n - 1) {
let _ =
Belt.Array.set( Belt.Array.set(
dxyShape, dxyShape,
i, i,
(fn(s1.xs[i], s2.xs[j]), s1.ys[i] *. s2.ys[j]), (fn(continuousShape.xs[i], discreteShape.xs[j]), continuousShape.ys[i] *. discreteShape.ys[j]),
); ) |> ignore;
(); ();
}; };
let _ = Belt.Array.set(outXYShapes, j, dxyShape); Belt.Array.set(outXYShapes, j, dxyShape) |> ignore;
(); ();
} }
} }
| `Multiply | `Multiply
| `Divide => { | `Divide => {
for (j in 0 to t2n - 1) { for (j in 0 to t2n - 1) {
// for each one of the discrete points // creates a new continuous shape for each one of the discrete points, and collects them in outXYShapes.
// create a new distribution, as long as the original continuous one
let dxyShape: array((float, float)) = let dxyShape: array((float, float)) =
Belt.Array.makeUninitializedUnsafe(t1n); Belt.Array.makeUninitializedUnsafe(t1n);
for (i in 0 to t1n - 1) { for (i in 0 to t1n - 1) {
let _ =
Belt.Array.set( Belt.Array.set(
dxyShape, dxyShape,
i, i,
(fn(s1.xs[i], s2.xs[j]), s1.ys[i] *. s2.ys[j] /. s2.xs[j]), (fn(continuousShape.xs[i], discreteShape.xs[j]), continuousShape.ys[i] *. discreteShape.ys[j] /. discreteShape.xs[j]),
); ) |> ignore;
(); ();
}; };
let _ = Belt.Array.set(outXYShapes, j, dxyShape); Belt.Array.set(outXYShapes, j, dxyShape) |> ignore;
(); ();
} }
} }
}; };
outXYShapes outXYShapes
|> E.A.fmap(XYShape.T.fromZippedArray); |> E.A.fmap(XYShape.T.fromZippedArray)
|> E.A.fold_left(
XYShape.PointwiseCombination.combine((+.),
XYShape.XtoY.continuousInterpolator(`Linear, `UseZero)),
XYShape.T.empty);
}; };

View File

@ -3,7 +3,7 @@ open Distributions;
type t = DistTypes.continuousShape; type t = DistTypes.continuousShape;
let getShape = (t: t) => t.xyShape; let getShape = (t: t) => t.xyShape;
let interpolation = (t: t) => t.interpolation; let interpolation = (t: t) => t.interpolation;
let make = (interpolation, xyShape, integralSumCache, integralCache): t => { let make = (~interpolation=`Linear, ~integralSumCache=None, ~integralCache=None, xyShape): t => {
xyShape, xyShape,
interpolation, interpolation,
integralSumCache, integralSumCache,
@ -19,7 +19,7 @@ let lastY = (t: t) => t |> getShape |> XYShape.T.lastY;
let oShapeMap = let oShapeMap =
(fn, {xyShape, interpolation, integralSumCache, integralCache}: t) (fn, {xyShape, interpolation, integralSumCache, integralCache}: t)
: option(DistTypes.continuousShape) => : option(DistTypes.continuousShape) =>
fn(xyShape) |> E.O.fmap(make(interpolation, _, integralSumCache, integralCache)); fn(xyShape) |> E.O.fmap(make(~interpolation, ~integralSumCache, ~integralCache));
let emptyIntegral: DistTypes.continuousShape = { let emptyIntegral: DistTypes.continuousShape = {
xyShape: {xs: [|neg_infinity|], ys: [|0.0|]}, xyShape: {xs: [|neg_infinity|], ys: [|0.0|]},
@ -35,7 +35,7 @@ let empty: DistTypes.continuousShape = {
}; };
let stepwiseToLinear = (t: t): t => let stepwiseToLinear = (t: t): t =>
make(`Linear, XYShape.Range.stepwiseToLinear(t.xyShape), t.integralSumCache, t.integralCache); make(~integralSumCache=t.integralSumCache, ~integralCache=t.integralCache, XYShape.Range.stepwiseToLinear(t.xyShape));
let combinePointwise = let combinePointwise =
( (
@ -71,16 +71,13 @@ let combinePointwise =
let interpolator = XYShape.XtoY.continuousInterpolator(t1.interpolation, extrapolation); let interpolator = XYShape.XtoY.continuousInterpolator(t1.interpolation, extrapolation);
make( make(
`Linear, ~integralSumCache=combinedIntegralSum,
XYShape.PointwiseCombination.combine( XYShape.PointwiseCombination.combine(
(+.), (+.),
interpolator, interpolator,
interpolator,
t1.xyShape, t1.xyShape,
t2.xyShape, t2.xyShape,
), ),
combinedIntegralSum,
None,
); );
}; };
@ -89,7 +86,7 @@ let toLinear = (t: t): option(t) => {
| {interpolation: `Stepwise, xyShape, integralSumCache, integralCache} => | {interpolation: `Stepwise, xyShape, integralSumCache, integralCache} =>
xyShape xyShape
|> XYShape.Range.stepsToContinuous |> XYShape.Range.stepsToContinuous
|> E.O.fmap(make(`Linear, _, integralSumCache, integralCache)) |> E.O.fmap(make(~integralSumCache, ~integralCache))
| {interpolation: `Linear} => Some(t) | {interpolation: `Linear} => Some(t)
}; };
}; };
@ -117,13 +114,13 @@ let reduce =
let mapY = (~integralSumCacheFn=_ => None, let mapY = (~integralSumCacheFn=_ => None,
~integralCacheFn=_ => None, ~integralCacheFn=_ => None,
fn, t: t) => { ~fn, t: t) => {
let yMapFn = shapeMap(XYShape.T.mapY(fn)); make(
~interpolation=t.interpolation,
t ~integralSumCache=t.integralSumCache |> E.O.bind(_, integralSumCacheFn),
|> yMapFn ~integralCache=t.integralCache |> E.O.bind(_, integralCacheFn),
|> updateIntegralSumCache(E.O.bind(t.integralSumCache, integralSumCacheFn)) t |> getShape |> XYShape.T.mapY(fn),
|> updateIntegralCache(E.O.bind(t.integralCache, integralCacheFn)); );
}; };
let rec scaleBy = (~scale=1.0, t: t): t => { let rec scaleBy = (~scale=1.0, t: t): t => {
@ -131,7 +128,7 @@ let rec scaleBy = (~scale=1.0, t: t): t => {
let scaledIntegralCache = E.O.bind(t.integralCache, v => Some(scaleBy(~scale, v))); let scaledIntegralCache = E.O.bind(t.integralCache, v => Some(scaleBy(~scale, v)));
t t
|> mapY((r: float) => r *. scale) |> mapY(~fn=(r: float) => r *. scale)
|> updateIntegralSumCache(scaledIntegralSumCache) |> updateIntegralSumCache(scaledIntegralSumCache)
|> updateIntegralCache(scaledIntegralCache) |> updateIntegralCache(scaledIntegralCache)
}; };
@ -177,24 +174,20 @@ module T =
let truncatedShape = let truncatedShape =
XYShape.T.fromZippedArray(truncatedZippedPairsWithNewPoints); XYShape.T.fromZippedArray(truncatedZippedPairsWithNewPoints);
make(`Linear, truncatedShape, None, None); make(truncatedShape)
}; };
// TODO: This should work with stepwise plots. // TODO: This should work with stepwise plots.
let integral = (t) => { let integral = (t) =>
if (t |> getShape |> XYShape.T.isEmpty) { switch (getShape(t) |> XYShape.T.isEmpty, t.integralCache) {
make(`Linear, {xs: [|neg_infinity|], ys: [|0.0|]}, None, None); | (true, _) => emptyIntegral
} else { | (false, Some(cache)) => cache
switch (t.integralCache) { | (false, None) =>
| Some(cache) => cache
| None =>
t t
|> getShape |> getShape
|> XYShape.Range.integrateWithTriangles |> XYShape.Range.integrateWithTriangles
|> E.O.toExt("This should not have happened") |> E.O.toExt("This should not have happened")
|> make(`Linear, _, None, None) |> make
};
};
}; };
let downsample = (length, t): t => let downsample = (length, t): t =>
@ -249,23 +242,17 @@ let combineAlgebraicallyWithDiscrete =
t2: DistTypes.discreteShape, t2: DistTypes.discreteShape,
) => { ) => {
let t1s = t1 |> getShape; let t1s = t1 |> getShape;
let t2s = t2.xyShape; // would like to use Discrete.getShape here, but current file structure doesn't allow for that let t2s = t2.xyShape; // TODO would like to use Discrete.getShape here, but current file structure doesn't allow for that
if (XYShape.T.isEmpty(t1s) || XYShape.T.isEmpty(t2s)) { if (XYShape.T.isEmpty(t1s) || XYShape.T.isEmpty(t2s)) {
empty; empty;
} else { } else {
let shapeArray = AlgebraicShapeCombination.combineShapesContinuousDiscrete(op, t1s, t2s); let continuousAsLinear = switch (t1.interpolation) {
| `Linear => t1;
| `Stepwise => stepwiseToLinear(t1)
};
let t1Interpolator = XYShape.XtoY.continuousInterpolator(t1.interpolation, `UseZero); let combinedShape = AlgebraicShapeCombination.combineShapesContinuousDiscrete(op, continuousAsLinear |> getShape, t2s);
let t2Interpolator = XYShape.XtoY.discreteInterpolator;
let combinedShape =
shapeArray
|> E.A.fold_left(
XYShape.PointwiseCombination.combine((+.),
t1Interpolator,
t2Interpolator),
XYShape.T.empty);
let combinedIntegralSum = let combinedIntegralSum =
Common.combineIntegralSums( Common.combineIntegralSums(
@ -275,7 +262,7 @@ let combineAlgebraicallyWithDiscrete =
); );
// TODO: It could make sense to automatically transform the integrals here (shift or scale) // TODO: It could make sense to automatically transform the integrals here (shift or scale)
make(t1.interpolation, combinedShape, combinedIntegralSum, None) make(~interpolation=t1.interpolation, ~integralSumCache=combinedIntegralSum, combinedShape)
}; };
}; };
@ -297,6 +284,6 @@ let combineAlgebraically =
t2.integralSumCache, t2.integralSumCache,
); );
// return a new Continuous distribution // return a new Continuous distribution
make(`Linear, combinedShape, combinedIntegralSum, None); make(~integralSumCache=combinedIntegralSum, combinedShape);
}; };
}; };

View File

@ -2,7 +2,7 @@ open Distributions;
type t = DistTypes.discreteShape; type t = DistTypes.discreteShape;
let make = (xyShape, integralSumCache, integralCache): t => {xyShape, integralSumCache, integralCache}; let make = (~integralSumCache=None, ~integralCache=None, xyShape): t => {xyShape, integralSumCache, integralCache};
let shapeMap = (fn, {xyShape, integralSumCache, integralCache}: t): t => { let shapeMap = (fn, {xyShape, integralSumCache, integralCache}: t): t => {
xyShape: fn(xyShape), xyShape: fn(xyShape),
integralSumCache, integralSumCache,
@ -10,9 +10,21 @@ let shapeMap = (fn, {xyShape, integralSumCache, integralCache}: t): t => {
}; };
let getShape = (t: t) => t.xyShape; let getShape = (t: t) => t.xyShape;
let oShapeMap = (fn, {xyShape, integralSumCache, integralCache}: t): option(t) => let oShapeMap = (fn, {xyShape, integralSumCache, integralCache}: t): option(t) =>
fn(xyShape) |> E.O.fmap(make(_, integralSumCache, integralCache)); fn(xyShape) |> E.O.fmap(make(~integralSumCache, ~integralCache));
let emptyIntegral: DistTypes.continuousShape = {
xyShape: {xs: [|neg_infinity|], ys: [|0.0|]},
interpolation: `Stepwise,
integralSumCache: Some(0.0),
integralCache: None,
};
let empty: DistTypes.discreteShape = {
xyShape: XYShape.T.empty,
integralSumCache: Some(0.0),
integralCache: Some(emptyIntegral),
};
let empty: t = {xyShape: XYShape.T.empty, integralSumCache: Some(0.0), integralCache: None};
let shapeFn = (fn, t: t) => t |> getShape |> fn; let shapeFn = (fn, t: t) => t |> getShape |> fn;
let lastY = (t: t) => t |> getShape |> XYShape.T.lastY; let lastY = (t: t) => t |> getShape |> XYShape.T.lastY;
@ -37,15 +49,13 @@ let combinePointwise =
// It could be done for pointwise additions, but is that ever needed? // It could be done for pointwise additions, but is that ever needed?
make( make(
~integralSumCache=combinedIntegralSum,
XYShape.PointwiseCombination.combine( XYShape.PointwiseCombination.combine(
(+.), (+.),
XYShape.XtoY.discreteInterpolator, XYShape.XtoY.discreteInterpolator,
XYShape.XtoY.discreteInterpolator,
t1.xyShape, t1.xyShape,
t2.xyShape, t2.xyShape,
), ),
combinedIntegralSum,
None,
); );
}; };
@ -100,27 +110,26 @@ let combineAlgebraically =
let combinedShape = XYShape.T.fromZippedArray(rxys); let combinedShape = XYShape.T.fromZippedArray(rxys);
make(combinedShape, combinedIntegralSum, None); make(~integralSumCache=combinedIntegralSum, combinedShape);
}; };
let mapY = (~integralSumCacheFn=_ => None, let mapY = (~integralSumCacheFn=_ => None,
~integralCacheFn=_ => None, ~integralCacheFn=_ => None,
fn, t: t) => { ~fn, t: t) => {
let yMapFn = shapeMap(XYShape.T.mapY(fn)); make(
~integralSumCache=t.integralSumCache |> E.O.bind(_, integralSumCacheFn),
t ~integralCache=t.integralCache |> E.O.bind(_, integralCacheFn),
|> yMapFn t |> getShape |> XYShape.T.mapY(fn),
|> updateIntegralSumCache(E.O.bind(t.integralSumCache, integralSumCacheFn)) );
|> updateIntegralCache(E.O.bind(t.integralCache, integralCacheFn));
}; };
let scaleBy = (~scale=1.0, t: t): t => { let scaleBy = (~scale=1.0, t: t): t => {
let scaledIntegralSumCache = E.O.bind(t.integralSumCache, v => Some(scale *. v)); let scaledIntegralSumCache = t.integralSumCache |> E.O.fmap((*.)(scale));
let scaledIntegralCache = E.O.bind(t.integralCache, v => Some(Continuous.scaleBy(~scale, v))); let scaledIntegralCache = t.integralCache |> E.O.fmap(Continuous.scaleBy(~scale));
t t
|> mapY((r: float) => r *. scale) |> mapY(~fn=(r: float) => r *. scale)
|> updateIntegralSumCache(scaledIntegralSumCache) |> updateIntegralSumCache(scaledIntegralSumCache)
|> updateIntegralCache(scaledIntegralCache) |> updateIntegralCache(scaledIntegralCache)
}; };
@ -130,17 +139,10 @@ module T =
type t = DistTypes.discreteShape; type t = DistTypes.discreteShape;
type integral = DistTypes.continuousShape; type integral = DistTypes.continuousShape;
let integral = (t) => let integral = (t) =>
if (t |> getShape |> XYShape.T.isEmpty) { switch (getShape(t) |> XYShape.T.isEmpty, t.integralCache) {
Continuous.make( | (true, _) => emptyIntegral
`Stepwise, | (false, Some(c)) => c
{xs: [|neg_infinity|], ys: [|0.0|]}, | (false, None) => {
None,
None,
);
} else {
switch (t.integralCache) {
| Some(c) => c
| None => {
let ts = getShape(t); let ts = getShape(t);
// The first xy of this integral should always be the zero, to ensure nice plotting // The first xy of this integral should always be the zero, to ensure nice plotting
let firstX = ts |> XYShape.T.minX; let firstX = ts |> XYShape.T.minX;
@ -150,10 +152,9 @@ module T =
|> XYShape.T.concat(prependedZeroPoint) |> XYShape.T.concat(prependedZeroPoint)
|> XYShape.T.accumulateYs((+.)); |> XYShape.T.accumulateYs((+.));
Continuous.make(`Stepwise, integralShape, None, None); Continuous.make(~interpolation=`Stepwise, integralShape);
} }
}; };
};
let integralEndY = (t: t) => let integralEndY = (t: t) =>
t.integralSumCache t.integralSumCache
@ -179,7 +180,6 @@ module T =
let currentLength = t |> getShape |> XYShape.T.length; let currentLength = t |> getShape |> XYShape.T.length;
if (i < currentLength && i >= 1 && currentLength > 1) { if (i < currentLength && i >= 1 && currentLength > 1) {
let clippedShape =
t t
|> getShape |> getShape
|> XYShape.T.zip |> XYShape.T.zip
@ -187,9 +187,8 @@ module T =
|> Belt.Array.reverse |> Belt.Array.reverse
|> Belt.Array.slice(_, ~offset=0, ~len=i) |> Belt.Array.slice(_, ~offset=0, ~len=i)
|> XYShape.Zipped.sortByX |> XYShape.Zipped.sortByX
|> XYShape.T.fromZippedArray; |> XYShape.T.fromZippedArray
|> make;
make(clippedShape, None, None); // if someone needs the sum, they'll have to recompute it
} else { } else {
t; t;
}; };
@ -197,7 +196,6 @@ module T =
let truncate = let truncate =
(leftCutoff: option(float), rightCutoff: option(float), t: t): t => { (leftCutoff: option(float), rightCutoff: option(float), t: t): t => {
let truncatedShape =
t t
|> getShape |> getShape
|> XYShape.T.zip |> XYShape.T.zip
@ -205,9 +203,8 @@ module T =
x >= E.O.default(neg_infinity, leftCutoff) x >= E.O.default(neg_infinity, leftCutoff)
&& x <= E.O.default(infinity, rightCutoff) && x <= E.O.default(infinity, rightCutoff)
) )
|> XYShape.T.fromZippedArray; |> XYShape.T.fromZippedArray
|> make;
make(truncatedShape, None, None);
}; };
let xToY = (f, t) => let xToY = (f, t) =>

View File

@ -94,11 +94,11 @@ module T =
( (
~integralSumCacheFn=previousIntegralSum => None, ~integralSumCacheFn=previousIntegralSum => None,
~integralCacheFn=previousIntegralCache => None, ~integralCacheFn=previousIntegralCache => None,
fn, ~fn,
{shape, _} as t: t, {shape, _} as t: t,
) )
: t => : t =>
Shape.T.mapY(~integralSumCacheFn, fn, shape) Shape.T.mapY(~integralSumCacheFn, ~fn, shape)
|> updateShape(_, t); |> updateShape(_, t);
// get the total of everything // get the total of everything

View File

@ -14,11 +14,11 @@ type xyShape = {
ys: array(float), ys: array(float),
}; };
type interpolation = [ type interpolationStrategy = [
| `Stepwise | `Stepwise
| `Linear | `Linear
]; ];
type extrapolation = [ type extrapolationStrategy = [
| `UseZero | `UseZero
| `UseOutermostPoints | `UseOutermostPoints
]; ];
@ -27,14 +27,13 @@ type interpolator = (xyShape, int, float) => float;
type continuousShape = { type continuousShape = {
xyShape, xyShape,
interpolation: interpolation, interpolation: interpolationStrategy,
integralSumCache: option(float), integralSumCache: option(float),
integralCache: option(continuousShape), integralCache: option(continuousShape),
}; };
type discreteShape = { type discreteShape = {
xyShape, xyShape,
/* interpolation is always `Discrete */
integralSumCache: option(float), integralSumCache: option(float),
integralCache: option(continuousShape), integralCache: option(continuousShape),
}; };

View File

@ -4,7 +4,7 @@ module type dist = {
let minX: t => float; let minX: t => float;
let maxX: t => float; let maxX: t => float;
let mapY: let mapY:
(~integralSumCacheFn: float => option(float)=?, ~integralCacheFn: DistTypes.continuousShape => option(DistTypes.continuousShape)=?, float => float, t) => t; (~integralSumCacheFn: float => option(float)=?, ~integralCacheFn: DistTypes.continuousShape => option(DistTypes.continuousShape)=?, ~fn: float => float, t) => t;
let xToY: (float, t) => DistTypes.mixedPoint; let xToY: (float, t) => DistTypes.mixedPoint;
let toShape: t => DistTypes.shape; let toShape: t => DistTypes.shape;
let toContinuous: t => option(DistTypes.continuousShape); let toContinuous: t => option(DistTypes.continuousShape);

View File

@ -1,7 +1,7 @@
open Distributions; open Distributions;
type t = DistTypes.mixedShape; type t = DistTypes.mixedShape;
let make = (~continuous, ~discrete, integralSumCache, integralCache): t => {continuous, discrete, integralSumCache, integralCache}; let make = (~integralSumCache=None, ~integralCache=None, ~continuous, ~discrete): t => {continuous, discrete, integralSumCache, integralCache};
let totalLength = (t: t): int => { let totalLength = (t: t): int => {
let continuousLength = let continuousLength =
@ -16,7 +16,7 @@ let scaleBy = (~scale=1.0, t: t): t => {
let scaledContinuous = Continuous.scaleBy(~scale, t.continuous); let scaledContinuous = Continuous.scaleBy(~scale, t.continuous);
let scaledIntegralCache = E.O.bind(t.integralCache, v => Some(Continuous.scaleBy(~scale, v))); let scaledIntegralCache = E.O.bind(t.integralCache, v => Some(Continuous.scaleBy(~scale, v)));
let scaledIntegralSumCache = E.O.bind(t.integralSumCache, s => Some(s *. scale)); let scaledIntegralSumCache = E.O.bind(t.integralSumCache, s => Some(s *. scale));
make(~discrete=scaledDiscrete, ~continuous=scaledContinuous, scaledIntegralSumCache, scaledIntegralCache); make(~discrete=scaledDiscrete, ~continuous=scaledContinuous, ~integralSumCache=scaledIntegralSumCache, ~integralCache=scaledIntegralCache);
}; };
let toContinuous = ({continuous}: t) => Some(continuous); let toContinuous = ({continuous}: t) => Some(continuous);
@ -54,7 +54,7 @@ module T =
let truncatedDiscrete = let truncatedDiscrete =
Discrete.T.truncate(leftCutoff, rightCutoff, discrete); Discrete.T.truncate(leftCutoff, rightCutoff, discrete);
make(~discrete=truncatedDiscrete, ~continuous=truncatedContinuous, None, None); make(~integralSumCache=None, ~integralCache=None, ~discrete=truncatedDiscrete, ~continuous=truncatedContinuous);
}; };
let normalize = (t: t): t => { let normalize = (t: t): t => {
@ -82,7 +82,7 @@ module T =
|> Discrete.scaleBy(~scale=newDiscreteSum /. discreteIntegralSum) |> Discrete.scaleBy(~scale=newDiscreteSum /. discreteIntegralSum)
|> Discrete.updateIntegralSumCache(Some(newDiscreteSum)); |> Discrete.updateIntegralSumCache(Some(newDiscreteSum));
make(~continuous=normalizedContinuous, ~discrete=normalizedDiscrete, Some(1.0), None); make(~integralSumCache=Some(1.0), ~integralCache=None, ~continuous=normalizedContinuous, ~discrete=normalizedDiscrete);
}; };
let xToY = (x, t: t) => { let xToY = (x, t: t) => {
@ -144,16 +144,12 @@ module T =
let discreteIntegral = Continuous.stepwiseToLinear(Discrete.T.Integral.get(t.discrete)); let discreteIntegral = Continuous.stepwiseToLinear(Discrete.T.Integral.get(t.discrete));
Continuous.make( Continuous.make(
`Linear,
XYShape.PointwiseCombination.combine( XYShape.PointwiseCombination.combine(
(+.), (+.),
XYShape.XtoY.continuousInterpolator(`Linear, `UseOutermostPoints), XYShape.XtoY.continuousInterpolator(`Linear, `UseOutermostPoints),
XYShape.XtoY.continuousInterpolator(`Linear, `UseOutermostPoints),
Continuous.getShape(continuousIntegral), Continuous.getShape(continuousIntegral),
Continuous.getShape(discreteIntegral), Continuous.getShape(discreteIntegral),
), ),
None,
None,
); );
}; };
}; };
@ -177,19 +173,19 @@ module T =
( (
~integralSumCacheFn=previousIntegralSum => None, ~integralSumCacheFn=previousIntegralSum => None,
~integralCacheFn=previousIntegral => None, ~integralCacheFn=previousIntegral => None,
fn, ~fn,
t: t, t: t,
) )
: t => { : t => {
let yMappedDiscrete: DistTypes.discreteShape = let yMappedDiscrete: DistTypes.discreteShape =
t.discrete t.discrete
|> Discrete.T.mapY(fn) |> Discrete.T.mapY(~fn)
|> Discrete.updateIntegralSumCache(E.O.bind(t.discrete.integralSumCache, integralSumCacheFn)) |> Discrete.updateIntegralSumCache(E.O.bind(t.discrete.integralSumCache, integralSumCacheFn))
|> Discrete.updateIntegralCache(E.O.bind(t.discrete.integralCache, integralCacheFn)); |> Discrete.updateIntegralCache(E.O.bind(t.discrete.integralCache, integralCacheFn));
let yMappedContinuous: DistTypes.continuousShape = let yMappedContinuous: DistTypes.continuousShape =
t.continuous t.continuous
|> Continuous.T.mapY(fn) |> Continuous.T.mapY(~fn)
|> Continuous.updateIntegralSumCache(E.O.bind(t.continuous.integralSumCache, integralSumCacheFn)) |> Continuous.updateIntegralSumCache(E.O.bind(t.continuous.integralSumCache, integralSumCacheFn))
|> Continuous.updateIntegralCache(E.O.bind(t.continuous.integralCache, integralCacheFn)); |> Continuous.updateIntegralCache(E.O.bind(t.continuous.integralCache, integralCacheFn));
@ -332,5 +328,5 @@ let combinePointwise = (~integralSumCachesFn = (_, _) => None, ~integralCachesFn
t2.integralCache, t2.integralCache,
); );
make(~discrete=reducedDiscrete, ~continuous=reducedContinuous, combinedIntegralSum, combinedIntegral); make(~integralSumCache=combinedIntegralSum, ~integralCache=combinedIntegral, ~discrete=reducedDiscrete, ~continuous=reducedContinuous);
}; };

View File

@ -9,8 +9,8 @@ type assumptions = {
}; };
let buildSimple = (~continuous: option(DistTypes.continuousShape), ~discrete: option(DistTypes.discreteShape)): option(DistTypes.shape) => { let buildSimple = (~continuous: option(DistTypes.continuousShape), ~discrete: option(DistTypes.discreteShape)): option(DistTypes.shape) => {
let continuous = continuous |> E.O.default(Continuous.make(`Linear, {xs: [||], ys: [||]}, Some(0.0), None)); let continuous = continuous |> E.O.default(Continuous.make(~integralSumCache=Some(0.0), {xs: [||], ys: [||]}));
let discrete = discrete |> E.O.default(Discrete.make({xs: [||], ys: [||]}, Some(0.0), None)); let discrete = discrete |> E.O.default(Discrete.make(~integralSumCache=Some(0.0), {xs: [||], ys: [||]}));
let cLength = let cLength =
continuous continuous
|> Continuous.getShape |> Continuous.getShape
@ -24,10 +24,10 @@ let buildSimple = (~continuous: option(DistTypes.continuousShape), ~discrete: op
| (_, _) => | (_, _) =>
let mixedDist = let mixedDist =
Mixed.make( Mixed.make(
~integralSumCache=None,
~integralCache=None,
~continuous, ~continuous,
~discrete, ~discrete,
None,
None,
); );
Some(Mixed(mixedDist)); Some(Mixed(mixedDist));
}; };

View File

@ -19,8 +19,8 @@ let fmap = ((fn1, fn2, fn3), t: t): t =>
let toMixed = let toMixed =
mapToAll(( mapToAll((
m => m, m => m,
d => Mixed.make(~discrete=d, ~continuous=Continuous.empty, d.integralSumCache, d.integralCache), d => Mixed.make(~integralSumCache=d.integralSumCache, ~integralCache=d.integralCache, ~discrete=d, ~continuous=Continuous.empty),
c => Mixed.make(~discrete=Discrete.empty, ~continuous=c, c.integralSumCache, c.integralCache), c => Mixed.make(~integralSumCache=c.integralSumCache, ~integralCache=c.integralCache, ~discrete=Discrete.empty, ~continuous=c),
)); ));
let combineAlgebraically = let combineAlgebraically =
@ -176,11 +176,11 @@ module T =
)); ));
}; };
let maxX = mapToAll((Mixed.T.maxX, Discrete.T.maxX, Continuous.T.maxX)); let maxX = mapToAll((Mixed.T.maxX, Discrete.T.maxX, Continuous.T.maxX));
let mapY = (~integralSumCacheFn=previousIntegralSum => None, ~integralCacheFn=previousIntegral=>None, fn) => let mapY = (~integralSumCacheFn=previousIntegralSum => None, ~integralCacheFn=previousIntegral=>None, ~fn) =>
fmap(( fmap((
Mixed.T.mapY(~integralSumCacheFn, ~integralCacheFn, fn), Mixed.T.mapY(~integralSumCacheFn, ~integralCacheFn, ~fn),
Discrete.T.mapY(~integralSumCacheFn, ~integralCacheFn, fn), Discrete.T.mapY(~integralSumCacheFn, ~integralCacheFn, ~fn),
Continuous.T.mapY(~integralSumCacheFn, ~integralCacheFn, fn), Continuous.T.mapY(~integralSumCacheFn, ~integralCacheFn, ~fn),
)); ));
let mean = (t: t): float => let mean = (t: t): float =>

View File

@ -146,10 +146,9 @@ module XtoY = {
/* Returns a between-points-interpolating function that can be used with PointwiseCombination.combine. /* Returns a between-points-interpolating function that can be used with PointwiseCombination.combine.
Interpolation can either be stepwise (using the value on the left) or linear. Extrapolation can be `UseZero or `UseOutermostPoints. */ Interpolation can either be stepwise (using the value on the left) or linear. Extrapolation can be `UseZero or `UseOutermostPoints. */
let continuousInterpolator = (interpolation: DistTypes.interpolation, extrapolation: DistTypes.extrapolation): interpolator => { let continuousInterpolator = (interpolation: DistTypes.interpolationStrategy, extrapolation: DistTypes.extrapolationStrategy): interpolator => {
switch (interpolation) { switch (interpolation, extrapolation) {
| `Linear => switch (extrapolation) { | (`Linear, `UseZero) => (t: T.t, leftIndex: int, x: float) => {
| `UseZero => (t: T.t, leftIndex: int, x: float) => {
if (leftIndex < 0) { if (leftIndex < 0) {
0.0 0.0
} else if (leftIndex >= T.length(t) - 1) { } else if (leftIndex >= T.length(t) - 1) {
@ -163,11 +162,11 @@ module XtoY = {
y1 *. (1. -. fraction) +. y2 *. fraction; y1 *. (1. -. fraction) +. y2 *. fraction;
}; };
} }
| `UseOutermostPoints => (t: T.t, leftIndex: int, x: float) => { | (`Linear, `UseOutermostPoints) => (t: T.t, leftIndex: int, x: float) => {
if (leftIndex < 0) { if (leftIndex < 0) {
t.ys[0]; t.ys[0];
} else if (leftIndex >= T.length(t) - 1) { } else if (leftIndex >= T.length(t) - 1) {
T.lastY(t); t.ys[T.length(t) - 1]
} else { } else {
let x1 = t.xs[leftIndex]; let x1 = t.xs[leftIndex];
let x2 = t.xs[leftIndex + 1]; let x2 = t.xs[leftIndex + 1];
@ -177,9 +176,7 @@ module XtoY = {
y1 *. (1. -. fraction) +. y2 *. fraction; y1 *. (1. -. fraction) +. y2 *. fraction;
}; };
} }
} | (`Stepwise, `UseZero) => (t: T.t, leftIndex: int, x: float) => {
| `Stepwise => switch (extrapolation) {
| `UseZero => (t: T.t, leftIndex: int, x: float) => {
if (leftIndex < 0) { if (leftIndex < 0) {
0.0 0.0
} else if (leftIndex >= T.length(t) - 1) { } else if (leftIndex >= T.length(t) - 1) {
@ -188,17 +185,16 @@ module XtoY = {
t.ys[leftIndex]; t.ys[leftIndex];
} }
} }
| `UseOutermostPoints => (t: T.t, leftIndex: int, x: float) => { | (`Stepwise, `UseOutermostPoints) => (t: T.t, leftIndex: int, x: float) => {
if (leftIndex < 0) { if (leftIndex < 0) {
t.ys[0]; t.ys[0];
} else if (leftIndex >= T.length(t) - 1) { } else if (leftIndex >= T.length(t) - 1) {
T.lastY(t); t.ys[T.length(t) - 1]
} else { } else {
t.ys[leftIndex]; t.ys[leftIndex];
} }
} }
} }
}
}; };
/* Returns a between-points-interpolating function that can be used with PointwiseCombination.combine. /* Returns a between-points-interpolating function that can be used with PointwiseCombination.combine.
@ -246,7 +242,7 @@ module PointwiseCombination = {
// and interpolates the value on the other side, thus accumulating xs and ys. // and interpolates the value on the other side, thus accumulating xs and ys.
// 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. // 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, t1Interpolator, t2Interpolator, t1, t2) { function(fn, interpolator, t1, t2) {
let t1n = t1.xs.length; let t1n = t1.xs.length;
let t2n = t2.xs.length; let t2n = t2.xs.length;
let outX = []; let outX = [];
@ -263,7 +259,7 @@ module PointwiseCombination = {
x = t1.xs[i]; x = t1.xs[i];
ya = t1.ys[i]; ya = t1.ys[i];
yb = t2Interpolator(t2, j, x); yb = interpolator(t2, j, x);
} else if (i == t1n - 1 && j < t2n - 1 || } 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 t1.xs[i+1] > t2.xs[j+1]) { // if b has to catch up to a, or if a is already done
j++; j++;
@ -271,7 +267,7 @@ module PointwiseCombination = {
x = t2.xs[j]; x = t2.xs[j];
yb = t2.ys[j]; yb = t2.ys[j];
ya = t1Interpolator(t1, i, x); ya = interpolator(t1, i, x);
} else if (i < t1n - 1 && j < t2n && t1.xs[i+1] === t2.xs[j+1]) { // if they happen to be equal, move both ahead } 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++; i++;
j++; j++;
@ -384,16 +380,16 @@ module Range = {
let newXs: array(float) = Belt.Array.makeUninitializedUnsafe(2 * length); let newXs: array(float) = Belt.Array.makeUninitializedUnsafe(2 * length);
let newYs: array(float) = Belt.Array.makeUninitializedUnsafe(2 * length); let newYs: array(float) = Belt.Array.makeUninitializedUnsafe(2 * length);
let _ = Belt.Array.set(newXs, 0, xs[0] -. epsilon_float); Belt.Array.set(newXs, 0, xs[0] -. epsilon_float) |> ignore;
let _ = Belt.Array.set(newYs, 0, 0.); Belt.Array.set(newYs, 0, 0.) |> ignore;
let _ = Belt.Array.set(newXs, 1, xs[0]); Belt.Array.set(newXs, 1, xs[0]) |> ignore;
let _ = Belt.Array.set(newYs, 1, ys[0]); Belt.Array.set(newYs, 1, ys[0]) |> ignore;
for (i in 1 to E.A.length(xs) - 1) { for (i in 1 to E.A.length(xs) - 1) {
let _ = Belt.Array.set(newXs, i * 2, xs[i] -. epsilon_float); Belt.Array.set(newXs, i * 2, xs[i] -. epsilon_float) |> ignore;
let _ = Belt.Array.set(newYs, i * 2, ys[i-1]); Belt.Array.set(newYs, i * 2, ys[i-1]) |> ignore;
let _ = Belt.Array.set(newXs, i * 2 + 1, xs[i]); Belt.Array.set(newXs, i * 2 + 1, xs[i]) |> ignore;
let _ = Belt.Array.set(newYs, i * 2 + 1, ys[i]); Belt.Array.set(newYs, i * 2 + 1, ys[i]) |> ignore;
(); ();
}; };

View File

@ -112,7 +112,7 @@ module VerticalScaling = {
Shape.T.mapY( Shape.T.mapY(
~integralSumCacheFn=integralSumCacheFn(sm), ~integralSumCacheFn=integralSumCacheFn(sm),
~integralCacheFn=integralCacheFn(sm), ~integralCacheFn=integralCacheFn(sm),
fn(sm), ~fn=fn(sm),
rs, rs,
), ),
), ),

View File

@ -120,7 +120,7 @@ module T = {
|> E.FloatFloatMap.fmap(r => r /. length) |> E.FloatFloatMap.fmap(r => r /. length)
|> E.FloatFloatMap.toArray |> E.FloatFloatMap.toArray
|> XYShape.T.fromZippedArray |> XYShape.T.fromZippedArray
|> Discrete.make(_, None, None); |> Discrete.make;
let pdf = let pdf =
continuousPart |> E.A.length > 5 continuousPart |> E.A.length > 5
@ -150,7 +150,7 @@ module T = {
~outputXYPoints=samplingInputs.outputXYPoints, ~outputXYPoints=samplingInputs.outputXYPoints,
formatUnitWidth(usedUnitWidth), formatUnitWidth(usedUnitWidth),
) )
|> Continuous.make(`Linear, _, None, None) |> Continuous.make
|> (r => Some((r, foo))); |> (r => Some((r, foo)));
} }
: None; : None;

View File

@ -317,13 +317,13 @@ module T = {
switch (d) { switch (d) {
| `Float(v) => | `Float(v) =>
Discrete( Discrete(
Discrete.make({xs: [|v|], ys: [|1.0|]}, Some(1.0), None), Discrete.make(~integralSumCache=Some(1.0), {xs: [|v|], ys: [|1.0|]}),
) )
| _ => | _ =>
let xs = interpolateXs(~xSelection=`ByWeight, d, sampleCount); let xs = interpolateXs(~xSelection=`ByWeight, d, sampleCount);
let ys = xs |> E.A.fmap(x => pdf(x, d)); let ys = xs |> E.A.fmap(x => pdf(x, d));
Continuous( Continuous(
Continuous.make(`Linear, {xs, ys}, Some(1.0), None), Continuous.make(~integralSumCache=Some(1.0), {xs, ys}),
); );
}; };
}; };