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