From a2729f34cb103fb17f125e9ad49f9e63eb60228b Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Mon, 4 Apr 2022 13:41:22 -0400 Subject: [PATCH] Pulled out XYShape to be more separate --- packages/squiggle-lang/src/js/index.ts | 2 +- .../Distributions/DistributionTypes.res | 92 +++++++++++++++++++ .../AlgebraicShapeCombination.res | 3 +- .../Distributions/PointSetDist/Continuous.res | 45 ++++++++- .../Distributions/PointSetDist/Discrete.res | 3 +- .../Distributions/PointSetDist/Mixed.res | 4 +- .../PointSetDist/PointSetTypes.res | 19 +--- .../PointSetDist => utility}/XYShape.res | 61 ++++-------- 8 files changed, 164 insertions(+), 65 deletions(-) create mode 100644 packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res rename packages/squiggle-lang/src/rescript/{Distributions/PointSetDist => utility}/XYShape.res (90%) diff --git a/packages/squiggle-lang/src/js/index.ts b/packages/squiggle-lang/src/js/index.ts index cee2987d..7856ef6f 100644 --- a/packages/squiggle-lang/src/js/index.ts +++ b/packages/squiggle-lang/src/js/index.ts @@ -1,7 +1,7 @@ import {runAll} 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 {t as DistPlus} from '../rescript/pointSetDist/DistPlus.gen'; +export type {t as DistPlus} from '../rescript/OldInterpreter/DistPlus.gen'; export let defaultSamplingInputs : SamplingInputs = { sampleCount : 10000, diff --git a/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res b/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res new file mode 100644 index 00000000..a3e249d3 --- /dev/null +++ b/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res @@ -0,0 +1,92 @@ +type genericDist = + | PointSet(PointSetTypes.pointSetDist) + | SampleSet(array) + | 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, option) + | 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` + } +} diff --git a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/AlgebraicShapeCombination.res b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/AlgebraicShapeCombination.res index 9cfdc66d..d94bd4a5 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/AlgebraicShapeCombination.res +++ b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/AlgebraicShapeCombination.res @@ -118,13 +118,14 @@ let combineShapesContinuousContinuous = ( | #Logarithm => (m1, m2) => log(m1) /. log(m2) } // 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 let combineVariancesFn = switch op { | #Add => (v1, v2, _, _) => v1 +. v2 | #Subtract => (v1, v2, _, _) => v1 +. v2 | #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. + | #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. } diff --git a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Continuous.res b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Continuous.res index 92654b35..f01457b7 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Continuous.res +++ b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Continuous.res @@ -1,6 +1,47 @@ open Distributions 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 interpolation = (t: t) => t.interpolation 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 indefiniteIntegralLinear = (p, a, b) => a *. p ** 2.0 /. 2.0 +. b *. p ** 3.0 /. 3.0 - XYShape.Analysis.integrateContinuousShape( + Analysis.integrate( ~indefiniteIntegralStepwise, ~indefiniteIntegralLinear, t, @@ -204,7 +245,7 @@ module T = Dist({ XYShape.Analysis.getVarianceDangerously( t, mean, - XYShape.Analysis.getMeanOfSquaresContinuousShape, + Analysis.getMeanOfSquares, ) }) diff --git a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Discrete.res b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Discrete.res index 3e2cc2ce..3b689453 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Discrete.res +++ b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Discrete.res @@ -209,8 +209,9 @@ module T = Dist({ let s = getShape(t) E.A.reducei(s.xs, 0.0, (acc, x, i) => acc +. x *. s.ys[i]) } + 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) } }) \ No newline at end of file diff --git a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Mixed.res b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Mixed.res index cdccdeb2..e05bd408 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Mixed.res +++ b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Mixed.res @@ -213,8 +213,8 @@ module T = Dist({ let getMeanOfSquares = ({discrete, continuous}: t) => { let discreteMean = - discrete |> Discrete.shapeMap(XYShape.Analysis.squareXYShape) |> Discrete.T.mean - let continuousMean = continuous |> XYShape.Analysis.getMeanOfSquaresContinuousShape + discrete |> Discrete.shapeMap(XYShape.T.square) |> Discrete.T.mean + let continuousMean = continuous |> Continuous.Analysis.getMeanOfSquares (discreteMean *. discreteIntegralSum +. continuousMean *. continuousIntegralSum) /. totalIntegralSum } diff --git a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetTypes.res b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetTypes.res index 15fbd4c3..2d7947c0 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetTypes.res +++ b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetTypes.res @@ -14,21 +14,10 @@ type distributionType = [ | #CDF ] -type xyShape = { - xs: array, - ys: array, -} - -type interpolationStrategy = [ - | #Stepwise - | #Linear -] -type extrapolationStrategy = [ - | #UseZero - | #UseOutermostPoints -] - -type interpolator = (xyShape, int, float) => float +type xyShape = XYShape.xyShape; +type interpolationStrategy = XYShape.interpolationStrategy; +type extrapolationStrategy = XYShape.extrapolationStrategy; +type interpolator = XYShape.extrapolationStrategy; type rec continuousShape = { xyShape: xyShape, diff --git a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/XYShape.res b/packages/squiggle-lang/src/rescript/utility/XYShape.res similarity index 90% rename from packages/squiggle-lang/src/rescript/Distributions/PointSetDist/XYShape.res rename to packages/squiggle-lang/src/rescript/utility/XYShape.res index 6cadec60..ec2df9f8 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/XYShape.res +++ b/packages/squiggle-lang/src/rescript/utility/XYShape.res @@ -1,4 +1,18 @@ -open PointSetTypes +type xyShape = { + xs: array, + ys: array, +} + +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 minProportion = (xMax -. xIntended) /. (xMax -. xMin) @@ -25,6 +39,7 @@ module 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 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 fromArray = ((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. Interpolation can either be stepwise (using the value on the left) or linear. Extrapolation can be `UseZero or `UseOutermostPoints. */ let continuousInterpolator = ( - interpolation: PointSetTypes.interpolationStrategy, - extrapolation: PointSetTypes.extrapolationStrategy, + interpolation: interpolationStrategy, + extrapolation: extrapolationStrategy, ): interpolator => switch (interpolation, extrapolation) { | (#Linear, #UseZero) => @@ -392,49 +407,9 @@ let logScorePoint = (sampleCount, t1, t2) => |> E.O.fmap(Pairs.y) 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 meanSquared = mean(t) ** 2.0 let meanOfSquares = getMeanOfSquares(t) meanOfSquares -. meanSquared } - - let squareXYShape = T.mapX(x => x ** 2.0) }