More XYShape cleanup
This commit is contained in:
parent
91734fdd32
commit
29d8e40693
|
@ -19,8 +19,8 @@ describe("Shape", () => {
|
|||
makeTest("minX", T.minX(continuous), Some(1.0));
|
||||
makeTest("maxX", T.maxX(continuous), Some(8.0));
|
||||
makeTest(
|
||||
"pointwiseFmap",
|
||||
T.pointwiseFmap(r => r *. 2.0, continuous) |> getShape |> (r => r.ys),
|
||||
"mapY",
|
||||
T.mapY(r => r *. 2.0, continuous) |> getShape |> (r => r.ys),
|
||||
[|16., 18.0, 4.0|],
|
||||
);
|
||||
describe("xToY", () => {
|
||||
|
@ -130,8 +130,8 @@ describe("Shape", () => {
|
|||
makeTest("minX", T.minX(discrete), Some(1.0));
|
||||
makeTest("maxX", T.maxX(discrete), Some(8.0));
|
||||
makeTest(
|
||||
"pointwiseFmap",
|
||||
T.pointwiseFmap(r => r *. 2.0, discrete) |> (r => r.ys),
|
||||
"mapY",
|
||||
T.mapY(r => r *. 2.0, discrete) |> (r => r.ys),
|
||||
[|0.6, 1.0, 0.4|],
|
||||
);
|
||||
makeTest(
|
||||
|
@ -213,8 +213,8 @@ describe("Shape", () => {
|
|||
makeTest("minX", T.minX(mixed), Some(1.0));
|
||||
makeTest("maxX", T.maxX(mixed), Some(14.0));
|
||||
makeTest(
|
||||
"pointwiseFmap",
|
||||
T.pointwiseFmap(r => r *. 2.0, mixed),
|
||||
"mapY",
|
||||
T.mapY(r => r *. 2.0, mixed),
|
||||
Distributions.Mixed.make(
|
||||
~continuous=
|
||||
Distributions.Continuous.make(
|
||||
|
|
|
@ -40,16 +40,16 @@ describe("XYShapes", () => {
|
|||
Some(210.3721280423322),
|
||||
);
|
||||
});
|
||||
describe("transverse", () => {
|
||||
makeTest(
|
||||
"When very different",
|
||||
XYShape.T.Transversal._transverse(
|
||||
(aCurrent, aLast) => aCurrent +. aLast,
|
||||
[|1.0, 2.0, 3.0, 4.0|],
|
||||
),
|
||||
[|1.0, 3.0, 6.0, 10.0|],
|
||||
)
|
||||
});
|
||||
// describe("transverse", () => {
|
||||
// makeTest(
|
||||
// "When very different",
|
||||
// XYShape.Transversal._transverse(
|
||||
// (aCurrent, aLast) => aCurrent +. aLast,
|
||||
// [|1.0, 2.0, 3.0, 4.0|],
|
||||
// ),
|
||||
// [|1.0, 3.0, 6.0, 10.0|],
|
||||
// )
|
||||
// });
|
||||
describe("integrateWithTriangles", () => {
|
||||
makeTest(
|
||||
"integrates correctly",
|
||||
|
|
|
@ -1,25 +1,9 @@
|
|||
let min = (f1: option(float), f2: option(float)) =>
|
||||
switch (f1, f2) {
|
||||
| (Some(f1), Some(f2)) => Some(f1 < f2 ? f1 : f2)
|
||||
| (Some(f1), None) => Some(f1)
|
||||
| (None, Some(f2)) => Some(f2)
|
||||
| (None, None) => None
|
||||
};
|
||||
|
||||
let max = (f1: option(float), f2: option(float)) =>
|
||||
switch (f1, f2) {
|
||||
| (Some(f1), Some(f2)) => Some(f1 > f2 ? f1 : f2)
|
||||
| (Some(f1), None) => Some(f1)
|
||||
| (None, Some(f2)) => Some(f2)
|
||||
| (None, None) => None
|
||||
};
|
||||
|
||||
module type dist = {
|
||||
type t;
|
||||
type integral;
|
||||
let minX: t => option(float);
|
||||
let maxX: t => option(float);
|
||||
let pointwiseFmap: (float => float, t) => t;
|
||||
let mapY: (float => float, t) => t;
|
||||
let xToY: (float, t) => DistTypes.mixedPoint;
|
||||
let toShape: t => DistTypes.shape;
|
||||
let toContinuous: t => option(DistTypes.continuousShape);
|
||||
|
@ -45,7 +29,7 @@ module Dist = (T: dist) => {
|
|||
| (Some(min), Some(max)) => Some(max -. min)
|
||||
| _ => None
|
||||
};
|
||||
let pointwiseFmap = T.pointwiseFmap;
|
||||
let mapY = T.mapY;
|
||||
let xToY = T.xToY;
|
||||
let truncate = T.truncate;
|
||||
let toShape = T.toShape;
|
||||
|
@ -56,8 +40,7 @@ module Dist = (T: dist) => {
|
|||
let toScaledDiscrete = T.toScaledDiscrete;
|
||||
|
||||
// TODO: Move this to each class, have use integral to produce integral in DistPlus class.
|
||||
let scaleBy = (~scale=1.0, t: t) =>
|
||||
t |> pointwiseFmap((r: float) => r *. scale);
|
||||
let scaleBy = (~scale=1.0, t: t) => t |> mapY((r: float) => r *. scale);
|
||||
|
||||
module Integral = {
|
||||
type t = T.integral;
|
||||
|
@ -87,7 +70,7 @@ module Continuous = {
|
|||
interpolation,
|
||||
};
|
||||
let lastY = (t: t) =>
|
||||
t |> xyShape |> XYShape.T.Pairs.unsafeLast |> (((_, y)) => y);
|
||||
t |> xyShape |> XYShape.Pairs.unsafeLast |> (((_, y)) => y);
|
||||
let oShapeMap =
|
||||
(fn, {xyShape, interpolation}: t): option(DistTypes.continuousShape) =>
|
||||
fn(xyShape) |> E.O.fmap(make(_, interpolation));
|
||||
|
@ -110,19 +93,19 @@ module Continuous = {
|
|||
let minX = shapeFn(XYShape.T.minX);
|
||||
let maxX = shapeFn(XYShape.T.maxX);
|
||||
let toDiscreteProbabilityMass = _ => 0.0;
|
||||
let pointwiseFmap = (fn, t: t) =>
|
||||
t |> xyShape |> XYShape.T.pointwiseMap(fn) |> fromShape;
|
||||
let mapY = (fn, t: t) =>
|
||||
t |> xyShape |> XYShape.T.mapY(fn) |> fromShape;
|
||||
let toShape = (t: t): DistTypes.shape => Continuous(t);
|
||||
let xToY = (f, {interpolation, xyShape}: t) =>
|
||||
switch (interpolation) {
|
||||
| `Stepwise =>
|
||||
xyShape
|
||||
|> XYShape.T.XtoY.stepwiseIncremental(f)
|
||||
|> XYShape.XtoY.stepwiseIncremental(f)
|
||||
|> E.O.default(0.0)
|
||||
|> DistTypes.MixedPoint.makeContinuous
|
||||
| `Linear =>
|
||||
xyShape
|
||||
|> XYShape.T.XtoY.linear(f)
|
||||
|> XYShape.XtoY.linear(f)
|
||||
|> DistTypes.MixedPoint.makeContinuous
|
||||
};
|
||||
|
||||
|
@ -146,16 +129,16 @@ module Continuous = {
|
|||
let truncate = (~cache=None, i, t) =>
|
||||
t
|
||||
|> shapeMap(
|
||||
XYShape.T.XsConversion.proportionByProbabilityMass(
|
||||
XYShape.XsConversion.proportionByProbabilityMass(
|
||||
i,
|
||||
integral(~cache, t).xyShape,
|
||||
),
|
||||
);
|
||||
let integralEndY = (~cache, t) => t |> integral(~cache) |> lastY;
|
||||
let integralXtoY = (~cache, f, t) =>
|
||||
t |> integral(~cache) |> shapeFn(XYShape.T.XtoY.linear(f));
|
||||
t |> integral(~cache) |> shapeFn(XYShape.XtoY.linear(f));
|
||||
let integralYtoX = (~cache, f, t) =>
|
||||
t |> integral(~cache) |> shapeFn(XYShape.T.YtoX.linear(f));
|
||||
t |> integral(~cache) |> shapeFn(XYShape.YtoX.linear(f));
|
||||
let toContinuous = t => Some(t);
|
||||
let toDiscrete = _ => None;
|
||||
let toScaledContinuous = t => Some(t);
|
||||
|
@ -179,14 +162,14 @@ module Discrete = {
|
|||
let integral = (~cache, t) =>
|
||||
switch (cache) {
|
||||
| Some(c) => c
|
||||
| None => Continuous.make(XYShape.T.accumulateYs(t), `Stepwise)
|
||||
| None => Continuous.make(XYShape.T.accumulateYs((+.), t), `Stepwise)
|
||||
};
|
||||
let integralEndY = (~cache, t) =>
|
||||
t |> integral(~cache) |> Continuous.lastY;
|
||||
let minX = XYShape.T.minX;
|
||||
let maxX = XYShape.T.maxX;
|
||||
let toDiscreteProbabilityMass = t => 1.0;
|
||||
let pointwiseFmap = XYShape.T.pointwiseMap;
|
||||
let mapY = XYShape.T.mapY;
|
||||
let toShape = (t: t): DistTypes.shape => Discrete(t);
|
||||
let toContinuous = _ => None;
|
||||
let toDiscrete = t => Some(t);
|
||||
|
@ -195,14 +178,14 @@ module Discrete = {
|
|||
let truncate = (~cache=None, i, t: t): DistTypes.discreteShape =>
|
||||
t
|
||||
|> XYShape.T.zip
|
||||
|> XYShape.T.Zipped.sortByY
|
||||
|> XYShape.Zipped.sortByY
|
||||
|> Belt.Array.reverse
|
||||
|> Belt.Array.slice(_, ~offset=0, ~len=i)
|
||||
|> XYShape.T.Zipped.sortByX
|
||||
|> XYShape.Zipped.sortByX
|
||||
|> XYShape.T.fromZippedArray;
|
||||
|
||||
let xToY = (f, t) => {
|
||||
XYShape.T.XtoY.stepwiseIfAtX(f, t)
|
||||
XYShape.XtoY.stepwiseIfAtX(f, t)
|
||||
|> E.O.default(0.0)
|
||||
|> DistTypes.MixedPoint.makeDiscrete;
|
||||
};
|
||||
|
@ -211,13 +194,13 @@ module Discrete = {
|
|||
t
|
||||
|> integral(~cache)
|
||||
|> Continuous.getShape
|
||||
|> XYShape.T.XtoY.linear(f);
|
||||
|> XYShape.XtoY.linear(f);
|
||||
|
||||
let integralYtoX = (~cache, f, t) =>
|
||||
t
|
||||
|> integral(~cache)
|
||||
|> Continuous.getShape
|
||||
|> XYShape.T.YtoX.linear(f);
|
||||
|> XYShape.YtoX.linear(f);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -348,9 +331,10 @@ module Mixed = {
|
|||
|
||||
let result =
|
||||
Continuous.make(
|
||||
XYShape.T.Combine.combineLinear(
|
||||
Continuous.getShape(cont), Continuous.getShape(dist), (a, b) =>
|
||||
a +. b
|
||||
XYShape.Combine.combineLinear(
|
||||
~fn=(a, b) => a +. b,
|
||||
Continuous.getShape(cont),
|
||||
Continuous.getShape(dist),
|
||||
),
|
||||
`Linear,
|
||||
);
|
||||
|
@ -366,22 +350,22 @@ module Mixed = {
|
|||
t
|
||||
|> integral(~cache)
|
||||
|> Continuous.getShape
|
||||
|> XYShape.T.XtoY.linear(f);
|
||||
|> XYShape.XtoY.linear(f);
|
||||
};
|
||||
|
||||
let integralYtoX = (~cache, f, t) => {
|
||||
t
|
||||
|> integral(~cache)
|
||||
|> Continuous.getShape
|
||||
|> XYShape.T.YtoX.linear(f);
|
||||
|> XYShape.YtoX.linear(f);
|
||||
};
|
||||
|
||||
// TODO: This functionality is kinda weird, because it seems to assume the cdf adds to 1.0 elsewhere, which wouldn't happen here.
|
||||
let pointwiseFmap =
|
||||
let mapY =
|
||||
(fn, {discrete, continuous, discreteProbabilityMassFraction}: t): t => {
|
||||
{
|
||||
discrete: Discrete.T.pointwiseFmap(fn, discrete),
|
||||
continuous: Continuous.T.pointwiseFmap(fn, continuous),
|
||||
discrete: Discrete.T.mapY(fn, discrete),
|
||||
continuous: Continuous.T.mapY(fn, continuous),
|
||||
discreteProbabilityMassFraction,
|
||||
};
|
||||
};
|
||||
|
@ -516,14 +500,10 @@ module Shape = {
|
|||
};
|
||||
let maxX = (t: t) =>
|
||||
mapToAll(t, (Mixed.T.maxX, Discrete.T.maxX, Continuous.T.maxX));
|
||||
let pointwiseFmap = (fn, t: t) =>
|
||||
let mapY = (fn, t: t) =>
|
||||
fmap(
|
||||
t,
|
||||
(
|
||||
Mixed.T.pointwiseFmap(fn),
|
||||
Discrete.T.pointwiseFmap(fn),
|
||||
Continuous.T.pointwiseFmap(fn),
|
||||
),
|
||||
(Mixed.T.mapY(fn), Discrete.T.mapY(fn), Continuous.T.mapY(fn)),
|
||||
);
|
||||
});
|
||||
};
|
||||
|
@ -592,9 +572,7 @@ module DistPlus = {
|
|||
|> toShape
|
||||
|> Shape.T.toScaledContinuous
|
||||
|> E.O.fmap(
|
||||
Continuous.T.pointwiseFmap(
|
||||
domainIncludedProbabilityMassAdjustment(t),
|
||||
),
|
||||
Continuous.T.mapY(domainIncludedProbabilityMassAdjustment(t)),
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -603,9 +581,7 @@ module DistPlus = {
|
|||
|> toShape
|
||||
|> Shape.T.toScaledDiscrete
|
||||
|> E.O.fmap(
|
||||
Discrete.T.pointwiseFmap(
|
||||
domainIncludedProbabilityMassAdjustment(t),
|
||||
),
|
||||
Discrete.T.mapY(domainIncludedProbabilityMassAdjustment(t)),
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -627,8 +603,8 @@ module DistPlus = {
|
|||
let truncate = (~cache=None, i, t) =>
|
||||
updateShape(t |> toShape |> Shape.T.truncate(i), t);
|
||||
// todo: adjust for limit, maybe?
|
||||
let pointwiseFmap = (fn, {shape, _} as t: t): t =>
|
||||
Shape.T.pointwiseFmap(fn, shape) |> updateShape(_, t);
|
||||
let mapY = (fn, {shape, _} as t: t): t =>
|
||||
Shape.T.mapY(fn, shape) |> updateShape(_, t);
|
||||
|
||||
let integralEndY = (~cache as _, t: t) =>
|
||||
Shape.T.Integral.sum(~cache=Some(t.integralCache), toShape(t));
|
||||
|
|
|
@ -11,10 +11,6 @@ let interpolate =
|
|||
module T = {
|
||||
type t = xyShape;
|
||||
type ts = array(xyShape);
|
||||
|
||||
let toJs = (t: t) => {
|
||||
{"xs": t.xs, "ys": t.ys};
|
||||
};
|
||||
let xs = (t: t) => t.xs;
|
||||
let ys = (t: t) => t.ys;
|
||||
let minX = (t: t) => t |> xs |> E.A.Sorted.min;
|
||||
|
@ -22,13 +18,16 @@ module T = {
|
|||
let minY = (t: t) => t |> ys |> E.A.Sorted.min;
|
||||
let maxY = (t: t) => t |> ys |> E.A.Sorted.max;
|
||||
let xTotalRange = (t: t) => t |> xs |> E.A.Sorted.range;
|
||||
let mapX = (fn, t: t): t => {xs: E.A.fmap(fn, t.xs), ys: t.ys};
|
||||
let mapY = (fn, t: t): t => {xs: t.xs, ys: E.A.fmap(fn, t.ys)};
|
||||
let zip = ({xs, ys}: t) => Belt.Array.zip(xs, ys);
|
||||
let pointwiseMap = (fn, t: t): t => {xs: t.xs, ys: t.ys |> E.A.fmap(fn)};
|
||||
let xMap = (fn, t: t): t => {xs: E.A.fmap(fn, t.xs), ys: t.ys};
|
||||
let fromArray = ((xs, ys)): t => {xs, ys};
|
||||
let fromArrays = (xs, ys): t => {xs, ys};
|
||||
let fromZippedArray = (is: array((float, float))): t =>
|
||||
is |> Belt.Array.unzip |> fromArray;
|
||||
let accumulateYs = (fn, p: t) => {
|
||||
fromArray((p.xs, E.A.accumulate(fn, p.ys)));
|
||||
};
|
||||
let fromZippedArray = (pairs: array((float, float))): t =>
|
||||
pairs |> Belt.Array.unzip |> fromArray;
|
||||
let equallyDividedXs = (t: t, newLength) => {
|
||||
E.A.Floats.range(
|
||||
minX(t) |> E.O.toExt("Unsafe"),
|
||||
|
@ -36,198 +35,195 @@ module T = {
|
|||
newLength,
|
||||
);
|
||||
};
|
||||
|
||||
module Ts = {
|
||||
type t = ts;
|
||||
let minX = (t: t) =>
|
||||
t
|
||||
|> E.A.fmap(minX)
|
||||
|> E.A.O.concatSomes
|
||||
|> E.A.min
|
||||
|> E.O.toExt("Unsafe");
|
||||
let maxX = (t: t) =>
|
||||
t
|
||||
|> E.A.fmap(maxX)
|
||||
|> E.A.O.concatSomes
|
||||
|> E.A.max
|
||||
|> E.O.toExt("Unsafe");
|
||||
let equallyDividedXs = (t: t, newLength) => {
|
||||
E.A.Floats.range(minX(t), maxX(t), newLength);
|
||||
};
|
||||
let toJs = (t: t) => {
|
||||
{"xs": t.xs, "ys": t.ys};
|
||||
};
|
||||
};
|
||||
|
||||
module Pairs = {
|
||||
let first = (t: t) =>
|
||||
switch (minX(t), minY(t)) {
|
||||
| (Some(x), Some(y)) => Some((x, y))
|
||||
| _ => None
|
||||
module Ts = {
|
||||
type t = T.ts;
|
||||
let minX = (t: t) =>
|
||||
t
|
||||
|> E.A.fmap(T.minX)
|
||||
|> E.A.O.concatSomes
|
||||
|> E.A.min
|
||||
|> E.O.toExt("Unsafe");
|
||||
let maxX = (t: t) =>
|
||||
t
|
||||
|> E.A.fmap(T.maxX)
|
||||
|> E.A.O.concatSomes
|
||||
|> E.A.max
|
||||
|> E.O.toExt("Unsafe");
|
||||
let equallyDividedXs = (t: t, newLength) => {
|
||||
E.A.Floats.range(minX(t), maxX(t), newLength);
|
||||
};
|
||||
let allXs = (t: t) => t |> E.A.fmap(T.xs) |> E.A.Sorted.concatMany;
|
||||
};
|
||||
|
||||
module Pairs = {
|
||||
let x = fst;
|
||||
let y = snd;
|
||||
let first = (t: T.t) =>
|
||||
switch (T.minX(t), T.minY(t)) {
|
||||
| (Some(x), Some(y)) => Some((x, y))
|
||||
| _ => None
|
||||
};
|
||||
let last = (t: T.t) =>
|
||||
switch (T.maxX(t), T.maxY(t)) {
|
||||
| (Some(x), Some(y)) => Some((x, y))
|
||||
| _ => None
|
||||
};
|
||||
|
||||
let unsafeFirst = (t: T.t) => first(t) |> E.O.toExn("Unsafe operation");
|
||||
let unsafeLast = (t: T.t) => last(t) |> E.O.toExn("Unsafe operation");
|
||||
|
||||
let getBy = (t: T.t, fn) => t |> T.zip |> E.A.getBy(_, fn);
|
||||
|
||||
let firstAtOrBeforeXValue = (xValue, t: T.t) => {
|
||||
let zipped = T.zip(t);
|
||||
let firstIndex =
|
||||
zipped |> Belt.Array.getIndexBy(_, ((x, _)) => x > xValue);
|
||||
let previousIndex =
|
||||
switch (firstIndex) {
|
||||
| None => Some(Array.length(zipped) - 1)
|
||||
| Some(0) => None
|
||||
| Some(n) => Some(n - 1)
|
||||
};
|
||||
let last = (t: t) =>
|
||||
switch (maxX(t), maxY(t)) {
|
||||
| (Some(x), Some(y)) => Some((x, y))
|
||||
| _ => None
|
||||
previousIndex |> Belt.Option.flatMap(_, Belt.Array.get(zipped));
|
||||
};
|
||||
};
|
||||
|
||||
module YtoX = {
|
||||
let linear = (y: float, t: T.t): float => {
|
||||
let firstHigherIndex =
|
||||
E.A.Sorted.binarySearchFirstElementGreaterIndex(T.ys(t), y);
|
||||
let foundX =
|
||||
switch (firstHigherIndex) {
|
||||
| `overMax => T.maxX(t) |> E.O.default(0.0)
|
||||
| `underMin => T.minX(t) |> E.O.default(0.0)
|
||||
| `firstHigher(firstHigherIndex) =>
|
||||
let lowerOrEqualIndex =
|
||||
firstHigherIndex - 1 < 0 ? 0 : firstHigherIndex - 1;
|
||||
let (_xs, _ys) = (T.xs(t), T.ys(t));
|
||||
let needsInterpolation = _ys[lowerOrEqualIndex] != y;
|
||||
if (needsInterpolation) {
|
||||
interpolate(
|
||||
_ys[lowerOrEqualIndex],
|
||||
_ys[firstHigherIndex],
|
||||
_xs[lowerOrEqualIndex],
|
||||
_xs[firstHigherIndex],
|
||||
y,
|
||||
);
|
||||
} else {
|
||||
_xs[lowerOrEqualIndex];
|
||||
};
|
||||
};
|
||||
let unsafeFirst = (t: t) => first(t) |> E.O.toExn("Unsafe operation");
|
||||
let unsafeLast = (t: t) => last(t) |> E.O.toExn("Unsafe operation");
|
||||
foundX;
|
||||
};
|
||||
};
|
||||
|
||||
let getBy = (t: t, fn) => t |> zip |> Belt.Array.getBy(_, fn);
|
||||
module XtoY = {
|
||||
let stepwiseIncremental = (f, t: T.t) =>
|
||||
Pairs.firstAtOrBeforeXValue(f, t) |> E.O.fmap(Pairs.y);
|
||||
|
||||
let firstAtOrBeforeXValue = (xValue, t: t) => {
|
||||
let zipped = zip(t);
|
||||
let firstIndex =
|
||||
zipped |> Belt.Array.getIndexBy(_, ((x, _)) => x > xValue);
|
||||
let previousIndex =
|
||||
switch (firstIndex) {
|
||||
| None => Some(Array.length(zipped) - 1)
|
||||
| Some(0) => None
|
||||
| Some(n) => Some(n - 1)
|
||||
let stepwiseIfAtX = (f: float, t: T.t) => {
|
||||
Pairs.getBy(t, ((x: float, _)) => {x == f}) |> E.O.fmap(Pairs.y);
|
||||
};
|
||||
|
||||
let linear = (x: float, t: T.t): float => {
|
||||
let firstHigherIndex =
|
||||
E.A.Sorted.binarySearchFirstElementGreaterIndex(T.xs(t), x);
|
||||
let n =
|
||||
switch (firstHigherIndex) {
|
||||
| `overMax => T.maxY(t) |> E.O.default(0.0)
|
||||
| `underMin => T.minY(t) |> E.O.default(0.0)
|
||||
| `firstHigher(firstHigherIndex) =>
|
||||
let lowerOrEqualIndex =
|
||||
firstHigherIndex - 1 < 0 ? 0 : firstHigherIndex - 1;
|
||||
let (_xs, _ys) = (T.xs(t), T.ys(t));
|
||||
let needsInterpolation = _xs[lowerOrEqualIndex] != x;
|
||||
if (needsInterpolation) {
|
||||
interpolate(
|
||||
_xs[lowerOrEqualIndex],
|
||||
_xs[firstHigherIndex],
|
||||
_ys[lowerOrEqualIndex],
|
||||
_ys[firstHigherIndex],
|
||||
x,
|
||||
);
|
||||
} else {
|
||||
_ys[lowerOrEqualIndex];
|
||||
};
|
||||
previousIndex |> Belt.Option.flatMap(_, Belt.Array.get(zipped));
|
||||
};
|
||||
};
|
||||
n;
|
||||
};
|
||||
};
|
||||
|
||||
module XsConversion = {
|
||||
let _replaceWithXs = (newXs: array(float), t: T.t): T.t => {
|
||||
let newYs = Belt.Array.map(newXs, XtoY.linear(_, t));
|
||||
{xs: newXs, ys: newYs};
|
||||
};
|
||||
|
||||
module YtoX = {
|
||||
let linear = (y: float, t: t): float => {
|
||||
let firstHigherIndex =
|
||||
E.A.Sorted.binarySearchFirstElementGreaterIndex(ys(t), y);
|
||||
let foundX =
|
||||
switch (firstHigherIndex) {
|
||||
| `overMax => maxX(t) |> E.O.default(0.0)
|
||||
| `underMin => minX(t) |> E.O.default(0.0)
|
||||
| `firstHigher(firstHigherIndex) =>
|
||||
let lowerOrEqualIndex =
|
||||
firstHigherIndex - 1 < 0 ? 0 : firstHigherIndex - 1;
|
||||
let (_xs, _ys) = (xs(t), ys(t));
|
||||
let needsInterpolation = _ys[lowerOrEqualIndex] != y;
|
||||
if (needsInterpolation) {
|
||||
interpolate(
|
||||
_ys[lowerOrEqualIndex],
|
||||
_ys[firstHigherIndex],
|
||||
_xs[lowerOrEqualIndex],
|
||||
_xs[firstHigherIndex],
|
||||
y,
|
||||
);
|
||||
} else {
|
||||
_xs[lowerOrEqualIndex];
|
||||
};
|
||||
};
|
||||
foundX;
|
||||
};
|
||||
let equallyDivideXByMass = (newLength: int, integral: T.t) =>
|
||||
E.A.Floats.range(0.0, 1.0, newLength)
|
||||
|> E.A.fmap(YtoX.linear(_, integral));
|
||||
|
||||
let proportionEquallyOverX = (newLength: int, t: T.t): T.t => {
|
||||
T.equallyDividedXs(t, newLength) |> _replaceWithXs(_, t);
|
||||
};
|
||||
|
||||
module XtoY = {
|
||||
let stepwiseIncremental = (f, t: t) =>
|
||||
Pairs.firstAtOrBeforeXValue(f, t) |> E.O.fmap(((_, y)) => y);
|
||||
let proportionByProbabilityMass =
|
||||
(newLength: int, integral: T.t, t: T.t): T.t => {
|
||||
equallyDivideXByMass(newLength, integral) |> _replaceWithXs(_, t);
|
||||
};
|
||||
};
|
||||
|
||||
let stepwiseIfAtX = (f: float, t: t) => {
|
||||
Pairs.getBy(t, ((x: float, _)) => {x == f})
|
||||
|> E.O.fmap(((_, y)) => y);
|
||||
};
|
||||
module Zipped = {
|
||||
type zipped = array((float, float));
|
||||
let sortByY = (t: zipped) =>
|
||||
t |> E.A.stableSortBy(_, ((_, y1), (_, y2)) => y1 > y2 ? 1 : 0);
|
||||
let sortByX = (t: zipped) =>
|
||||
t |> E.A.stableSortBy(_, ((x1, _), (x2, _)) => x1 > x2 ? 1 : 0);
|
||||
};
|
||||
|
||||
let linear = (x: float, t: t): float => {
|
||||
let firstHigherIndex =
|
||||
E.A.Sorted.binarySearchFirstElementGreaterIndex(xs(t), x);
|
||||
let n =
|
||||
switch (firstHigherIndex) {
|
||||
| `overMax => maxY(t) |> E.O.default(0.0)
|
||||
| `underMin => minY(t) |> E.O.default(0.0)
|
||||
| `firstHigher(firstHigherIndex) =>
|
||||
let lowerOrEqualIndex =
|
||||
firstHigherIndex - 1 < 0 ? 0 : firstHigherIndex - 1;
|
||||
let (_xs, _ys) = (xs(t), ys(t));
|
||||
let needsInterpolation = _xs[lowerOrEqualIndex] != x;
|
||||
if (needsInterpolation) {
|
||||
interpolate(
|
||||
_xs[lowerOrEqualIndex],
|
||||
_xs[firstHigherIndex],
|
||||
_ys[lowerOrEqualIndex],
|
||||
_ys[firstHigherIndex],
|
||||
x,
|
||||
);
|
||||
} else {
|
||||
_ys[lowerOrEqualIndex];
|
||||
};
|
||||
};
|
||||
n;
|
||||
};
|
||||
module Combine = {
|
||||
type xsSelection =
|
||||
| ALL_XS
|
||||
| XS_EVENLY_DIVIDED(int);
|
||||
|
||||
type xToYSelection =
|
||||
| LINEAR
|
||||
| STEPWISE_INCREMENTAL
|
||||
| STEPWISE_IF_AT_X;
|
||||
|
||||
let combine =
|
||||
(
|
||||
~xToYSelection: (float, T.t) => 'a,
|
||||
~xsSelection=ALL_XS,
|
||||
~fn,
|
||||
t1: T.t,
|
||||
t2: T.t,
|
||||
) => {
|
||||
let allXs =
|
||||
switch (xsSelection) {
|
||||
| ALL_XS => Ts.allXs([|t1, t2|])
|
||||
| XS_EVENLY_DIVIDED(sampleCount) =>
|
||||
Ts.equallyDividedXs([|t1, t2|], sampleCount)
|
||||
};
|
||||
|
||||
let allYs =
|
||||
allXs |> E.A.fmap(x => fn(xToYSelection(x, t1), xToYSelection(x, t2)));
|
||||
T.fromArrays(allXs, allYs);
|
||||
};
|
||||
|
||||
module XsConversion = {
|
||||
let replaceWithXs = (newXs: array(float), t: t): t => {
|
||||
let newYs = Belt.Array.map(newXs, f => XtoY.linear(f, t));
|
||||
{xs: newXs, ys: newYs};
|
||||
};
|
||||
let combineLinear = combine(~xToYSelection=XtoY.linear);
|
||||
let combineStepwise = combine(~xToYSelection=XtoY.stepwiseIncremental);
|
||||
let combineIfAtX = combine(~xToYSelection=XtoY.stepwiseIfAtX);
|
||||
|
||||
let proportionEquallyOverX = (newLength: int, t: t): t => {
|
||||
equallyDividedXs(t, newLength) |> replaceWithXs(_, t);
|
||||
};
|
||||
|
||||
let proportionByProbabilityMass = (newLength: int, integral: t, t: t): t => {
|
||||
E.A.Floats.range(0.0, 1.0, newLength)
|
||||
|> E.A.fmap(YtoX.linear(_, integral))
|
||||
|> replaceWithXs(_, t);
|
||||
};
|
||||
// TODO: I'd bet this is pretty slow. Maybe it would be faster to intersperse Xs and Ys separately.
|
||||
let intersperse = (t1: T.t, t2: T.t) => {
|
||||
E.A.intersperse(T.zip(t1), T.zip(t2)) |> T.fromZippedArray;
|
||||
};
|
||||
|
||||
module Zipped = {
|
||||
type zipped = array((float, float));
|
||||
let sortByY = (t: zipped) =>
|
||||
t |> E.A.stableSortBy(_, ((_, y1), (_, y2)) => y1 > y2 ? 1 : 0);
|
||||
let sortByX = (t: zipped) =>
|
||||
t |> E.A.stableSortBy(_, ((x1, _), (x2, _)) => x1 > x2 ? 1 : 0);
|
||||
};
|
||||
|
||||
module Combine = {
|
||||
let _allXs = (t1: t, t2: t) => E.A.Sorted.concat(xs(t1), xs(t2));
|
||||
|
||||
let _combineAbstract = (comboAlg, t1: t, t2: t, fn) => {
|
||||
let allXs = _allXs(t1, t2);
|
||||
let allYs =
|
||||
allXs
|
||||
|> E.A.fmap(x => {
|
||||
let y1 = comboAlg(x, t1);
|
||||
let y2 = comboAlg(x, t2);
|
||||
fn(y1, y2);
|
||||
});
|
||||
fromArrays(allXs, allYs);
|
||||
};
|
||||
|
||||
let combineLinear = _combineAbstract(XtoY.linear);
|
||||
let combineStepwise = _combineAbstract(XtoY.stepwiseIncremental);
|
||||
let combineIfAtX = _combineAbstract(XtoY.stepwiseIfAtX);
|
||||
|
||||
// TODO: I'd bet this is pretty slow
|
||||
let intersperse = (t1: t, t2: t) => {
|
||||
E.A.intersperse(zip(t1), zip(t2)) |> fromZippedArray;
|
||||
};
|
||||
};
|
||||
|
||||
module Transversal = {
|
||||
let _transverse = (fn, items) => {
|
||||
let length = items |> E.A.length;
|
||||
let empty = Belt.Array.make(length, items |> E.A.unsafe_get(_, 0));
|
||||
Belt.Array.forEachWithIndex(
|
||||
items,
|
||||
(index, element) => {
|
||||
let item =
|
||||
switch (index) {
|
||||
| 0 => element
|
||||
| index => fn(element, E.A.unsafe_get(empty, index - 1))
|
||||
};
|
||||
let _ = Belt.Array.set(empty, index, item);
|
||||
();
|
||||
},
|
||||
);
|
||||
empty;
|
||||
};
|
||||
|
||||
let _transverseShape = (fn, p: t) => {
|
||||
fromArray((p.xs, _transverse(fn, p.ys)));
|
||||
};
|
||||
};
|
||||
|
||||
let accumulateYs =
|
||||
Transversal._transverseShape((aCurrent, aLast) => aCurrent +. aLast);
|
||||
};
|
||||
|
||||
// I'm really not sure this part is actually what we want at this point.
|
||||
|
@ -235,12 +231,10 @@ module Range = {
|
|||
// ((lastX, lastY), (nextX, nextY))
|
||||
type zippedRange = ((float, float), (float, float));
|
||||
|
||||
let floatSum = Belt.Array.reduce(_, 0., (a, b) => a +. b);
|
||||
let toT = T.fromZippedArray;
|
||||
let nextX = ((_, (nextX, _)): zippedRange) => nextX;
|
||||
|
||||
let rangePointAssumingSteps =
|
||||
(((lastX, lastY), (nextX, nextY)): zippedRange) => (
|
||||
let rangePointAssumingSteps = (((_, lastY), (nextX, _)): zippedRange) => (
|
||||
nextX,
|
||||
lastY,
|
||||
);
|
||||
|
@ -270,14 +264,15 @@ module Range = {
|
|||
let integrateWithTriangles = ({xs, ys}) => {
|
||||
let length = E.A.length(xs);
|
||||
let cumulativeY = Belt.Array.make(length, 0.0);
|
||||
//TODO: I don't think this next line is needed, but definitely check.
|
||||
let _ = Belt.Array.set(cumulativeY, 0, 0.0);
|
||||
for (x in 0 to E.A.length(xs) - 2) {
|
||||
Belt.Array.set(
|
||||
cumulativeY,
|
||||
x + 1,
|
||||
(xs[x + 1] -. xs[x]) *. ((ys[x] +. ys[x + 1]) /. 2.) +. cumulativeY[x],
|
||||
);
|
||||
let _ =
|
||||
Belt.Array.set(
|
||||
cumulativeY,
|
||||
x + 1,
|
||||
(xs[x + 1] -. xs[x])
|
||||
*. ((ys[x] +. ys[x + 1]) /. 2.)
|
||||
+. cumulativeY[x],
|
||||
);
|
||||
();
|
||||
};
|
||||
Some({xs, ys: cumulativeY});
|
||||
|
@ -295,7 +290,7 @@ module Range = {
|
|||
items
|
||||
|> Belt.Array.map(_, rangePointAssumingSteps)
|
||||
|> T.fromZippedArray
|
||||
|> T.Combine.intersperse(t |> T.xMap(e => e +. diff)),
|
||||
|> Combine.intersperse(t |> T.mapX(e => e +. diff)),
|
||||
)
|
||||
| _ => Some(t)
|
||||
};
|
||||
|
@ -310,25 +305,21 @@ module Range = {
|
|||
};
|
||||
};
|
||||
|
||||
let combinePointwise = (fn, sampleCount, t1: xyShape, t2: xyShape) => {
|
||||
let xs = T.Ts.equallyDividedXs([|t1, t2|], sampleCount);
|
||||
let ys =
|
||||
xs |> E.A.fmap(x => fn(T.XtoY.linear(x, t1), T.XtoY.linear(x, t2)));
|
||||
T.fromArrays(xs, ys);
|
||||
};
|
||||
|
||||
let logScoreDist =
|
||||
combinePointwise((prediction, answer) =>
|
||||
switch (answer) {
|
||||
| 0. => 0.0
|
||||
| answer =>
|
||||
answer *. Js.Math.log2(Js.Math.abs_float(prediction /. answer))
|
||||
}
|
||||
);
|
||||
let pointLogScore = (prediction, answer) =>
|
||||
switch (answer) {
|
||||
| 0. => 0.0
|
||||
| answer => answer *. Js.Math.log2(Js.Math.abs_float(prediction /. answer))
|
||||
};
|
||||
|
||||
let logScorePoint = (sampleCount, t1, t2) =>
|
||||
logScoreDist(sampleCount, t1, t2)
|
||||
Combine.combine(
|
||||
~xsSelection=XS_EVENLY_DIVIDED(sampleCount),
|
||||
~xToYSelection=XtoY.linear,
|
||||
~fn=pointLogScore,
|
||||
t1,
|
||||
t2,
|
||||
)
|
||||
|> Range.integrateWithTriangles
|
||||
|> E.O.fmap(T.accumulateYs)
|
||||
|> E.O.bind(_, T.Pairs.last)
|
||||
|> E.O.fmap(((_, y)) => y);
|
||||
|> E.O.fmap(T.accumulateYs((+.)))
|
||||
|> E.O.bind(_, Pairs.last)
|
||||
|> E.O.fmap(Pairs.y);
|
|
@ -76,6 +76,17 @@ module O = {
|
|||
| None => Error(error)
|
||||
};
|
||||
|
||||
let compare = (compare, f1: option(float), f2: option(float)) =>
|
||||
switch (f1, f2) {
|
||||
| (Some(f1), Some(f2)) => Some(compare(f1, f2) ? f1 : f2)
|
||||
| (Some(f1), None) => Some(f1)
|
||||
| (None, Some(f2)) => Some(f2)
|
||||
| (None, None) => None
|
||||
};
|
||||
|
||||
let min = compare((<));
|
||||
let max = compare((>));
|
||||
|
||||
module React = {
|
||||
let defaultNull = default(ReasonReact.null);
|
||||
let fmapOrNull = fn => fmap(fn) ||> default(ReasonReact.null);
|
||||
|
@ -289,6 +300,26 @@ module A = {
|
|||
items^;
|
||||
};
|
||||
|
||||
// This is like map, but
|
||||
//accumulate((a,b) => a + b, [1,2,3]) => [1, 3, 5]
|
||||
let accumulate = (fn: ('a, 'a) => 'a, items: array('a)) => {
|
||||
let length = items |> length;
|
||||
let empty = Belt.Array.make(length, items |> unsafe_get(_, 0));
|
||||
Belt.Array.forEachWithIndex(
|
||||
items,
|
||||
(index, element) => {
|
||||
let item =
|
||||
switch (index) {
|
||||
| 0 => element
|
||||
| index => fn(element, unsafe_get(empty, index - 1))
|
||||
};
|
||||
let _ = Belt.Array.set(empty, index, item);
|
||||
();
|
||||
},
|
||||
);
|
||||
empty;
|
||||
};
|
||||
|
||||
// @todo: Is -1 still the indicator that this is false (as is true with
|
||||
// @todo: js findIndex)? Wasn't sure.
|
||||
let findIndex = (e, i) =>
|
||||
|
@ -356,6 +387,12 @@ module A = {
|
|||
ts;
|
||||
};
|
||||
|
||||
let concatMany = (t1: array(array('a))) => {
|
||||
let ts = Belt.Array.concatMany(t1);
|
||||
ts |> Array.fast_sort(compare);
|
||||
ts;
|
||||
};
|
||||
|
||||
module Floats = {
|
||||
let makeIncrementalUp = (a, b) =>
|
||||
Array.make(b - a + 1, a)
|
||||
|
|
Loading…
Reference in New Issue
Block a user