Merge pull request #181 from QURIresearch/dist-refactor

Rescript Organization Refactor #1
This commit is contained in:
Quinn 2022-04-05 15:47:16 -04:00 committed by GitHub
commit f6ee23f229
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 269 additions and 319 deletions

View File

@ -1,89 +0,0 @@
open Jest
open Expect
let makeTest = (~only=false, str, item1, item2) =>
only
? Only.test(str, () => expect(item1) -> toEqual(item2))
: test(str, () => expect(item1) -> toEqual(item2))
describe("PointSetTypes", () =>
describe("Domain", () => {
let makeComplete = (yPoint, expectation) =>
makeTest(
"With input: " ++ Js.Float.toString(yPoint),
PointSetTypes.Domain.yPointToSubYPoint(Complete, yPoint),
expectation,
)
let makeSingle = (direction: [#left | #right], excludingProbabilityMass, yPoint, expectation) =>
makeTest(
"Excluding: " ++
(Js.Float.toString(excludingProbabilityMass) ++
(" and yPoint: " ++ Js.Float.toString(yPoint))),
PointSetTypes.Domain.yPointToSubYPoint(
direction == #left
? LeftLimited({xPoint: 3.0, excludingProbabilityMass: excludingProbabilityMass})
: RightLimited({xPoint: 3.0, excludingProbabilityMass: excludingProbabilityMass}),
yPoint,
),
expectation,
)
let makeDouble = (domain, yPoint, expectation) =>
makeTest("Excluding: limits", PointSetTypes.Domain.yPointToSubYPoint(domain, yPoint), expectation)
describe("With Complete Domain", () => {
makeComplete(0.0, Some(0.0))
makeComplete(0.6, Some(0.6))
makeComplete(1.0, Some(1.0))
})
describe("With Left Limit", () => {
makeSingle(#left, 0.5, 1.0, Some(1.0))
makeSingle(#left, 0.5, 0.75, Some(0.5))
makeSingle(#left, 0.8, 0.9, Some(0.5))
makeSingle(#left, 0.5, 0.4, None)
makeSingle(#left, 0.5, 0.5, Some(0.0))
})
describe("With Right Limit", () => {
makeSingle(#right, 0.5, 1.0, None)
makeSingle(#right, 0.5, 0.25, Some(0.5))
makeSingle(#right, 0.8, 0.5, None)
makeSingle(#right, 0.2, 0.2, Some(0.25))
makeSingle(#right, 0.5, 0.5, Some(1.0))
makeSingle(#right, 0.5, 0.0, Some(0.0))
makeSingle(#right, 0.5, 0.5, Some(1.0))
})
describe("With Left and Right Limit", () => {
makeDouble(
LeftAndRightLimited(
{excludingProbabilityMass: 0.25, xPoint: 3.0},
{excludingProbabilityMass: 0.25, xPoint: 10.0},
),
0.5,
Some(0.5),
)
makeDouble(
LeftAndRightLimited(
{excludingProbabilityMass: 0.1, xPoint: 3.0},
{excludingProbabilityMass: 0.1, xPoint: 10.0},
),
0.2,
Some(0.125),
)
makeDouble(
LeftAndRightLimited(
{excludingProbabilityMass: 0.1, xPoint: 3.0},
{excludingProbabilityMass: 0.1, xPoint: 10.0},
),
0.1,
Some(0.0),
)
makeDouble(
LeftAndRightLimited(
{excludingProbabilityMass: 0.1, xPoint: 3.0},
{excludingProbabilityMass: 0.1, xPoint: 10.0},
),
0.05,
None,
)
})
})
)

View File

@ -1,7 +1,7 @@
open Jest open Jest
open Expect open Expect
let env: GenericDist_GenericOperation.env = { let env: DistributionOperation.env = {
sampleCount: 100, sampleCount: 100,
xyPointLength: 100, xyPointLength: 100,
} }
@ -11,9 +11,9 @@ let normalDist10: GenericDist_Types.genericDist = Symbolic(#Normal({mean: 10.0,
let normalDist20: GenericDist_Types.genericDist = Symbolic(#Normal({mean: 20.0, stdev: 2.0})) let normalDist20: GenericDist_Types.genericDist = Symbolic(#Normal({mean: 20.0, stdev: 2.0}))
let uniformDist: GenericDist_Types.genericDist = Symbolic(#Uniform({low: 9.0, high: 10.0})) let uniformDist: GenericDist_Types.genericDist = Symbolic(#Uniform({low: 9.0, high: 10.0}))
let {toFloat, toDist, toString, toError} = module(GenericDist_GenericOperation.Output) let {toFloat, toDist, toString, toError} = module(DistributionOperation.Output)
let {run} = module(GenericDist_GenericOperation) let {run} = module(DistributionOperation)
let {fmap} = module(GenericDist_GenericOperation.Output) let {fmap} = module(DistributionOperation.Output)
let run = run(~env) let run = run(~env)
let outputMap = fmap(~env) let outputMap = fmap(~env)
let toExt: option<'a> => 'a = E.O.toExt( let toExt: option<'a> => 'a = E.O.toExt(
@ -29,7 +29,7 @@ describe("normalize", () => {
describe("mean", () => { describe("mean", () => {
test("for a normal distribution", () => { test("for a normal distribution", () => {
let result = GenericDist_GenericOperation.run(~env, FromDist(ToFloat(#Mean), normalDist)) let result = DistributionOperation.run(~env, FromDist(ToFloat(#Mean), normalDist))
expect(result)->toEqual(Float(5.0)) expect(result)->toEqual(Float(5.0))
}) })
}) })

View File

@ -1,7 +1,7 @@
import {runAll} from '../rescript/ProgramEvaluator.gen'; import {runAll} from '../rescript/ProgramEvaluator.gen';
import type { Inputs_SamplingInputs_t as SamplingInputs, exportEnv, exportType, exportDistribution} from '../rescript/ProgramEvaluator.gen'; import type { Inputs_SamplingInputs_t as SamplingInputs, exportEnv, exportType, exportDistribution} from '../rescript/ProgramEvaluator.gen';
export type { SamplingInputs, exportEnv, exportDistribution } export type { SamplingInputs, exportEnv, exportDistribution }
export type {t as DistPlus} from '../rescript/pointSetDist/DistPlus.gen'; export type {t as DistPlus} from '../rescript/OldInterpreter/DistPlus.gen';
export let defaultSamplingInputs : SamplingInputs = { export let defaultSamplingInputs : SamplingInputs = {
sampleCount : 10000, sampleCount : 10000,

View File

@ -0,0 +1,92 @@
type genericDist =
| PointSet(PointSetTypes.pointSetDist)
| SampleSet(array<float>)
| Symbolic(SymbolicDistTypes.symbolicDist)
type error =
| NotYetImplemented
| Unreachable
| DistributionVerticalShiftIsInvalid
| Other(string)
module Operation = {
type direction =
| Algebraic
| Pointwise
type arithmeticOperation = [
| #Add
| #Multiply
| #Subtract
| #Divide
| #Exponentiate
| #Logarithm
]
let arithmeticToFn = (arithmetic: arithmeticOperation) =>
switch arithmetic {
| #Add => \"+."
| #Multiply => \"*."
| #Subtract => \"-."
| #Exponentiate => \"**"
| #Divide => \"/."
| #Logarithm => (a, b) => log(a) /. log(b)
}
type toFloat = [
| #Cdf(float)
| #Inv(float)
| #Pdf(float)
| #Mean
| #Sample
]
}
module DistributionOperation = {
type toDist =
| Normalize
| ToPointSet
| ToSampleSet(int)
| Truncate(option<float>, option<float>)
| Inspect
type toFloatArray = Sample(int)
type fromDist =
| ToFloat(Operation.toFloat)
| ToDist(toDist)
| ToDistCombination(Operation.direction, Operation.arithmeticOperation, [#Dist(genericDist) | #Float(float)])
| ToString
type singleParamaterFunction =
| FromDist(fromDist)
| FromFloat(fromDist)
type genericFunctionCallInfo =
| FromDist(fromDist, genericDist)
| FromFloat(fromDist, float)
| Mixture(array<(genericDist, float)>)
let distCallToString = (distFunction: fromDist): string =>
switch distFunction {
| ToFloat(#Cdf(r)) => `cdf(${E.Float.toFixed(r)})`
| ToFloat(#Inv(r)) => `inv(${E.Float.toFixed(r)})`
| ToFloat(#Mean) => `mean`
| ToFloat(#Pdf(r)) => `pdf(${E.Float.toFixed(r)})`
| ToFloat(#Sample) => `sample`
| ToDist(Normalize) => `normalize`
| ToDist(ToPointSet) => `toPointSet`
| ToDist(ToSampleSet(r)) => `toSampleSet(${E.I.toString(r)})`
| ToDist(Truncate(_, _)) => `truncate`
| ToDist(Inspect) => `inspect`
| ToString => `toString`
| ToDistCombination(Algebraic, _, _) => `algebraic`
| ToDistCombination(Pointwise, _, _) => `pointwise`
}
let toString = (d: genericFunctionCallInfo): string =>
switch d {
| FromDist(f, _) | FromFloat(f, _) => distCallToString(f)
| Mixture(_) => `mixture`
}
}

View File

@ -30,7 +30,7 @@ let truncate: (
~toPointSetFn: toPointSetFn, ~toPointSetFn: toPointSetFn,
~leftCutoff: option<float>=?, ~leftCutoff: option<float>=?,
~rightCutoff: option<float>=?, ~rightCutoff: option<float>=?,
unit, unit
) => result<t, error> ) => result<t, error>
let algebraicCombination: ( let algebraicCombination: (

View File

@ -118,13 +118,14 @@ let combineShapesContinuousContinuous = (
| #Logarithm => (m1, m2) => log(m1) /. log(m2) | #Logarithm => (m1, m2) => log(m1) /. log(m2)
} // note: here, mInv2 = mean(1 / t2) ~= 1 / mean(t2) } // note: here, mInv2 = mean(1 / t2) ~= 1 / mean(t2)
// TODO: I don't know what the variances are for exponentatiation // TODO: Variances are for exponentatiation or logarithms are almost totally made up and very likely very wrong.
// converts the variances and means of the two inputs into the variance of the output // converts the variances and means of the two inputs into the variance of the output
let combineVariancesFn = switch op { let combineVariancesFn = switch op {
| #Add => (v1, v2, _, _) => v1 +. v2 | #Add => (v1, v2, _, _) => v1 +. v2
| #Subtract => (v1, v2, _, _) => v1 +. v2 | #Subtract => (v1, v2, _, _) => v1 +. v2
| #Multiply => (v1, v2, m1, m2) => v1 *. v2 +. v1 *. m2 ** 2. +. v2 *. m1 ** 2. | #Multiply => (v1, v2, m1, m2) => v1 *. v2 +. v1 *. m2 ** 2. +. v2 *. m1 ** 2.
| #Exponentiate => (v1, v2, m1, m2) => v1 *. v2 +. v1 *. m2 ** 2. +. v2 *. m1 ** 2. | #Exponentiate => (v1, v2, m1, m2) => v1 *. v2 +. v1 *. m2 ** 2. +. v2 *. m1 ** 2.
| #Logarithm => (v1, v2, m1, m2) => v1 *. v2 +. v1 *. m2 ** 2. +. v2 *. m1 ** 2.
| #Divide => (v1, vInv2, m1, mInv2) => v1 *. vInv2 +. v1 *. mInv2 ** 2. +. vInv2 *. m1 ** 2. | #Divide => (v1, vInv2, m1, mInv2) => v1 *. vInv2 +. v1 *. mInv2 ** 2. +. vInv2 *. m1 ** 2.
} }

View File

@ -1,6 +1,47 @@
open Distributions open Distributions
type t = PointSetTypes.continuousShape type t = PointSetTypes.continuousShape
module Analysis = {
let integrate = (
~indefiniteIntegralStepwise=(p, h1) => h1 *. p,
~indefiniteIntegralLinear=(p, a, b) => a *. p +. b *. p ** 2.0 /. 2.0,
t: t,
): float => {
let xs = t.xyShape.xs
let ys = t.xyShape.ys
E.A.reducei(xs, 0.0, (acc, _x, i) => {
let areaUnderIntegral = // TODO Take this switch statement out of the loop body
switch (t.interpolation, i) {
| (_, 0) => 0.0
| (#Stepwise, _) =>
indefiniteIntegralStepwise(xs[i], ys[i - 1]) -.
indefiniteIntegralStepwise(xs[i - 1], ys[i - 1])
| (#Linear, _) =>
let x1 = xs[i - 1]
let x2 = xs[i]
if x1 == x2 {
0.0
} else {
let h1 = ys[i - 1]
let h2 = ys[i]
let b = (h1 -. h2) /. (x1 -. x2)
let a = h1 -. b *. x1
indefiniteIntegralLinear(x2, a, b) -. indefiniteIntegralLinear(x1, a, b)
}
}
acc +. areaUnderIntegral
})
}
let getMeanOfSquares = (t: t) => {
let indefiniteIntegralLinear = (p, a, b) => a *. p ** 3.0 /. 3.0 +. b *. p ** 4.0 /. 4.0
let indefiniteIntegralStepwise = (p, h1) => h1 *. p ** 3.0 /. 3.0
integrate(~indefiniteIntegralStepwise, ~indefiniteIntegralLinear, t)
}
}
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=#Linear, ~integralSumCache=None, ~integralCache=None, xyShape): t => { let make = (~interpolation=#Linear, ~integralSumCache=None, ~integralCache=None, xyShape): t => {
@ -194,7 +235,7 @@ module T = Dist({
let indefiniteIntegralStepwise = (p, h1) => h1 *. p ** 2.0 /. 2.0 let indefiniteIntegralStepwise = (p, h1) => h1 *. p ** 2.0 /. 2.0
let indefiniteIntegralLinear = (p, a, b) => a *. p ** 2.0 /. 2.0 +. b *. p ** 3.0 /. 3.0 let indefiniteIntegralLinear = (p, a, b) => a *. p ** 2.0 /. 2.0 +. b *. p ** 3.0 /. 3.0
XYShape.Analysis.integrateContinuousShape( Analysis.integrate(
~indefiniteIntegralStepwise, ~indefiniteIntegralStepwise,
~indefiniteIntegralLinear, ~indefiniteIntegralLinear,
t, t,
@ -204,7 +245,7 @@ module T = Dist({
XYShape.Analysis.getVarianceDangerously( XYShape.Analysis.getVarianceDangerously(
t, t,
mean, mean,
XYShape.Analysis.getMeanOfSquaresContinuousShape, Analysis.getMeanOfSquares,
) )
}) })

View File

@ -209,8 +209,9 @@ module T = Dist({
let s = getShape(t) let s = getShape(t)
E.A.reducei(s.xs, 0.0, (acc, x, i) => acc +. x *. s.ys[i]) E.A.reducei(s.xs, 0.0, (acc, x, i) => acc +. x *. s.ys[i])
} }
let variance = (t: t): float => { let variance = (t: t): float => {
let getMeanOfSquares = t => t |> shapeMap(XYShape.Analysis.squareXYShape) |> mean let getMeanOfSquares = t => t |> shapeMap(XYShape.T.square) |> mean
XYShape.Analysis.getVarianceDangerously(t, mean, getMeanOfSquares) XYShape.Analysis.getVarianceDangerously(t, mean, getMeanOfSquares)
} }
}) })

View File

@ -213,8 +213,8 @@ module T = Dist({
let getMeanOfSquares = ({discrete, continuous}: t) => { let getMeanOfSquares = ({discrete, continuous}: t) => {
let discreteMean = let discreteMean =
discrete |> Discrete.shapeMap(XYShape.Analysis.squareXYShape) |> Discrete.T.mean discrete |> Discrete.shapeMap(XYShape.T.square) |> Discrete.T.mean
let continuousMean = continuous |> XYShape.Analysis.getMeanOfSquaresContinuousShape let continuousMean = continuous |> Continuous.Analysis.getMeanOfSquares
(discreteMean *. discreteIntegralSum +. continuousMean *. continuousIntegralSum) /. (discreteMean *. discreteIntegralSum +. continuousMean *. continuousIntegralSum) /.
totalIntegralSum totalIntegralSum
} }

View File

@ -0,0 +1,93 @@
type domainLimit = {
xPoint: float,
excludingProbabilityMass: float,
}
type domain =
| Complete
| LeftLimited(domainLimit)
| RightLimited(domainLimit)
| LeftAndRightLimited(domainLimit, domainLimit)
type distributionType = [
| #PDF
| #CDF
]
type xyShape = XYShape.xyShape;
type interpolationStrategy = XYShape.interpolationStrategy;
type extrapolationStrategy = XYShape.extrapolationStrategy;
type interpolator = XYShape.extrapolationStrategy;
type rec continuousShape = {
xyShape: xyShape,
interpolation: interpolationStrategy,
integralSumCache: option<float>,
integralCache: option<continuousShape>,
}
type discreteShape = {
xyShape: xyShape,
integralSumCache: option<float>,
integralCache: option<continuousShape>,
}
type mixedShape = {
continuous: continuousShape,
discrete: discreteShape,
integralSumCache: option<float>,
integralCache: option<continuousShape>,
}
type pointSetDistMonad<'a, 'b, 'c> =
| Mixed('a)
| Discrete('b)
| Continuous('c)
@genType
type pointSetDist = pointSetDistMonad<mixedShape, discreteShape, continuousShape>
module ShapeMonad = {
let fmap = (t: pointSetDistMonad<'a, 'b, 'c>, (fn1, fn2, fn3)): pointSetDistMonad<'d, 'e, 'f> =>
switch t {
| Mixed(m) => Mixed(fn1(m))
| Discrete(m) => Discrete(fn2(m))
| Continuous(m) => Continuous(fn3(m))
}
}
type generationSource =
| SquiggleString(string)
| Shape(pointSetDist)
@genType
type distPlus = {
pointSetDist: pointSetDist,
integralCache: continuousShape,
squiggleString: option<string>,
}
type mixedPoint = {
continuous: float,
discrete: float,
}
module MixedPoint = {
type t = mixedPoint
let toContinuousValue = (t: t) => t.continuous
let toDiscreteValue = (t: t) => t.discrete
let makeContinuous = (continuous: float): t => {continuous: continuous, discrete: 0.0}
let makeDiscrete = (discrete: float): t => {continuous: 0.0, discrete: discrete}
let fmap = (fn: float => float, t: t) => {
continuous: fn(t.continuous),
discrete: fn(t.discrete),
}
let combine2 = (fn, c: t, d: t): t => {
continuous: fn(c.continuous, d.continuous),
discrete: fn(c.discrete, d.discrete),
}
let add = combine2((a, b) => a +. b)
}

View File

@ -8,28 +8,22 @@ let make =
( (
~pointSetDist, ~pointSetDist,
~squiggleString, ~squiggleString,
~domain=Complete,
~unit=UnspecifiedDistribution,
(), (),
) )
: t => { : t => {
let integral = pointSetDistIntegral(pointSetDist); let integral = pointSetDistIntegral(pointSetDist);
{pointSetDist, domain, integralCache: integral, unit, squiggleString}; {pointSetDist, integralCache: integral, squiggleString};
}; };
let update = let update =
( (
~pointSetDist=?, ~pointSetDist=?,
~integralCache=?, ~integralCache=?,
~domain=?,
~unit=?,
~squiggleString=?, ~squiggleString=?,
t: t, t: t,
) => { ) => {
pointSetDist: E.O.default(t.pointSetDist, pointSetDist), pointSetDist: E.O.default(t.pointSetDist, pointSetDist),
integralCache: E.O.default(t.integralCache, integralCache), integralCache: E.O.default(t.integralCache, integralCache),
domain: E.O.default(t.domain, domain),
unit: E.O.default(t.unit, unit),
squiggleString: E.O.default(t.squiggleString, squiggleString), squiggleString: E.O.default(t.squiggleString, squiggleString),
}; };
@ -38,12 +32,6 @@ let updateShape = (pointSetDist, t) => {
update(~pointSetDist, ~integralCache, t); update(~pointSetDist, ~integralCache, t);
}; };
let domainIncludedProbabilityMass = (t: t) =>
Domain.includedProbabilityMass(t.domain);
let domainIncludedProbabilityMassAdjustment = (t: t, f) =>
f *. Domain.includedProbabilityMass(t.domain);
let toPointSetDist = ({pointSetDist, _}: t) => pointSetDist; let toPointSetDist = ({pointSetDist, _}: t) => pointSetDist;
let pointSetDistFn = (fn, {pointSetDist}: t) => fn(pointSetDist); let pointSetDistFn = (fn, {pointSetDist}: t) => fn(pointSetDist);
@ -73,8 +61,7 @@ module T =
let xToY = (f, t: t) => let xToY = (f, t: t) =>
t t
|> toPointSetDist |> toPointSetDist
|> PointSetDist.T.xToY(f) |> PointSetDist.T.xToY(f);
|> MixedPoint.fmap(domainIncludedProbabilityMassAdjustment(t));
let minX = pointSetDistFn(PointSetDist.T.minX); let minX = pointSetDistFn(PointSetDist.T.minX);
let maxX = pointSetDistFn(PointSetDist.T.maxX); let maxX = pointSetDistFn(PointSetDist.T.maxX);
@ -115,7 +102,6 @@ module T =
f, f,
toPointSetDist(t), toPointSetDist(t),
) )
|> domainIncludedProbabilityMassAdjustment(t);
}; };
// TODO: This part is broken when there is a limit, if this is supposed to be taken into account. // TODO: This part is broken when there is a limit, if this is supposed to be taken into account.

View File

@ -1,7 +1,7 @@
module ExpressionValue = ReducerInterface_ExpressionValue module ExpressionValue = ReducerInterface_ExpressionValue
type expressionValue = ReducerInterface_ExpressionValue.expressionValue type expressionValue = ReducerInterface_ExpressionValue.expressionValue
let runGenericOperation = GenericDist_GenericOperation.run( let runGenericOperation = DistributionOperation.run(
~env={ ~env={
sampleCount: 1000, sampleCount: 1000,
xyPointLength: 1000, xyPointLength: 1000,
@ -86,7 +86,7 @@ module SymbolicConstructors = {
let symbolicResultToOutput = ( let symbolicResultToOutput = (
symbolicResult: result<SymbolicDistTypes.symbolicDist, string>, symbolicResult: result<SymbolicDistTypes.symbolicDist, string>,
): option<GenericDist_GenericOperation.outputType> => ): option<DistributionOperation.outputType> =>
switch symbolicResult { switch symbolicResult {
| Ok(r) => Some(Dist(Symbolic(r))) | Ok(r) => Some(Dist(Symbolic(r)))
| Error(r) => Some(GenDistError(Other(r))) | Error(r) => Some(GenDistError(Other(r)))
@ -98,7 +98,7 @@ module Math = {
} }
let dispatchToGenericOutput = (call: ExpressionValue.functionCall): option< let dispatchToGenericOutput = (call: ExpressionValue.functionCall): option<
GenericDist_GenericOperation.outputType, DistributionOperation.outputType,
> => { > => {
let (fnName, args) = call let (fnName, args) = call
switch (fnName, args) { switch (fnName, args) {
@ -165,7 +165,7 @@ let dispatchToGenericOutput = (call: ExpressionValue.functionCall): option<
} }
} }
let genericOutputToReducerValue = (o: GenericDist_GenericOperation.outputType): result< let genericOutputToReducerValue = (o: DistributionOperation.outputType): result<
expressionValue, expressionValue,
Reducer_ErrorValue.errorValue, Reducer_ErrorValue.errorValue,
> => > =>

View File

@ -1,4 +1,22 @@
open PointSetTypes @genType
type xyShape = {
xs: array<float>,
ys: array<float>,
}
@genType
type interpolationStrategy = [
| #Stepwise
| #Linear
]
@genType
type extrapolationStrategy = [
| #UseZero
| #UseOutermostPoints
]
type interpolator = (xyShape, int, float) => float
let interpolate = (xMin: float, xMax: float, yMin: float, yMax: float, xIntended: float): float => { let interpolate = (xMin: float, xMax: float, yMin: float, yMax: float, xIntended: float): float => {
let minProportion = (xMax -. xIntended) /. (xMax -. xMin) let minProportion = (xMax -. xIntended) /. (xMax -. xMin)
@ -25,6 +43,7 @@ module T = {
let xTotalRange = (t: t) => maxX(t) -. minX(t) let xTotalRange = (t: t) => maxX(t) -. minX(t)
let mapX = (fn, t: t): t => {xs: E.A.fmap(fn, t.xs), ys: t.ys} 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 mapY = (fn, t: t): t => {xs: t.xs, ys: E.A.fmap(fn, t.ys)}
let square = mapX(x => x ** 2.0)
let zip = ({xs, ys}: t) => Belt.Array.zip(xs, ys) let zip = ({xs, ys}: t) => Belt.Array.zip(xs, ys)
let fromArray = ((xs, ys)): t => {xs: xs, ys: ys} let fromArray = ((xs, ys)): t => {xs: xs, ys: ys}
let fromArrays = (xs, ys): t => {xs: xs, ys: ys} let fromArrays = (xs, ys): t => {xs: xs, ys: ys}
@ -126,8 +145,8 @@ 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 = ( let continuousInterpolator = (
interpolation: PointSetTypes.interpolationStrategy, interpolation: interpolationStrategy,
extrapolation: PointSetTypes.extrapolationStrategy, extrapolation: extrapolationStrategy,
): interpolator => ): interpolator =>
switch (interpolation, extrapolation) { switch (interpolation, extrapolation) {
| (#Linear, #UseZero) => | (#Linear, #UseZero) =>
@ -392,49 +411,9 @@ let logScorePoint = (sampleCount, t1, t2) =>
|> E.O.fmap(Pairs.y) |> E.O.fmap(Pairs.y)
module Analysis = { module Analysis = {
let integrateContinuousShape = (
~indefiniteIntegralStepwise=(p, h1) => h1 *. p,
~indefiniteIntegralLinear=(p, a, b) => a *. p +. b *. p ** 2.0 /. 2.0,
t: PointSetTypes.continuousShape,
): float => {
let xs = t.xyShape.xs
let ys = t.xyShape.ys
E.A.reducei(xs, 0.0, (acc, _x, i) => {
let areaUnderIntegral = // TODO Take this switch statement out of the loop body
switch (t.interpolation, i) {
| (_, 0) => 0.0
| (#Stepwise, _) =>
indefiniteIntegralStepwise(xs[i], ys[i - 1]) -.
indefiniteIntegralStepwise(xs[i - 1], ys[i - 1])
| (#Linear, _) =>
let x1 = xs[i - 1]
let x2 = xs[i]
if x1 == x2 {
0.0
} else {
let h1 = ys[i - 1]
let h2 = ys[i]
let b = (h1 -. h2) /. (x1 -. x2)
let a = h1 -. b *. x1
indefiniteIntegralLinear(x2, a, b) -. indefiniteIntegralLinear(x1, a, b)
}
}
acc +. areaUnderIntegral
})
}
let getMeanOfSquaresContinuousShape = (t: PointSetTypes.continuousShape) => {
let indefiniteIntegralLinear = (p, a, b) => a *. p ** 3.0 /. 3.0 +. b *. p ** 4.0 /. 4.0
let indefiniteIntegralStepwise = (p, h1) => h1 *. p ** 3.0 /. 3.0
integrateContinuousShape(~indefiniteIntegralStepwise, ~indefiniteIntegralLinear, t)
}
let getVarianceDangerously = (t: 't, mean: 't => float, getMeanOfSquares: 't => float): float => { let getVarianceDangerously = (t: 't, mean: 't => float, getMeanOfSquares: 't => float): float => {
let meanSquared = mean(t) ** 2.0 let meanSquared = mean(t) ** 2.0
let meanOfSquares = getMeanOfSquares(t) let meanOfSquares = getMeanOfSquares(t)
meanOfSquares -. meanSquared meanOfSquares -. meanSquared
} }
let squareXYShape = T.mapX(x => x ** 2.0)
} }

View File

@ -1,154 +0,0 @@
type domainLimit = {
xPoint: float,
excludingProbabilityMass: float,
}
type domain =
| Complete
| LeftLimited(domainLimit)
| RightLimited(domainLimit)
| LeftAndRightLimited(domainLimit, domainLimit)
type distributionType = [
| #PDF
| #CDF
]
type xyShape = {
xs: array<float>,
ys: array<float>,
}
type interpolationStrategy = [
| #Stepwise
| #Linear
]
type extrapolationStrategy = [
| #UseZero
| #UseOutermostPoints
]
type interpolator = (xyShape, int, float) => float
type rec continuousShape = {
xyShape: xyShape,
interpolation: interpolationStrategy,
integralSumCache: option<float>,
integralCache: option<continuousShape>,
}
type discreteShape = {
xyShape: xyShape,
integralSumCache: option<float>,
integralCache: option<continuousShape>,
}
type mixedShape = {
continuous: continuousShape,
discrete: discreteShape,
integralSumCache: option<float>,
integralCache: option<continuousShape>,
}
type pointSetDistMonad<'a, 'b, 'c> =
| Mixed('a)
| Discrete('b)
| Continuous('c)
@genType
type pointSetDist = pointSetDistMonad<mixedShape, discreteShape, continuousShape>
module ShapeMonad = {
let fmap = (t: pointSetDistMonad<'a, 'b, 'c>, (fn1, fn2, fn3)): pointSetDistMonad<'d, 'e, 'f> =>
switch t {
| Mixed(m) => Mixed(fn1(m))
| Discrete(m) => Discrete(fn2(m))
| Continuous(m) => Continuous(fn3(m))
}
}
type generationSource =
| SquiggleString(string)
| Shape(pointSetDist)
type distributionUnit =
| UnspecifiedDistribution
@genType
type distPlus = {
pointSetDist: pointSetDist,
domain: domain,
integralCache: continuousShape,
unit: distributionUnit,
squiggleString: option<string>,
}
module DistributionUnit = {
let toJson = (distributionUnit: distributionUnit) =>
switch distributionUnit {
| _ => Js.Null.fromOption(None)
}
}
module Domain = {
let excludedProbabilityMass = (t: domain) =>
switch t {
| Complete => 0.0
| LeftLimited({excludingProbabilityMass}) => excludingProbabilityMass
| RightLimited({excludingProbabilityMass}) => excludingProbabilityMass
| LeftAndRightLimited({excludingProbabilityMass: l}, {excludingProbabilityMass: r}) => l +. r
}
let includedProbabilityMass = (t: domain) => 1.0 -. excludedProbabilityMass(t)
let initialProbabilityMass = (t: domain) =>
switch t {
| Complete
| RightLimited(_) => 0.0
| LeftLimited({excludingProbabilityMass}) => excludingProbabilityMass
| LeftAndRightLimited({excludingProbabilityMass}, _) => excludingProbabilityMass
}
let normalizeProbabilityMass = (t: domain) => 1. /. excludedProbabilityMass(t)
let yPointToSubYPoint = (t: domain, yPoint) =>
switch t {
| Complete => Some(yPoint)
| LeftLimited({excludingProbabilityMass}) if yPoint < excludingProbabilityMass => None
| LeftLimited({excludingProbabilityMass}) if yPoint >= excludingProbabilityMass =>
Some((yPoint -. excludingProbabilityMass) /. includedProbabilityMass(t))
| RightLimited({excludingProbabilityMass}) if yPoint > 1. -. excludingProbabilityMass => None
| RightLimited({excludingProbabilityMass}) if yPoint <= 1. -. excludingProbabilityMass =>
Some(yPoint /. includedProbabilityMass(t))
| LeftAndRightLimited({excludingProbabilityMass: l}, _) if yPoint < l => None
| LeftAndRightLimited(_, {excludingProbabilityMass: r}) if yPoint > 1.0 -. r => None
| LeftAndRightLimited({excludingProbabilityMass: l}, _) =>
Some((yPoint -. l) /. includedProbabilityMass(t))
| _ => None
}
}
type mixedPoint = {
continuous: float,
discrete: float,
}
module MixedPoint = {
type t = mixedPoint
let toContinuousValue = (t: t) => t.continuous
let toDiscreteValue = (t: t) => t.discrete
let makeContinuous = (continuous: float): t => {continuous: continuous, discrete: 0.0}
let makeDiscrete = (discrete: float): t => {continuous: 0.0, discrete: discrete}
let fmap = (fn: float => float, t: t) => {
continuous: fn(t.continuous),
discrete: fn(t.discrete),
}
let combine2 = (fn, c: t, d: t): t => {
continuous: fn(c.continuous, d.continuous),
discrete: fn(c.discrete, d.discrete),
}
let add = combine2((a, b) => a +. b)
}