Pulled out XYShape to be more separate

This commit is contained in:
Ozzie Gooen 2022-04-04 13:41:22 -04:00
parent 60b760f0cd
commit a2729f34cb
8 changed files with 164 additions and 65 deletions

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

@ -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: I don't know what the variances are for exponentatiation or logarithms
// 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

@ -14,21 +14,10 @@ type distributionType = [
| #CDF | #CDF
] ]
type xyShape = { type xyShape = XYShape.xyShape;
xs: array<float>, type interpolationStrategy = XYShape.interpolationStrategy;
ys: array<float>, type extrapolationStrategy = XYShape.extrapolationStrategy;
} type interpolator = XYShape.extrapolationStrategy;
type interpolationStrategy = [
| #Stepwise
| #Linear
]
type extrapolationStrategy = [
| #UseZero
| #UseOutermostPoints
]
type interpolator = (xyShape, int, float) => float
type rec continuousShape = { type rec continuousShape = {
xyShape: xyShape, xyShape: xyShape,

View File

@ -1,4 +1,18 @@
open PointSetTypes type xyShape = {
xs: array<float>,
ys: array<float>,
}
type interpolationStrategy = [
| #Stepwise
| #Linear
]
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 +39,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 +141,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 +407,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)
} }