From 508f6738730366614eb5326ecb0b647fed2f44f2 Mon Sep 17 00:00:00 2001 From: Sam Nolan Date: Fri, 22 Apr 2022 16:27:17 -0400 Subject: [PATCH 01/11] Change NaN operations to results - Also deleting old GenericDist_Types - Also removing some string errors --- .../DistributionOperation_test.res | 2 +- .../Distributions/GenericDist_Fixtures.res | 22 +- .../Invariants/AlgebraicCombination_test.res | 42 ++-- .../Distributions/Invariants/Means_test.res | 2 +- .../ReducerInterface_Distribution_test.res | 12 +- .../__tests__/TS/Symbolic_test.ts | 2 +- .../DistributionOperation.res | 19 +- .../DistributionOperation.resi | 10 +- .../Distributions/DistributionTypes.res | 118 ++++++++++- .../Distributions/GenericDist/GenericDist.res | 38 ++-- .../GenericDist/GenericDist.resi | 14 +- .../GenericDist/GenericDist_Types.res | 194 ------------------ .../Distributions/PointSetDist/Continuous.res | 23 ++- .../Distributions/PointSetDist/Discrete.res | 22 +- .../PointSetDist/Distributions.res | 7 + .../Distributions/PointSetDist/Mixed.res | 42 +++- .../PointSetDist/PointSetDist.res | 34 ++- .../SampleSetDist/SampleSetDist.res | 40 +++- .../SampleSetDist_ToPointSet.res | 2 +- .../SymbolicDist/SymbolicDist.res | 2 +- .../SymbolicDist/SymbolicDistTypes.res | 2 +- .../rescript/Reducer/Reducer_ErrorValue.res | 2 + .../ReducerInterface_ExpressionValue.res | 2 +- .../ReducerInterface_GenericDistribution.res | 51 ++--- .../src/rescript/TypescriptInterface.res | 2 +- .../squiggle-lang/src/rescript/Utility/E.res | 12 +- .../src/rescript/Utility/Operation.res | 71 +++++-- .../src/rescript/Utility/XYShape.res | 4 + 28 files changed, 459 insertions(+), 334 deletions(-) delete mode 100644 packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist_Types.res diff --git a/packages/squiggle-lang/__tests__/Distributions/DistributionOperation_test.res b/packages/squiggle-lang/__tests__/Distributions/DistributionOperation_test.res index 078cad05..60e74d74 100644 --- a/packages/squiggle-lang/__tests__/Distributions/DistributionOperation_test.res +++ b/packages/squiggle-lang/__tests__/Distributions/DistributionOperation_test.res @@ -30,7 +30,7 @@ let toExt: option<'a> => 'a = E.O.toExt( describe("sparkline", () => { let runTest = ( name: string, - dist: GenericDist_Types.genericDist, + dist: DistributionTypes.genericDist, expected: DistributionOperation.outputType, ) => { test(name, () => { diff --git a/packages/squiggle-lang/__tests__/Distributions/GenericDist_Fixtures.res b/packages/squiggle-lang/__tests__/Distributions/GenericDist_Fixtures.res index a967b924..4d41930f 100644 --- a/packages/squiggle-lang/__tests__/Distributions/GenericDist_Fixtures.res +++ b/packages/squiggle-lang/__tests__/Distributions/GenericDist_Fixtures.res @@ -1,14 +1,14 @@ -let normalDist5: GenericDist_Types.genericDist = Symbolic(#Normal({mean: 5.0, stdev: 2.0})) -let normalDist10: GenericDist_Types.genericDist = Symbolic(#Normal({mean: 10.0, stdev: 2.0})) -let normalDist20: GenericDist_Types.genericDist = Symbolic(#Normal({mean: 20.0, stdev: 2.0})) -let normalDist: GenericDist_Types.genericDist = normalDist5 +let normalDist5: DistributionTypes.genericDist = Symbolic(#Normal({mean: 5.0, stdev: 2.0})) +let normalDist10: DistributionTypes.genericDist = Symbolic(#Normal({mean: 10.0, stdev: 2.0})) +let normalDist20: DistributionTypes.genericDist = Symbolic(#Normal({mean: 20.0, stdev: 2.0})) +let normalDist: DistributionTypes.genericDist = normalDist5 -let betaDist: GenericDist_Types.genericDist = Symbolic(#Beta({alpha: 2.0, beta: 5.0})) -let lognormalDist: GenericDist_Types.genericDist = Symbolic(#Lognormal({mu: 0.0, sigma: 1.0})) -let cauchyDist: GenericDist_Types.genericDist = Symbolic(#Cauchy({local: 1.0, scale: 1.0})) -let triangularDist: GenericDist_Types.genericDist = Symbolic( +let betaDist: DistributionTypes.genericDist = Symbolic(#Beta({alpha: 2.0, beta: 5.0})) +let lognormalDist: DistributionTypes.genericDist = Symbolic(#Lognormal({mu: 0.0, sigma: 1.0})) +let cauchyDist: DistributionTypes.genericDist = Symbolic(#Cauchy({local: 1.0, scale: 1.0})) +let triangularDist: DistributionTypes.genericDist = Symbolic( #Triangular({low: 1.0, medium: 2.0, high: 3.0}), ) -let exponentialDist: GenericDist_Types.genericDist = Symbolic(#Exponential({rate: 2.0})) -let uniformDist: GenericDist_Types.genericDist = Symbolic(#Uniform({low: 9.0, high: 10.0})) -let floatDist: GenericDist_Types.genericDist = Symbolic(#Float(1e1)) +let exponentialDist: DistributionTypes.genericDist = Symbolic(#Exponential({rate: 2.0})) +let uniformDist: DistributionTypes.genericDist = Symbolic(#Uniform({low: 9.0, high: 10.0})) +let floatDist: DistributionTypes.genericDist = Symbolic(#Float(1e1)) diff --git a/packages/squiggle-lang/__tests__/Distributions/Invariants/AlgebraicCombination_test.res b/packages/squiggle-lang/__tests__/Distributions/Invariants/AlgebraicCombination_test.res index c7e0ac46..718a4e14 100644 --- a/packages/squiggle-lang/__tests__/Distributions/Invariants/AlgebraicCombination_test.res +++ b/packages/squiggle-lang/__tests__/Distributions/Invariants/AlgebraicCombination_test.res @@ -43,7 +43,7 @@ describe("(Algebraic) addition of distributions", () => { test("normal(mean=5) + normal(mean=20)", () => { normalDist5 ->algebraicAdd(normalDist20) - ->E.R2.fmap(GenericDist_Types.Constructors.UsingDists.mean) + ->E.R2.fmap(DistributionTypes.Constructors.UsingDists.mean) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) ->E.R.toExn @@ -57,7 +57,7 @@ describe("(Algebraic) addition of distributions", () => { let received = uniformDist ->algebraicAdd(betaDist) - ->E.R2.fmap(GenericDist_Types.Constructors.UsingDists.mean) + ->E.R2.fmap(DistributionTypes.Constructors.UsingDists.mean) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) ->E.R.toExn @@ -74,7 +74,7 @@ describe("(Algebraic) addition of distributions", () => { let received = betaDist ->algebraicAdd(uniformDist) - ->E.R2.fmap(GenericDist_Types.Constructors.UsingDists.mean) + ->E.R2.fmap(DistributionTypes.Constructors.UsingDists.mean) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) ->E.R.toExn @@ -95,7 +95,7 @@ describe("(Algebraic) addition of distributions", () => { let received = normalDist10 // this should be normal(10, sqrt(8)) ->Ok - ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.pdf(d, x)) + ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.pdf(d, x)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) ->E.R.toOption @@ -103,7 +103,7 @@ describe("(Algebraic) addition of distributions", () => { let calculated = normalDist5 ->algebraicAdd(normalDist5) - ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.pdf(d, x)) + ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.pdf(d, x)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) ->E.R.toOption @@ -126,7 +126,7 @@ describe("(Algebraic) addition of distributions", () => { let received = normalDist20 ->Ok - ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.pdf(d, 1.9e1)) + ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.pdf(d, 1.9e1)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) ->E.R.toOption @@ -134,7 +134,7 @@ describe("(Algebraic) addition of distributions", () => { let calculated = normalDist10 ->algebraicAdd(normalDist10) - ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.pdf(d, 1.9e1)) + ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.pdf(d, 1.9e1)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) ->E.R.toOption @@ -155,7 +155,7 @@ describe("(Algebraic) addition of distributions", () => { let received = uniformDist ->algebraicAdd(betaDist) - ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.pdf(d, 1e1)) + ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.pdf(d, 1e1)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) ->E.R.toExn @@ -170,7 +170,7 @@ describe("(Algebraic) addition of distributions", () => { let received = betaDist ->algebraicAdd(uniformDist) - ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.pdf(d, 1e1)) + ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.pdf(d, 1e1)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) ->E.R.toExn @@ -187,7 +187,7 @@ describe("(Algebraic) addition of distributions", () => { let received = normalDist10 ->Ok - ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.cdf(d, x)) + ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.cdf(d, x)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) ->E.R.toOption @@ -195,7 +195,7 @@ describe("(Algebraic) addition of distributions", () => { let calculated = normalDist5 ->algebraicAdd(normalDist5) - ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.cdf(d, x)) + ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.cdf(d, x)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) ->E.R.toOption @@ -217,7 +217,7 @@ describe("(Algebraic) addition of distributions", () => { let received = normalDist20 ->Ok - ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.cdf(d, 1.25e1)) + ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.cdf(d, 1.25e1)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) ->E.R.toOption @@ -225,7 +225,7 @@ describe("(Algebraic) addition of distributions", () => { let calculated = normalDist10 ->algebraicAdd(normalDist10) - ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.cdf(d, 1.25e1)) + ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.cdf(d, 1.25e1)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) ->E.R.toOption @@ -246,7 +246,7 @@ describe("(Algebraic) addition of distributions", () => { let received = uniformDist ->algebraicAdd(betaDist) - ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.cdf(d, 1e1)) + ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.cdf(d, 1e1)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) ->E.R.toExn @@ -261,7 +261,7 @@ describe("(Algebraic) addition of distributions", () => { let received = betaDist ->algebraicAdd(uniformDist) - ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.cdf(d, 1e1)) + ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.cdf(d, 1e1)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) ->E.R.toExn @@ -279,7 +279,7 @@ describe("(Algebraic) addition of distributions", () => { let received = normalDist10 ->Ok - ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.inv(d, x)) + ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.inv(d, x)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) ->E.R.toOption @@ -287,7 +287,7 @@ describe("(Algebraic) addition of distributions", () => { let calculated = normalDist5 ->algebraicAdd(normalDist5) - ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.inv(d, x)) + ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.inv(d, x)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) ->E.R.toOption @@ -309,7 +309,7 @@ describe("(Algebraic) addition of distributions", () => { let received = normalDist20 ->Ok - ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.inv(d, 1e-1)) + ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.inv(d, 1e-1)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) ->E.R.toOption @@ -317,7 +317,7 @@ describe("(Algebraic) addition of distributions", () => { let calculated = normalDist10 ->algebraicAdd(normalDist10) - ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.inv(d, 1e-1)) + ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.inv(d, 1e-1)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) ->E.R.toOption @@ -338,7 +338,7 @@ describe("(Algebraic) addition of distributions", () => { let received = uniformDist ->algebraicAdd(betaDist) - ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.inv(d, 2e-2)) + ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.inv(d, 2e-2)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) ->E.R.toExn @@ -353,7 +353,7 @@ describe("(Algebraic) addition of distributions", () => { let received = betaDist ->algebraicAdd(uniformDist) - ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.inv(d, 2e-2)) + ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.inv(d, 2e-2)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) ->E.R.toExn diff --git a/packages/squiggle-lang/__tests__/Distributions/Invariants/Means_test.res b/packages/squiggle-lang/__tests__/Distributions/Invariants/Means_test.res index bcc461bc..3806c7f9 100644 --- a/packages/squiggle-lang/__tests__/Distributions/Invariants/Means_test.res +++ b/packages/squiggle-lang/__tests__/Distributions/Invariants/Means_test.res @@ -15,7 +15,7 @@ open TestHelpers module Internals = { let epsilon = 5e1 - let mean = GenericDist_Types.Constructors.UsingDists.mean + let mean = DistributionTypes.Constructors.UsingDists.mean let expectImpossiblePath: string => assertion = algebraicOp => `${algebraicOp} has`->expect->toEqual("failed") diff --git a/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res b/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res index d3defb40..65a76b88 100644 --- a/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res +++ b/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res @@ -22,7 +22,7 @@ describe("eval on distribution functions", () => { testEval("mean(-normal(5,2))", "Ok(-5)") }) describe("to", () => { - testEval("5 to 2", "Error(TODO: Low value must be less than high value.)") + testEval("5 to 2", "Error(Math Error: Low value must be less than high value.)") testEval("to(2,5)", "Ok(Lognormal(1.1512925464970227,0.27853260523016377))") testEval("to(-2,2)", "Ok(Normal(0,1.2159136638235384))") }) @@ -88,8 +88,14 @@ describe("eval on distribution functions", () => { describe("log", () => { testEval("log(2, uniform(5,8))", "Ok(Sample Set Distribution)") - testEval("log(normal(5,2), 3)", "Ok(Sample Set Distribution)") - testEval("log(normal(5,2), normal(10,1))", "Ok(Sample Set Distribution)") + testEval( + "log(normal(5,2), 3)", + "Error(Math Error: Operation Error: Operation returned complex result)", + ) + testEval( + "log(normal(5,2), normal(10,1))", + "Error(Math Error: Operation Error: Operation returned complex result)", + ) testEval("log(uniform(5,8))", "Ok(Sample Set Distribution)") testEval("log10(uniform(5,8))", "Ok(Sample Set Distribution)") }) diff --git a/packages/squiggle-lang/__tests__/TS/Symbolic_test.ts b/packages/squiggle-lang/__tests__/TS/Symbolic_test.ts index 71d87642..1bcb1e2e 100644 --- a/packages/squiggle-lang/__tests__/TS/Symbolic_test.ts +++ b/packages/squiggle-lang/__tests__/TS/Symbolic_test.ts @@ -12,7 +12,7 @@ describe("Symbolic mean", () => { expect(squiggleResult.value).toBeCloseTo((x + y + z) / 3); } catch (err) { expect((err as Error).message).toEqual( - "Expected squiggle expression to evaluate but got error: TODO: Triangular values must be increasing order." + "Expected squiggle expression to evaluate but got error: Math Error: Triangular values must be increasing order." ); } } diff --git a/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.res b/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.res index 44584791..77d6acc5 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.res +++ b/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.res @@ -1,4 +1,4 @@ -type functionCallInfo = GenericDist_Types.Operation.genericFunctionCallInfo +type functionCallInfo = DistributionTypes.DistributionOperation.genericFunctionCallInfo type genericDist = DistributionTypes.genericDist type error = DistributionTypes.error @@ -120,7 +120,10 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => { (), )->OutputLocal.toDistR - let fromDistFn = (subFnName: GenericDist_Types.Operation.fromDist, dist: genericDist) => { + let fromDistFn = ( + subFnName: DistributionTypes.DistributionOperation.fromDist, + dist: genericDist, + ) => { let response = switch subFnName { | ToFloat(distToFloatOperation) => GenericDist.toFloatOperation(dist, ~toPointSetFn, ~distToFloatOperation) @@ -162,9 +165,9 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => { ->GenericDist.pointwiseCombination(~toPointSetFn, ~arithmeticOperation, ~t2) ->E.R2.fmap(r => Dist(r)) ->OutputLocal.fromResult - | ToDistCombination(Pointwise, arithmeticOperation, #Float(float)) => + | ToDistCombination(Pointwise, arithmeticOperation, #Float(f)) => dist - ->GenericDist.pointwiseCombinationFloat(~toPointSetFn, ~arithmeticOperation, ~float) + ->GenericDist.pointwiseCombinationFloat(~toPointSetFn, ~arithmeticOperation, ~f) ->E.R2.fmap(r => Dist(r)) ->OutputLocal.fromResult } @@ -192,7 +195,7 @@ module Output = { let fmap = ( ~env, input: outputType, - functionCallInfo: GenericDist_Types.Operation.singleParamaterFunction, + functionCallInfo: DistributionTypes.DistributionOperation.singleParamaterFunction, ): outputType => { let newFnCall: result = switch (functionCallInfo, input) { | (FromDist(fromDist), Dist(o)) => Ok(FromDist(fromDist, o)) @@ -205,11 +208,11 @@ module Output = { } } -// See comment above GenericDist_Types.Constructors to explain the purpose of this module. +// See comment above DistributionTypes.Constructors to explain the purpose of this module. // I tried having another internal module called UsingDists, similar to how its done in -// GenericDist_Types.Constructors. However, this broke GenType for me, so beware. +// DistributionTypes.Constructors. However, this broke GenType for me, so beware. module Constructors = { - module C = GenericDist_Types.Constructors.UsingDists + module C = DistributionTypes.Constructors.UsingDists open OutputLocal let mean = (~env, dist) => C.mean(dist)->run(~env)->toFloatR let sample = (~env, dist) => C.sample(dist)->run(~env)->toFloatR diff --git a/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.resi b/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.resi index 9b21a04e..5ad34354 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.resi +++ b/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.resi @@ -4,7 +4,7 @@ type env = { xyPointLength: int, } -open GenericDist_Types +open DistributionTypes @genType type outputType = @@ -15,15 +15,15 @@ type outputType = | GenDistError(error) @genType -let run: (~env: env, GenericDist_Types.Operation.genericFunctionCallInfo) => outputType +let run: (~env: env, DistributionTypes.DistributionOperation.genericFunctionCallInfo) => outputType let runFromDist: ( ~env: env, - ~functionCallInfo: GenericDist_Types.Operation.fromDist, + ~functionCallInfo: DistributionTypes.DistributionOperation.fromDist, genericDist, ) => outputType let runFromFloat: ( ~env: env, - ~functionCallInfo: GenericDist_Types.Operation.fromDist, + ~functionCallInfo: DistributionTypes.DistributionOperation.fromDist, float, ) => outputType @@ -38,7 +38,7 @@ module Output: { let toBool: t => option let toBoolR: t => result let toError: t => option - let fmap: (~env: env, t, GenericDist_Types.Operation.singleParamaterFunction) => t + let fmap: (~env: env, t, DistributionTypes.DistributionOperation.singleParamaterFunction) => t } module Constructors: { diff --git a/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res b/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res index 4082cb6e..b171fa95 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res +++ b/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res @@ -9,9 +9,43 @@ type error = | NotYetImplemented | Unreachable | DistributionVerticalShiftIsInvalid + | TooFewSamples | ArgumentError(string) + | OperationError(Operation.invalidOperationError) + | PointSetConversionError(SampleSetDist.pointsetConversionError) + | SparklineError(PointSetDist.sparklineError) // This type of error is for when we find a sparkline of a discrete distribution. This should probably at some point be actually implemented | Other(string) +let sampleErrorToDistErr = (err: SampleSetDist.sampleSetError): error => + switch err { + | TooFewSamples => TooFewSamples + } + +@genType +module Error = { + type t = error + + let fromString = (s: string): t => Other(s) + + @genType + let toString = (err: error): string => + switch err { + | NotYetImplemented => "Function Not Yet Implemented" + | Unreachable => "Unreachable" + | DistributionVerticalShiftIsInvalid => "Distribution Vertical Shift is Invalid" + | ArgumentError(s) => `Argument Error ${s}` + | TooFewSamples => "Too Few Samples" + | OperationError(err) => `Operation Error: ${Operation.invalidOperationErrorToString(err)}` + | PointSetConversionError(err) => SampleSetDist.pointsetConversionErrorToString(err) + | SparklineError(err) => PointSetDist.sparklineErrorToString(err) + | Other(s) => s + } + + let resultStringToResultError: result<'a, string> => result<'a, error> = n => + n->E.R2.errMap(r => r->fromString) +} + +@genType module Operation = { type direction = | Algebraic @@ -43,6 +77,9 @@ module Operation = { | #Mean | #Sample ] + + @genType + type pointsetXSelection = [#Linear | #ByWeight] } module DistributionOperation = { @@ -55,6 +92,12 @@ module DistributionOperation = { type toFloatArray = Sample(int) + type toBool = IsNormalized + + type toString = + | ToString + | ToSparkline(int) + type fromDist = | ToFloat(Operation.toFloat) | ToDist(toDist) @@ -63,7 +106,8 @@ module DistributionOperation = { Operation.arithmeticOperation, [#Dist(genericDist) | #Float(float)], ) - | ToString + | ToString(toString) + | ToBool(toBool) type singleParamaterFunction = | FromDist(fromDist) @@ -86,7 +130,9 @@ module DistributionOperation = { | ToDist(ToSampleSet(r)) => `toSampleSet(${E.I.toString(r)})` | ToDist(Truncate(_, _)) => `truncate` | ToDist(Inspect) => `inspect` - | ToString => `toString` + | ToString(ToString) => `toString` + | ToString(ToSparkline(n)) => `toSparkline(${E.I.toString(n)})` + | ToBool(IsNormalized) => `isNormalized` | ToDistCombination(Algebraic, _, _) => `algebraic` | ToDistCombination(Pointwise, _, _) => `pointwise` } @@ -97,3 +143,71 @@ module DistributionOperation = { | Mixture(_) => `mixture` } } +module Constructors = { + type t = DistributionOperation.genericFunctionCallInfo + + module UsingDists = { + @genType + let mean = (dist): t => FromDist(ToFloat(#Mean), dist) + let sample = (dist): t => FromDist(ToFloat(#Sample), dist) + let cdf = (dist, x): t => FromDist(ToFloat(#Cdf(x)), dist) + let inv = (dist, x): t => FromDist(ToFloat(#Inv(x)), dist) + let pdf = (dist, x): t => FromDist(ToFloat(#Pdf(x)), dist) + let normalize = (dist): t => FromDist(ToDist(Normalize), dist) + let isNormalized = (dist): t => FromDist(ToBool(IsNormalized), dist) + let toPointSet = (dist): t => FromDist(ToDist(ToPointSet), dist) + let toSampleSet = (dist, r): t => FromDist(ToDist(ToSampleSet(r)), dist) + let truncate = (dist, left, right): t => FromDist(ToDist(Truncate(left, right)), dist) + let inspect = (dist): t => FromDist(ToDist(Inspect), dist) + let toString = (dist): t => FromDist(ToString(ToString), dist) + let toSparkline = (dist, n): t => FromDist(ToString(ToSparkline(n)), dist) + let algebraicAdd = (dist1, dist2: genericDist): t => FromDist( + ToDistCombination(Algebraic, #Add, #Dist(dist2)), + dist1, + ) + let algebraicMultiply = (dist1, dist2): t => FromDist( + ToDistCombination(Algebraic, #Multiply, #Dist(dist2)), + dist1, + ) + let algebraicDivide = (dist1, dist2): t => FromDist( + ToDistCombination(Algebraic, #Divide, #Dist(dist2)), + dist1, + ) + let algebraicSubtract = (dist1, dist2): t => FromDist( + ToDistCombination(Algebraic, #Subtract, #Dist(dist2)), + dist1, + ) + let algebraicLogarithm = (dist1, dist2): t => FromDist( + ToDistCombination(Algebraic, #Logarithm, #Dist(dist2)), + dist1, + ) + let algebraicPower = (dist1, dist2): t => FromDist( + ToDistCombination(Algebraic, #Power, #Dist(dist2)), + dist1, + ) + let pointwiseAdd = (dist1, dist2): t => FromDist( + ToDistCombination(Pointwise, #Add, #Dist(dist2)), + dist1, + ) + let pointwiseMultiply = (dist1, dist2): t => FromDist( + ToDistCombination(Pointwise, #Multiply, #Dist(dist2)), + dist1, + ) + let pointwiseDivide = (dist1, dist2): t => FromDist( + ToDistCombination(Pointwise, #Divide, #Dist(dist2)), + dist1, + ) + let pointwiseSubtract = (dist1, dist2): t => FromDist( + ToDistCombination(Pointwise, #Subtract, #Dist(dist2)), + dist1, + ) + let pointwiseLogarithm = (dist1, dist2): t => FromDist( + ToDistCombination(Pointwise, #Logarithm, #Dist(dist2)), + dist1, + ) + let pointwisePower = (dist1, dist2): t => FromDist( + ToDistCombination(Pointwise, #Power, #Dist(dist2)), + dist1, + ) + } +} diff --git a/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.res b/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.res index 5e62de53..5a195f4b 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.res +++ b/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.res @@ -14,7 +14,7 @@ let sampleN = (t: t, n) => } let toSampleSetDist = (t: t, n) => - SampleSetDist.make(sampleN(t, n))->GenericDist_Types.Error.resultStringToResultError + SampleSetDist.make(sampleN(t, n))->E.R2.errMap(DistributionTypes.sampleErrorToDistErr) let fromFloat = (f: float): t => Symbolic(SymbolicDist.Float.make(f)) @@ -68,7 +68,7 @@ let toPointSet = ( t, ~xyPointLength, ~sampleCount, - ~xSelection: GenericDist_Types.Operation.pointsetXSelection=#ByWeight, + ~xSelection: DistributionTypes.Operation.pointsetXSelection=#ByWeight, (), ): result => { switch (t: t) { @@ -83,7 +83,7 @@ let toPointSet = ( pointSetDistLength: xyPointLength, kernelWidth: None, }, - )->GenericDist_Types.Error.resultStringToResultError + )->E.R2.errMap(x => DistributionTypes.PointSetConversionError(x)) } } @@ -97,7 +97,7 @@ let toSparkline = (t: t, ~sampleCount: int, ~bucketCount: int=20, ()): resulttoPointSet(~xSelection=#Linear, ~xyPointLength=bucketCount * 3, ~sampleCount, ()) ->E.R.bind(r => - r->PointSetDist.toSparkline(bucketCount)->GenericDist_Types.Error.resultStringToResultError + r->PointSetDist.toSparkline(bucketCount)->E.R2.errMap(x => DistributionTypes.SparklineError(x)) ) module Truncate = { @@ -148,10 +148,10 @@ let truncate = Truncate.run */ module AlgebraicCombination = { let tryAnalyticalSimplification = ( - arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation, + arithmeticOperation: DistributionTypes.Operation.arithmeticOperation, t1: t, t2: t, - ): option> => + ): option> => switch (arithmeticOperation, t1, t2) { | (arithmeticOperation, Symbolic(d1), Symbolic(d2)) => switch SymbolicDist.T.tryAnalyticalSimplification(d1, d2, arithmeticOperation) { @@ -174,14 +174,14 @@ module AlgebraicCombination = { let runMonteCarlo = ( toSampleSet: toSampleSetFn, - arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation, + arithmeticOperation: DistributionTypes.Operation.arithmeticOperation, t1: t, t2: t, - ) => { + ): result => { let fn = Operation.Algebraic.toFn(arithmeticOperation) E.R.merge(toSampleSet(t1), toSampleSet(t2)) ->E.R.bind(((t1, t2)) => { - SampleSetDist.map2(~fn, ~t1, ~t2)->GenericDist_Types.Error.resultStringToResultError + SampleSetDist.map2(~fn, ~t1, ~t2)->E.R2.errMap(x => DistributionTypes.OperationError(x)) }) ->E.R2.fmap(r => DistributionTypes.SampleSet(r)) } @@ -224,7 +224,7 @@ module AlgebraicCombination = { ): result => { switch tryAnalyticalSimplification(arithmeticOperation, t1, t2) { | Some(Ok(symbolicDist)) => Ok(Symbolic(symbolicDist)) - | Some(Error(e)) => Error(Other(e)) + | Some(Error(e)) => Error(OperationError(e)) | None => switch chooseConvolutionOrMonteCarlo(arithmeticOperation, t1, t2) { | MonteCarlo => runMonteCarlo(toSampleSetFn, arithmeticOperation, t1, t2) @@ -247,7 +247,7 @@ let pointwiseCombination = ( E.R.merge(toPointSetFn(t1), toPointSetFn(t2)) ->E.R2.fmap(((t1, t2)) => PointSetDist.combinePointwise( - GenericDist_Types.Operation.arithmeticToFn(arithmeticOperation), + DistributionTypes.Operation.arithmeticToFn(arithmeticOperation), t1, t2, ) @@ -258,23 +258,23 @@ let pointwiseCombination = ( let pointwiseCombinationFloat = ( t: t, ~toPointSetFn: toPointSetFn, - ~arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation, - ~float: float, + ~arithmeticOperation: DistributionTypes.Operation.arithmeticOperation, + ~f: float, ): result => { let m = switch arithmeticOperation { | #Add | #Subtract => Error(DistributionTypes.DistributionVerticalShiftIsInvalid) | (#Multiply | #Divide | #Power | #Logarithm) as arithmeticOperation => - toPointSetFn(t)->E.R2.fmap(t => { + toPointSetFn(t)->E.R.bind(t => { //TODO: Move to PointSet codebase let fn = (secondary, main) => Operation.Scale.toFn(arithmeticOperation, main, secondary) let integralSumCacheFn = Operation.Scale.toIntegralSumCacheFn(arithmeticOperation) let integralCacheFn = Operation.Scale.toIntegralCacheFn(arithmeticOperation) - PointSetDist.T.mapY( - ~integralSumCacheFn=integralSumCacheFn(float), - ~integralCacheFn=integralCacheFn(float), - ~fn=fn(float), + PointSetDist.T.mapYResult( + ~integralSumCacheFn=integralSumCacheFn(f), + ~integralCacheFn=integralCacheFn(f), + ~fn=fn(f), t, - ) + )->E.R2.errMap(x => DistributionTypes.OperationError(x)) }) } m->E.R2.fmap(r => DistributionTypes.PointSet(r)) diff --git a/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.resi b/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.resi index c49d18b5..276ca4e3 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.resi +++ b/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.resi @@ -1,5 +1,5 @@ -type t = GenericDist_Types.genericDist -type error = GenericDist_Types.error +type t = DistributionTypes.genericDist +type error = DistributionTypes.error type toPointSetFn = t => result type toSampleSetFn = t => result type scaleMultiplyFn = (t, float) => result @@ -28,7 +28,7 @@ let toPointSet: ( t, ~xyPointLength: int, ~sampleCount: int, - ~xSelection: GenericDist_Types.Operation.pointsetXSelection=?, + ~xSelection: DistributionTypes.Operation.pointsetXSelection=?, unit, ) => result let toSparkline: (t, ~sampleCount: int, ~bucketCount: int=?, unit) => result @@ -45,22 +45,22 @@ let algebraicCombination: ( t, ~toPointSetFn: toPointSetFn, ~toSampleSetFn: toSampleSetFn, - ~arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation, + ~arithmeticOperation: DistributionTypes.Operation.arithmeticOperation, ~t2: t, ) => result let pointwiseCombination: ( t, ~toPointSetFn: toPointSetFn, - ~arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation, + ~arithmeticOperation: DistributionTypes.Operation.arithmeticOperation, ~t2: t, ) => result let pointwiseCombinationFloat: ( t, ~toPointSetFn: toPointSetFn, - ~arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation, - ~float: float, + ~arithmeticOperation: DistributionTypes.Operation.arithmeticOperation, + ~f: float, ) => result let mixture: ( diff --git a/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist_Types.res b/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist_Types.res deleted file mode 100644 index b045a6d5..00000000 --- a/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist_Types.res +++ /dev/null @@ -1,194 +0,0 @@ -type genericDist = DistributionTypes.genericDist -@genType -type error = DistributionTypes.error - -@genType -module Error = { - type t = error - - let fromString = (s: string): t => Other(s) - - @genType - let toString = (x: t) => { - switch x { - | NotYetImplemented => "Not Yet Implemented" - | Unreachable => "Unreachable" - | DistributionVerticalShiftIsInvalid => "Distribution Vertical Shift Is Invalid" - | ArgumentError(x) => `Argument Error: ${x}` - | Other(s) => s - } - } - - let resultStringToResultError: result<'a, string> => result<'a, error> = n => - n->E.R2.errMap(r => r->fromString->Error) -} - -module Operation = { - type direction = - | Algebraic - | Pointwise - - type arithmeticOperation = [ - | #Add - | #Multiply - | #Subtract - | #Divide - | #Power - | #Logarithm - ] - - let arithmeticToFn = (arithmetic: arithmeticOperation) => - switch arithmetic { - | #Add => \"+." - | #Multiply => \"*." - | #Subtract => \"-." - | #Power => \"**" - | #Divide => \"/." - | #Logarithm => (a, b) => log(a) /. log(b) - } - - type toFloat = [ - | #Cdf(float) - | #Inv(float) - | #Mean - | #Pdf(float) - | #Sample - ] - - @genType - type pointsetXSelection = [#Linear | #ByWeight] - - type toDist = - | Normalize - | ToPointSet - | ToSampleSet(int) - | Truncate(option, option) - | Inspect - - type toFloatArray = Sample(int) - - type toString = - | ToString - | ToSparkline(int) - - type toBool = IsNormalized - - type fromDist = - | ToFloat(toFloat) - | ToDist(toDist) - | ToDistCombination(direction, arithmeticOperation, [#Dist(genericDist) | #Float(float)]) - | ToString(toString) - | ToBool(toBool) - - type singleParamaterFunction = - | FromDist(fromDist) - | FromFloat(fromDist) - - @genType - 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) => `toString` - | ToString(ToSparkline(n)) => `toSparkline(${E.I.toString(n)})` - | ToBool(IsNormalized) => `isNormalized` - | ToDistCombination(Algebraic, _, _) => `algebraic` - | ToDistCombination(Pointwise, _, _) => `pointwise` - } - - let toString = (d: genericFunctionCallInfo): string => - switch d { - | FromDist(f, _) | FromFloat(f, _) => distCallToString(f) - | Mixture(_) => `mixture` - } -} - -/* -It can be a pain to write out the genericFunctionCallInfo. The constructors help with this. -This code only covers some of genericFunctionCallInfo: many arguments could be called with either a -float or a distribution. The "UsingDists" module assumes that everything is a distribution. -This is a tradeoff of some generality in order to get a bit more simplicity. -I could see having a longer interface in the future, but it could be messy. -Like, algebraicAddDistFloat vs. algebraicAddDistDist -*/ -module Constructors = { - type t = Operation.genericFunctionCallInfo - - module UsingDists = { - @genType - let mean = (dist): t => FromDist(ToFloat(#Mean), dist) - let sample = (dist): t => FromDist(ToFloat(#Sample), dist) - let cdf = (dist, x): t => FromDist(ToFloat(#Cdf(x)), dist) - let inv = (dist, x): t => FromDist(ToFloat(#Inv(x)), dist) - let pdf = (dist, x): t => FromDist(ToFloat(#Pdf(x)), dist) - let normalize = (dist): t => FromDist(ToDist(Normalize), dist) - let isNormalized = (dist): t => FromDist(ToBool(IsNormalized), dist) - let toPointSet = (dist): t => FromDist(ToDist(ToPointSet), dist) - let toSampleSet = (dist, r): t => FromDist(ToDist(ToSampleSet(r)), dist) - let truncate = (dist, left, right): t => FromDist(ToDist(Truncate(left, right)), dist) - let inspect = (dist): t => FromDist(ToDist(Inspect), dist) - let toString = (dist): t => FromDist(ToString(ToString), dist) - let toSparkline = (dist, n): t => FromDist(ToString(ToSparkline(n)), dist) - let algebraicAdd = (dist1, dist2: genericDist): t => FromDist( - ToDistCombination(Algebraic, #Add, #Dist(dist2)), - dist1, - ) - let algebraicMultiply = (dist1, dist2): t => FromDist( - ToDistCombination(Algebraic, #Multiply, #Dist(dist2)), - dist1, - ) - let algebraicDivide = (dist1, dist2): t => FromDist( - ToDistCombination(Algebraic, #Divide, #Dist(dist2)), - dist1, - ) - let algebraicSubtract = (dist1, dist2): t => FromDist( - ToDistCombination(Algebraic, #Subtract, #Dist(dist2)), - dist1, - ) - let algebraicLogarithm = (dist1, dist2): t => FromDist( - ToDistCombination(Algebraic, #Logarithm, #Dist(dist2)), - dist1, - ) - let algebraicPower = (dist1, dist2): t => FromDist( - ToDistCombination(Algebraic, #Power, #Dist(dist2)), - dist1, - ) - let pointwiseAdd = (dist1, dist2): t => FromDist( - ToDistCombination(Pointwise, #Add, #Dist(dist2)), - dist1, - ) - let pointwiseMultiply = (dist1, dist2): t => FromDist( - ToDistCombination(Pointwise, #Multiply, #Dist(dist2)), - dist1, - ) - let pointwiseDivide = (dist1, dist2): t => FromDist( - ToDistCombination(Pointwise, #Divide, #Dist(dist2)), - dist1, - ) - let pointwiseSubtract = (dist1, dist2): t => FromDist( - ToDistCombination(Pointwise, #Subtract, #Dist(dist2)), - dist1, - ) - let pointwiseLogarithm = (dist1, dist2): t => FromDist( - ToDistCombination(Pointwise, #Logarithm, #Dist(dist2)), - dist1, - ) - let pointwisePower = (dist1, dist2): t => FromDist( - ToDistCombination(Pointwise, #Power, #Dist(dist2)), - dist1, - ) - } -} diff --git a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Continuous.res b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Continuous.res index 6c9cb3fd..5512b5cb 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Continuous.res +++ b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Continuous.res @@ -146,7 +146,27 @@ let reduce = ( continuousShapes, ) => continuousShapes |> E.A.fold_left(combinePointwise(~integralSumCachesFn, fn), empty) -let mapY = (~integralSumCacheFn=_ => None, ~integralCacheFn=_ => None, ~fn, t: t) => +let mapYResult = ( + ~integralSumCacheFn=_ => None, + ~integralCacheFn=_ => None, + ~fn: float => result, + t: t, +): result => + XYShape.T.mapYResult(fn, getShape(t))->E.R2.fmap(x => + make( + ~interpolation=t.interpolation, + ~integralSumCache=t.integralSumCache |> E.O.bind(_, integralSumCacheFn), + ~integralCache=t.integralCache |> E.O.bind(_, integralCacheFn), + x, + ) + ) + +let mapY = ( + ~integralSumCacheFn=_ => None, + ~integralCacheFn=_ => None, + ~fn: float => float, + t: t, +): t => make( ~interpolation=t.interpolation, ~integralSumCache=t.integralSumCache |> E.O.bind(_, integralSumCacheFn), @@ -170,6 +190,7 @@ module T = Dist({ let minX = shapeFn(XYShape.T.minX) let maxX = shapeFn(XYShape.T.maxX) let mapY = mapY + let mapYResult = mapYResult let updateIntegralCache = updateIntegralCache let toDiscreteProbabilityMassFraction = _ => 0.0 let toPointSetDist = (t: t): PointSetTypes.pointSetDist => Continuous(t) diff --git a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Discrete.res b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Discrete.res index 4037338d..92fdc7f0 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Discrete.res +++ b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Discrete.res @@ -103,7 +103,26 @@ let combineAlgebraically = (op: Operation.convolutionOperation, t1: t, t2: t): t make(~integralSumCache=combinedIntegralSum, combinedShape) } -let mapY = (~integralSumCacheFn=_ => None, ~integralCacheFn=_ => None, ~fn, t: t) => +let mapYResult = ( + ~integralSumCacheFn=_ => None, + ~integralCacheFn=_ => None, + ~fn: float => result, + t: t, +): result => + XYShape.T.mapYResult(fn, getShape(t))->E.R2.fmap(x => + make( + ~integralSumCache=t.integralSumCache |> E.O.bind(_, integralSumCacheFn), + ~integralCache=t.integralCache |> E.O.bind(_, integralCacheFn), + x, + ) + ) + +let mapY = ( + ~integralSumCacheFn=_ => None, + ~integralCacheFn=_ => None, + ~fn: float => float, + t: t, +): t => make( ~integralSumCache=t.integralSumCache |> E.O.bind(_, integralSumCacheFn), ~integralCache=t.integralCache |> E.O.bind(_, integralCacheFn), @@ -143,6 +162,7 @@ module T = Dist({ let maxX = shapeFn(XYShape.T.maxX) let toDiscreteProbabilityMassFraction = _ => 1.0 let mapY = mapY + let mapYResult = mapYResult let updateIntegralCache = updateIntegralCache let toPointSetDist = (t: t): PointSetTypes.pointSetDist => Discrete(t) let toContinuous = _ => None diff --git a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Distributions.res b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Distributions.res index 0821611c..407eae85 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Distributions.res +++ b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Distributions.res @@ -9,6 +9,12 @@ module type dist = { ~fn: float => float, t, ) => t + let mapYResult: ( + ~integralSumCacheFn: float => option=?, + ~integralCacheFn: PointSetTypes.continuousShape => option=?, + ~fn: float => result, + t, + ) => result let xToY: (float, t) => PointSetTypes.mixedPoint let toPointSetDist: t => PointSetTypes.pointSetDist let toContinuous: t => option @@ -37,6 +43,7 @@ module Dist = (T: dist) => { let integral = T.integral let xTotalRange = (t: t) => maxX(t) -. minX(t) let mapY = T.mapY + let mapYResult = T.mapYResult let xToY = T.xToY let downsample = T.downsample let toPointSetDist = T.toPointSetDist diff --git a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Mixed.res b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Mixed.res index 223c55e2..4d441a9a 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Mixed.res +++ b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Mixed.res @@ -164,7 +164,12 @@ module T = Dist({ // This pipes all ys (continuous and discrete) through fn. // If mapY is a linear operation, we might be able to update the integralSumCaches as well; // if not, they'll be set to None. - let mapY = (~integralSumCacheFn=_ => None, ~integralCacheFn=_ => None, ~fn, t: t): t => { + let mapY = ( + ~integralSumCacheFn=_ => None, + ~integralCacheFn=_ => None, + ~fn: float => float, + t: t, + ): t => { let yMappedDiscrete: PointSetTypes.discreteShape = t.discrete |> Discrete.T.mapY(~fn) @@ -187,6 +192,41 @@ module T = Dist({ } } + let mapYResult = ( + ~integralSumCacheFn=_ => None, + ~integralCacheFn=_ => None, + ~fn: float => result, + t: t, + ): result => { + E.R.merge( + Discrete.T.mapYResult(~fn, t.discrete), + Continuous.T.mapYResult(~fn, t.continuous), + )->E.R2.fmap(((discreteMapped, continuousMapped)) => { + let yMappedDiscrete: PointSetTypes.discreteShape = + discreteMapped + |> Discrete.updateIntegralSumCache( + E.O.bind(t.discrete.integralSumCache, integralSumCacheFn), + ) + |> Discrete.updateIntegralCache(E.O.bind(t.discrete.integralCache, integralCacheFn)) + + let yMappedContinuous: PointSetTypes.continuousShape = + continuousMapped + |> Continuous.updateIntegralSumCache( + E.O.bind(t.continuous.integralSumCache, integralSumCacheFn), + ) + |> Continuous.updateIntegralCache(E.O.bind(t.continuous.integralCache, integralCacheFn)) + + ( + { + discrete: yMappedDiscrete, + continuous: yMappedContinuous, + integralSumCache: E.O.bind(t.integralSumCache, integralSumCacheFn), + integralCache: E.O.bind(t.integralCache, integralCacheFn), + }: t + ) + }) + } + let mean = ({discrete, continuous}: t): float => { let discreteMean = Discrete.T.mean(discrete) let continuousMean = Continuous.T.mean(continuous) diff --git a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetDist.res b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetDist.res index 0035d646..b654c890 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetDist.res +++ b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetDist.res @@ -16,6 +16,13 @@ let fmap = ((fn1, fn2, fn3), t: t): t => | Continuous(m) => Continuous(fn3(m)) } +let fmapResult = ((fn1, fn2, fn3), t: t): result => + switch t { + | Mixed(m) => fn1(m)->E.R2.fmap(x => PointSetTypes.Mixed(x)) + | Discrete(m) => fn2(m)->E.R2.fmap(x => PointSetTypes.Discrete(x)) + | Continuous(m) => fn3(m)->E.R2.fmap(x => PointSetTypes.Continuous(x)) + } + let toMixed = mapToAll(( m => m, d => @@ -130,13 +137,26 @@ module T = Dist({ let integralYtoX = f => mapToAll((Mixed.T.Integral.yToX(f), Discrete.T.Integral.yToX(f), Continuous.T.Integral.yToX(f))) let maxX = mapToAll((Mixed.T.maxX, Discrete.T.maxX, Continuous.T.maxX)) - let mapY = (~integralSumCacheFn=_ => None, ~integralCacheFn=_ => None, ~fn) => + let mapY = (~integralSumCacheFn=_ => None, ~integralCacheFn=_ => None, ~fn: float => float): ( + t => t + ) => fmap(( Mixed.T.mapY(~integralSumCacheFn, ~integralCacheFn, ~fn), Discrete.T.mapY(~integralSumCacheFn, ~integralCacheFn, ~fn), Continuous.T.mapY(~integralSumCacheFn, ~integralCacheFn, ~fn), )) + let mapYResult = ( + ~integralSumCacheFn=_ => None, + ~integralCacheFn=_ => None, + ~fn: float => result, + ): (t => result) => + fmapResult(( + Mixed.T.mapYResult(~integralSumCacheFn, ~integralCacheFn, ~fn), + Discrete.T.mapYResult(~integralSumCacheFn, ~integralCacheFn, ~fn), + Continuous.T.mapYResult(~integralSumCacheFn, ~integralCacheFn, ~fn), + )) + let mean = (t: t): float => switch t { | Mixed(m) => Mixed.T.mean(m) @@ -195,8 +215,16 @@ let operate = (distToFloatOp: Operation.distToFloatOperation, s): float => | #Mean => T.mean(s) } -let toSparkline = (t: t, bucketCount) => +@genType +type sparklineError = CannotSparklineDiscrete + +let sparklineErrorToString = (err: sparklineError): string => + switch err { + | CannotSparklineDiscrete => "Cannot find the sparkline of a discrete distribution" + } + +let toSparkline = (t: t, bucketCount): result => T.toContinuous(t) ->E.O2.fmap(Continuous.downsampleEquallyOverX(bucketCount)) - ->E.O2.toResult("toContinous Error: Could not convert into continuous distribution") + ->E.O2.toResult(CannotSparklineDiscrete) ->E.R2.fmap(r => Continuous.getShape(r).ys->Sparklines.create()) diff --git a/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSetDist.res b/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSetDist.res index fcd4055d..b775c8ac 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSetDist.res +++ b/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSetDist.res @@ -1,3 +1,11 @@ +@genType +type sampleSetError = TooFewSamples + +let sampleSetErrorToString = (err: sampleSetError): string => + switch err { + | TooFewSamples => "Too few samples when constructing sample set" + } + /* This is used as a smart constructor. The only way to create a SampleSetDist.t is to call this constructor. @@ -8,7 +16,7 @@ module T: { //When we get a good functional library in TS, we could refactor that out. @genType type t = array - let make: array => result + let make: array => result let get: t => array } = { type t = array @@ -16,7 +24,7 @@ module T: { if E.A.length(a) > 5 { Ok(a) } else { - Error("too small") + Error(TooFewSamples) } let get = (a: t) => a } @@ -25,19 +33,27 @@ include T let length = (t: t) => get(t)->E.A.length +@genType +type pointsetConversionError = TooFewSamplesForConversionToPointSet + +let pointsetConversionErrorToString = (err: pointsetConversionError) => + switch err { + | TooFewSamplesForConversionToPointSet => "Too Few Samples to convert to point set" + } + /* TODO: Refactor to get a more precise estimate. Also, this code is just fairly messy, could use some refactoring. */ let toPointSetDist = (~samples: t, ~samplingInputs: SamplingInputs.samplingInputs): result< PointSetTypes.pointSetDist, - string, + pointsetConversionError, > => SampleSetDist_ToPointSet.toPointSetDist( ~samples=get(samples), ~samplingInputs, (), - ).pointSetDist->E.O2.toResult("Failed to convert to PointSetDist") + ).pointSetDist->E.O2.toResult(TooFewSamplesForConversionToPointSet) //Randomly get one sample from the distribution let sample = (t: t): float => { @@ -62,7 +78,19 @@ let sampleN = (t: t, n) => { } //TODO: Figure out what to do if distributions are different lengths. ``zip`` is kind of inelegant for this. -let map2 = (~fn: (float, float) => float, ~t1: t, ~t2: t) => { +let map2 = ( + ~fn: (float, float) => result, + ~t1: t, + ~t2: t, +): result => { let samples = Belt.Array.zip(get(t1), get(t2))->E.A2.fmap(((a, b)) => fn(a, b)) - make(samples) + + // This assertion should never be reached. In order for it to be reached, one + // of the input parameters would need to be a sample set distribution with less + // than 6 samples. Which should be impossible due to the smart constructor. + // I could prove this to the type system (say, creating a {first: float, second: float, ..., fifth: float, rest: array} + // But doing so would take too much time, so I'll leave it as an assertion + E.A.R.firstErrorOrOpen(samples)->E.R2.fmap(x => + E.R.assertOk("Input of samples should be larger than 5", make(x)) + ) } diff --git a/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSetDist_ToPointSet.res b/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSetDist_ToPointSet.res index 59b4fa46..3916e6fe 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSetDist_ToPointSet.res +++ b/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSetDist_ToPointSet.res @@ -83,7 +83,7 @@ let toPointSetDist = ( ~samples: Internals.T.t, ~samplingInputs: SamplingInputs.samplingInputs, (), -) => { +): Internals.Types.outputs => { Array.fast_sort(compare, samples) let (continuousPart, discretePart) = E.A.Sorted.Floats.split(samples) let length = samples |> E.A.length |> float_of_int diff --git a/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res b/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res index a6eda12b..7ce95721 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res +++ b/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res @@ -379,7 +379,7 @@ module T = { ): analyticalSimplificationResult => switch (d1, d2) { | (#Float(v1), #Float(v2)) => - switch Operation.Algebraic.applyFn(op, v1, v2) { + switch Operation.Algebraic.toFn(op, v1, v2) { | Ok(r) => #AnalyticalSolution(#Float(r)) | Error(n) => #Error(n) } diff --git a/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDistTypes.res b/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDistTypes.res index 4625867d..121ff0b9 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDistTypes.res +++ b/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDistTypes.res @@ -45,6 +45,6 @@ type symbolicDist = [ type analyticalSimplificationResult = [ | #AnalyticalSolution(symbolicDist) - | #Error(string) + | #Error(Operation.invalidOperationError) | #NoSolution ] diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_ErrorValue.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_ErrorValue.res index 4ad60732..48fae58c 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_ErrorValue.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_ErrorValue.res @@ -9,6 +9,7 @@ type errorValue = | RERecordPropertyNotFound(string, string) | RESymbolNotFound(string) | RESyntaxError(string) + | REDistributionError(DistributionTypes.error) | RETodo(string) // To do type t = errorValue @@ -20,6 +21,7 @@ let errorToString = err => | REAssignmentExpected => "Assignment expected" | REExpressionExpected => "Expression expected" | REFunctionExpected(msg) => `Function expected: ${msg}` + | REDistributionError(err) => `Math Error: ${DistributionTypes.Error.toString(err)}` | REJavaScriptExn(omsg, oname) => { let answer = "JS Exception:" let answer = switch oname { diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExpressionValue.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExpressionValue.res index 2a8dc2e1..0470543d 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExpressionValue.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExpressionValue.res @@ -10,7 +10,7 @@ type rec expressionValue = | EvArray(array) | EvBool(bool) | EvCall(string) // External function call - | EvDistribution(GenericDist_Types.genericDist) + | EvDistribution(DistributionTypes.genericDist) | EvNumber(float) | EvRecord(Js.Dict.t) | EvString(string) diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res index f21010e2..68ac7393 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res @@ -29,8 +29,8 @@ module Helpers = { } let catchAndConvertTwoArgsToDists = (args: array): option<( - GenericDist_Types.genericDist, - GenericDist_Types.genericDist, + DistributionTypes.genericDist, + DistributionTypes.genericDist, )> => { switch args { | [EvDistribution(a), EvDistribution(b)] => Some((a, b)) @@ -41,33 +41,41 @@ module Helpers = { } let toFloatFn = ( - fnCall: GenericDist_Types.Operation.toFloat, - dist: GenericDist_Types.genericDist, + fnCall: DistributionTypes.Operation.toFloat, + dist: DistributionTypes.genericDist, ) => { - FromDist(GenericDist_Types.Operation.ToFloat(fnCall), dist)->runGenericOperation->Some + FromDist(DistributionTypes.DistributionOperation.ToFloat(fnCall), dist) + ->runGenericOperation + ->Some } let toStringFn = ( - fnCall: GenericDist_Types.Operation.toString, - dist: GenericDist_Types.genericDist, + fnCall: DistributionTypes.DistributionOperation.toString, + dist: DistributionTypes.genericDist, ) => { - FromDist(GenericDist_Types.Operation.ToString(fnCall), dist)->runGenericOperation->Some + FromDist(DistributionTypes.DistributionOperation.ToString(fnCall), dist) + ->runGenericOperation + ->Some } let toBoolFn = ( - fnCall: GenericDist_Types.Operation.toBool, - dist: GenericDist_Types.genericDist, + fnCall: DistributionTypes.DistributionOperation.toBool, + dist: DistributionTypes.genericDist, ) => { - FromDist(GenericDist_Types.Operation.ToBool(fnCall), dist)->runGenericOperation->Some + FromDist(DistributionTypes.DistributionOperation.ToBool(fnCall), dist) + ->runGenericOperation + ->Some } - let toDistFn = (fnCall: GenericDist_Types.Operation.toDist, dist) => { - FromDist(GenericDist_Types.Operation.ToDist(fnCall), dist)->runGenericOperation->Some + let toDistFn = (fnCall: DistributionTypes.DistributionOperation.toDist, dist) => { + FromDist(DistributionTypes.DistributionOperation.ToDist(fnCall), dist) + ->runGenericOperation + ->Some } let twoDiststoDistFn = (direction, arithmetic, dist1, dist2) => { FromDist( - GenericDist_Types.Operation.ToDistCombination( + DistributionTypes.DistributionOperation.ToDistCombination( direction, arithmeticMap(arithmetic), #Dist(dist2), @@ -84,7 +92,7 @@ module Helpers = { let parseNumberArray = (ags: array): Belt.Result.t, string> => E.A.fmap(parseNumber, ags) |> E.A.R.firstErrorOrOpen - let parseDist = (args: expressionValue): Belt.Result.t => + let parseDist = (args: expressionValue): Belt.Result.t => switch args { | EvDistribution(x) => Ok(x) | EvNumber(x) => Ok(GenericDist.fromFloat(x)) @@ -92,12 +100,12 @@ module Helpers = { } let parseDistributionArray = (ags: array): Belt.Result.t< - array, + array, string, > => E.A.fmap(parseDist, ags) |> E.A.R.firstErrorOrOpen let mixtureWithGivenWeights = ( - distributions: array, + distributions: array, weights: array, ): DistributionOperation.outputType => E.A.length(distributions) == E.A.length(weights) @@ -107,7 +115,7 @@ module Helpers = { ) let mixtureWithDefaultWeights = ( - distributions: array, + distributions: array, ): DistributionOperation.outputType => { let length = E.A.length(distributions) let weights = Belt.Array.make(length, 1.0 /. Belt.Int.toFloat(length)) @@ -259,12 +267,7 @@ let genericOutputToReducerValue = (o: DistributionOperation.outputType): result< | Float(d) => Ok(EvNumber(d)) | String(d) => Ok(EvString(d)) | Bool(d) => Ok(EvBool(d)) - | GenDistError(NotYetImplemented) => Error(RETodo("Function not yet implemented")) - | GenDistError(Unreachable) => Error(RETodo("Unreachable")) - | GenDistError(DistributionVerticalShiftIsInvalid) => - Error(RETodo("Distribution Vertical Shift Is Invalid")) - | GenDistError(ArgumentError(err)) => Error(RETodo("Argument Error: " ++ err)) - | GenDistError(Other(s)) => Error(RETodo(s)) + | GenDistError(err) => Error(REDistributionError(err)) } let dispatch = call => { diff --git a/packages/squiggle-lang/src/rescript/TypescriptInterface.res b/packages/squiggle-lang/src/rescript/TypescriptInterface.res index 6a0b7bcf..8704bf5e 100644 --- a/packages/squiggle-lang/src/rescript/TypescriptInterface.res +++ b/packages/squiggle-lang/src/rescript/TypescriptInterface.res @@ -53,4 +53,4 @@ type continuousShape = PointSetTypes.continuousShape let errorValueToString = Reducer_ErrorValue.errorToString @genType -let distributionErrorToString = GenericDist_Types.Error.toString +let distributionErrorToString = DistributionTypes.Error.toString diff --git a/packages/squiggle-lang/src/rescript/Utility/E.res b/packages/squiggle-lang/src/rescript/Utility/E.res index 8c6c4511..779ab03a 100644 --- a/packages/squiggle-lang/src/rescript/Utility/E.res +++ b/packages/squiggle-lang/src/rescript/Utility/E.res @@ -152,6 +152,8 @@ module I = { let toString = Js.Int.toString } +exception Assertion(string) + /* R for Result */ module R = { let result = Rationale.Result.result @@ -159,6 +161,11 @@ module R = { let fmap = Rationale.Result.fmap let bind = Rationale.Result.bind let toExn = Belt.Result.getExn + let assertOk = (message: string, x: result<'a, 'b>): 'a => + switch x { + | Ok(r) => r + | Error(_) => raise(Assertion(message)) + } let default = (default, res: Belt.Result.t<'a, 'b>) => switch res { | Ok(r) => r @@ -210,10 +217,10 @@ module R2 = { let bind = (a, b) => R.bind(b, a) //Converts result type to change error type only - let errMap = (a, map) => + let errMap = (a: result<'a, 'b>, map: 'b => 'c): result<'a, 'c> => switch a { | Ok(r) => Ok(r) - | Error(e) => map(e) + | Error(e) => Error(map(e)) } let fmap2 = (xR, f) => @@ -436,6 +443,7 @@ module A = { r |> Belt.Array.map(_, r => Belt.Result.getExn(r)) bringErrorUp |> Belt.Result.map(_, forceOpen) } + let filterOk = (x: array>): array<'a> => fmap(R.toOption, x)->O.concatSomes } module Sorted = { diff --git a/packages/squiggle-lang/src/rescript/Utility/Operation.res b/packages/squiggle-lang/src/rescript/Utility/Operation.res index f78a432c..587f2e31 100644 --- a/packages/squiggle-lang/src/rescript/Utility/Operation.res +++ b/packages/squiggle-lang/src/rescript/Utility/Operation.res @@ -37,22 +37,42 @@ module Convolution = { } } +@genType +type invalidOperationError = + | DivisionByZeroError + | ComplexNumberError + +let invalidOperationErrorToString = (err: invalidOperationError): string => + switch err { + | DivisionByZeroError => "Cannot divide by zero" + | ComplexNumberError => "Operation returned complex result" + } + module Algebraic = { type t = algebraicOperation - let toFn: (t, float, float) => float = x => + let toFn: (t, float, float) => result = (x, a, b) => switch x { - | #Add => \"+." - | #Subtract => \"-." - | #Multiply => \"*." - | #Power => \"**" - | #Divide => \"/." - | #Logarithm => (a, b) => log(a) /. log(b) - } - - let applyFn = (t, f1, f2) => - switch (t, f1, f2) { - | (#Divide, _, 0.) => Error("Cannot divide $v1 by zero.") - | _ => Ok(toFn(t, f1, f2)) + | #Add => Ok(a +. b) + | #Subtract => Ok(a -. b) + | #Multiply => Ok(a *. b) + | #Power => + if a > 0.0 { + Ok(a ** b) + } else { + Error(ComplexNumberError) + } + | #Divide => + if b != 0.0 { + Ok(a /. b) + } else { + Error(DivisionByZeroError) + } + | #Logarithm => + if a > 0.0 && b > 0.0 { + Ok(log(a) /. log(b)) + } else { + Error(ComplexNumberError) + } } let toString = x => @@ -96,12 +116,27 @@ module DistToFloat = { // Note that different logarithms don't really do anything. module Scale = { type t = scaleOperation - let toFn = x => + let toFn = (x: t, a: float, b: float): result => switch x { - | #Multiply => \"*." - | #Divide => \"/." - | #Power => \"**" - | #Logarithm => (a, b) => log(a) /. log(b) + | #Multiply => Ok(a *. b) + | #Divide => + if b != 0.0 { + Ok(a /. b) + } else { + Error(DivisionByZeroError) + } + | #Power => + if a > 0.0 { + Ok(a ** b) + } else { + Error(ComplexNumberError) + } + | #Logarithm => + if a > 0.0 && b > 0.0 { + Ok(log(a) /. log(b)) + } else { + Error(DivisionByZeroError) + } } let format = (operation: t, value, scaleBy) => diff --git a/packages/squiggle-lang/src/rescript/Utility/XYShape.res b/packages/squiggle-lang/src/rescript/Utility/XYShape.res index 020acd42..d792f70e 100644 --- a/packages/squiggle-lang/src/rescript/Utility/XYShape.res +++ b/packages/squiggle-lang/src/rescript/Utility/XYShape.res @@ -43,6 +43,10 @@ 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 mapYResult = (fn: float => result, t: t): result => { + let mappedYs = E.A.fmap(fn, t.ys) + E.A.R.firstErrorOrOpen(mappedYs)->E.R2.fmap(y => {xs: t.xs, ys: y}) + } 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} From 9c92d93ca4971cd80f6c3d17a269cd9206f3bc16 Mon Sep 17 00:00:00 2001 From: Sam Nolan Date: Fri, 22 Apr 2022 17:21:45 -0400 Subject: [PATCH 02/11] Fix bad validation math --- packages/squiggle-lang/src/rescript/Utility/Operation.res | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/squiggle-lang/src/rescript/Utility/Operation.res b/packages/squiggle-lang/src/rescript/Utility/Operation.res index 587f2e31..3002785f 100644 --- a/packages/squiggle-lang/src/rescript/Utility/Operation.res +++ b/packages/squiggle-lang/src/rescript/Utility/Operation.res @@ -56,7 +56,7 @@ module Algebraic = { | #Subtract => Ok(a -. b) | #Multiply => Ok(a *. b) | #Power => - if a > 0.0 { + if a >= 0.0 { Ok(a ** b) } else { Error(ComplexNumberError) @@ -68,7 +68,10 @@ module Algebraic = { Error(DivisionByZeroError) } | #Logarithm => - if a > 0.0 && b > 0.0 { + if b == 1. { + Error(DivisionByZeroError) + } + else if a > 0.0 && b > 0.0 { Ok(log(a) /. log(b)) } else { Error(ComplexNumberError) From 79af95ed78c7afbafd1d6401eff6dfd5bb331307 Mon Sep 17 00:00:00 2001 From: Sam Nolan Date: Sat, 23 Apr 2022 09:47:48 -0400 Subject: [PATCH 03/11] Remove unneeded prefix to error --- .../ReducerInterface/ReducerInterface_Distribution_test.res | 4 ++-- .../src/rescript/Distributions/DistributionTypes.res | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res b/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res index 65a76b88..f956416c 100644 --- a/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res +++ b/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res @@ -90,11 +90,11 @@ describe("eval on distribution functions", () => { testEval("log(2, uniform(5,8))", "Ok(Sample Set Distribution)") testEval( "log(normal(5,2), 3)", - "Error(Math Error: Operation Error: Operation returned complex result)", + "Error(Math Error: Operation returned complex result)", ) testEval( "log(normal(5,2), normal(10,1))", - "Error(Math Error: Operation Error: Operation returned complex result)", + "Error(Math Error: Operation returned complex result)", ) testEval("log(uniform(5,8))", "Ok(Sample Set Distribution)") testEval("log10(uniform(5,8))", "Ok(Sample Set Distribution)") diff --git a/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res b/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res index b171fa95..9bd8b5c4 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res +++ b/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res @@ -35,7 +35,7 @@ module Error = { | DistributionVerticalShiftIsInvalid => "Distribution Vertical Shift is Invalid" | ArgumentError(s) => `Argument Error ${s}` | TooFewSamples => "Too Few Samples" - | OperationError(err) => `Operation Error: ${Operation.invalidOperationErrorToString(err)}` + | OperationError(err) => Operation.invalidOperationErrorToString(err) | PointSetConversionError(err) => SampleSetDist.pointsetConversionErrorToString(err) | SparklineError(err) => PointSetDist.sparklineErrorToString(err) | Other(s) => s From ad593e659b48bd491acc541e0868074c031d648a Mon Sep 17 00:00:00 2001 From: Sam Nolan Date: Sat, 23 Apr 2022 09:56:47 -0400 Subject: [PATCH 04/11] Move error types to types modules --- .../ReducerInterface_Distribution_test.res | 5 +-- .../Distributions/DistributionTypes.res | 18 +++++----- .../Distributions/GenericDist/GenericDist.res | 4 +-- .../PointSetDist/PointSetDist.res | 12 ++----- .../PointSetDist/PointSetTypes.res | 8 +++++ .../SampleSetDist/SampleSetDist.res | 35 +++++++++++-------- .../SymbolicDist/SymbolicDistTypes.res | 2 +- .../src/rescript/Utility/Operation.res | 26 +++++++------- 8 files changed, 57 insertions(+), 53 deletions(-) diff --git a/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res b/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res index f956416c..3af5c0cc 100644 --- a/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res +++ b/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res @@ -88,10 +88,7 @@ describe("eval on distribution functions", () => { describe("log", () => { testEval("log(2, uniform(5,8))", "Ok(Sample Set Distribution)") - testEval( - "log(normal(5,2), 3)", - "Error(Math Error: Operation returned complex result)", - ) + testEval("log(normal(5,2), 3)", "Error(Math Error: Operation returned complex result)") testEval( "log(normal(5,2), normal(10,1))", "Error(Math Error: Operation returned complex result)", diff --git a/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res b/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res index 9bd8b5c4..8a3541c0 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res +++ b/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res @@ -11,16 +11,11 @@ type error = | DistributionVerticalShiftIsInvalid | TooFewSamples | ArgumentError(string) - | OperationError(Operation.invalidOperationError) + | OperationError(Operation.Error.invalidOperationError) | PointSetConversionError(SampleSetDist.pointsetConversionError) - | SparklineError(PointSetDist.sparklineError) // This type of error is for when we find a sparkline of a discrete distribution. This should probably at some point be actually implemented + | SparklineError(PointSetTypes.sparklineError) // This type of error is for when we find a sparkline of a discrete distribution. This should probably at some point be actually implemented | Other(string) -let sampleErrorToDistErr = (err: SampleSetDist.sampleSetError): error => - switch err { - | TooFewSamples => TooFewSamples - } - @genType module Error = { type t = error @@ -35,14 +30,19 @@ module Error = { | DistributionVerticalShiftIsInvalid => "Distribution Vertical Shift is Invalid" | ArgumentError(s) => `Argument Error ${s}` | TooFewSamples => "Too Few Samples" - | OperationError(err) => Operation.invalidOperationErrorToString(err) + | OperationError(err) => Operation.Error.invalidOperationErrorToString(err) | PointSetConversionError(err) => SampleSetDist.pointsetConversionErrorToString(err) - | SparklineError(err) => PointSetDist.sparklineErrorToString(err) + | SparklineError(err) => PointSetTypes.sparklineErrorToString(err) | Other(s) => s } let resultStringToResultError: result<'a, string> => result<'a, error> = n => n->E.R2.errMap(r => r->fromString) + + let sampleErrorToDistErr = (err: SampleSetDist.sampleSetError): error => + switch err { + | TooFewSamples => TooFewSamples + } } @genType diff --git a/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.res b/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.res index 5a195f4b..55dfe746 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.res +++ b/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.res @@ -14,7 +14,7 @@ let sampleN = (t: t, n) => } let toSampleSetDist = (t: t, n) => - SampleSetDist.make(sampleN(t, n))->E.R2.errMap(DistributionTypes.sampleErrorToDistErr) + SampleSetDist.make(sampleN(t, n))->E.R2.errMap(DistributionTypes.Error.sampleErrorToDistErr) let fromFloat = (f: float): t => Symbolic(SymbolicDist.Float.make(f)) @@ -151,7 +151,7 @@ module AlgebraicCombination = { arithmeticOperation: DistributionTypes.Operation.arithmeticOperation, t1: t, t2: t, - ): option> => + ): option> => switch (arithmeticOperation, t1, t2) { | (arithmeticOperation, Symbolic(d1), Symbolic(d2)) => switch SymbolicDist.T.tryAnalyticalSimplification(d1, d2, arithmeticOperation) { diff --git a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetDist.res b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetDist.res index b654c890..c2cf0d9c 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetDist.res +++ b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetDist.res @@ -215,16 +215,8 @@ let operate = (distToFloatOp: Operation.distToFloatOperation, s): float => | #Mean => T.mean(s) } -@genType -type sparklineError = CannotSparklineDiscrete - -let sparklineErrorToString = (err: sparklineError): string => - switch err { - | CannotSparklineDiscrete => "Cannot find the sparkline of a discrete distribution" - } - -let toSparkline = (t: t, bucketCount): result => +let toSparkline = (t: t, bucketCount): result => T.toContinuous(t) ->E.O2.fmap(Continuous.downsampleEquallyOverX(bucketCount)) - ->E.O2.toResult(CannotSparklineDiscrete) + ->E.O2.toResult(PointSetTypes.CannotSparklineDiscrete) ->E.R2.fmap(r => Continuous.getShape(r).ys->Sparklines.create()) diff --git a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetTypes.res b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetTypes.res index a1a02a67..fcc796ec 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetTypes.res +++ b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetTypes.res @@ -94,3 +94,11 @@ module MixedPoint = { let add = combine2((a, b) => a +. b) } + +@genType +type sparklineError = CannotSparklineDiscrete + +let sparklineErrorToString = (err: sparklineError): string => + switch err { + | CannotSparklineDiscrete => "Cannot find the sparkline of a discrete distribution" + } diff --git a/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSetDist.res b/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSetDist.res index b775c8ac..bb5f17d6 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSetDist.res +++ b/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSetDist.res @@ -1,10 +1,23 @@ @genType -type sampleSetError = TooFewSamples +module Error = { + @genType + type sampleSetError = TooFewSamples -let sampleSetErrorToString = (err: sampleSetError): string => - switch err { - | TooFewSamples => "Too few samples when constructing sample set" - } + let sampleSetErrorToString = (err: sampleSetError): string => + switch err { + | TooFewSamples => "Too few samples when constructing sample set" + } + + @genType + type pointsetConversionError = TooFewSamplesForConversionToPointSet + + let pointsetConversionErrorToString = (err: pointsetConversionError) => + switch err { + | TooFewSamplesForConversionToPointSet => "Too Few Samples to convert to point set" + } +} + +include Error /* This is used as a smart constructor. The only way to create a SampleSetDist.t is to call @@ -33,14 +46,6 @@ include T let length = (t: t) => get(t)->E.A.length -@genType -type pointsetConversionError = TooFewSamplesForConversionToPointSet - -let pointsetConversionErrorToString = (err: pointsetConversionError) => - switch err { - | TooFewSamplesForConversionToPointSet => "Too Few Samples to convert to point set" - } - /* TODO: Refactor to get a more precise estimate. Also, this code is just fairly messy, could use some refactoring. @@ -79,10 +84,10 @@ let sampleN = (t: t, n) => { //TODO: Figure out what to do if distributions are different lengths. ``zip`` is kind of inelegant for this. let map2 = ( - ~fn: (float, float) => result, + ~fn: (float, float) => result, ~t1: t, ~t2: t, -): result => { +): result => { let samples = Belt.Array.zip(get(t1), get(t2))->E.A2.fmap(((a, b)) => fn(a, b)) // This assertion should never be reached. In order for it to be reached, one diff --git a/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDistTypes.res b/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDistTypes.res index 121ff0b9..274a908e 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDistTypes.res +++ b/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDistTypes.res @@ -45,6 +45,6 @@ type symbolicDist = [ type analyticalSimplificationResult = [ | #AnalyticalSolution(symbolicDist) - | #Error(Operation.invalidOperationError) + | #Error(Operation.Error.invalidOperationError) | #NoSolution ] diff --git a/packages/squiggle-lang/src/rescript/Utility/Operation.res b/packages/squiggle-lang/src/rescript/Utility/Operation.res index 3002785f..59adf1c5 100644 --- a/packages/squiggle-lang/src/rescript/Utility/Operation.res +++ b/packages/squiggle-lang/src/rescript/Utility/Operation.res @@ -38,19 +38,22 @@ module Convolution = { } @genType -type invalidOperationError = - | DivisionByZeroError - | ComplexNumberError +module Error = { + @genType + type invalidOperationError = + | DivisionByZeroError + | ComplexNumberError -let invalidOperationErrorToString = (err: invalidOperationError): string => - switch err { - | DivisionByZeroError => "Cannot divide by zero" - | ComplexNumberError => "Operation returned complex result" - } + let invalidOperationErrorToString = (err: invalidOperationError): string => + switch err { + | DivisionByZeroError => "Cannot divide by zero" + | ComplexNumberError => "Operation returned complex result" + } +} module Algebraic = { type t = algebraicOperation - let toFn: (t, float, float) => result = (x, a, b) => + let toFn: (t, float, float) => result = (x, a, b) => switch x { | #Add => Ok(a +. b) | #Subtract => Ok(a -. b) @@ -70,8 +73,7 @@ module Algebraic = { | #Logarithm => if b == 1. { Error(DivisionByZeroError) - } - else if a > 0.0 && b > 0.0 { + } else if a > 0.0 && b > 0.0 { Ok(log(a) /. log(b)) } else { Error(ComplexNumberError) @@ -119,7 +121,7 @@ module DistToFloat = { // Note that different logarithms don't really do anything. module Scale = { type t = scaleOperation - let toFn = (x: t, a: float, b: float): result => + let toFn = (x: t, a: float, b: float): result => switch x { | #Multiply => Ok(a *. b) | #Divide => From 9fb7148290b0dc83727cec0d1e5b27c69756a46f Mon Sep 17 00:00:00 2001 From: Sam Nolan Date: Sat, 23 Apr 2022 10:17:07 -0400 Subject: [PATCH 05/11] Refactor mapY and mapYResult in mixed to remove duplication --- .../Distributions/PointSetDist/Mixed.res | 61 ++++++++++--------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Mixed.res b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Mixed.res index 4d441a9a..62f9d8dd 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Mixed.res +++ b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Mixed.res @@ -161,24 +161,20 @@ module T = Dist({ let integralYtoX = (f, t) => t |> integral |> Continuous.getShape |> XYShape.YtoX.linear(f) - // This pipes all ys (continuous and discrete) through fn. - // If mapY is a linear operation, we might be able to update the integralSumCaches as well; - // if not, they'll be set to None. - let mapY = ( + let createMixedFromContinuousDiscrete = ( ~integralSumCacheFn=_ => None, ~integralCacheFn=_ => None, - ~fn: float => float, t: t, + discrete: PointSetTypes.discreteShape, + continuous: PointSetTypes.continuousShape, ): t => { let yMappedDiscrete: PointSetTypes.discreteShape = - t.discrete - |> Discrete.T.mapY(~fn) + discrete |> Discrete.updateIntegralSumCache(E.O.bind(t.discrete.integralSumCache, integralSumCacheFn)) |> Discrete.updateIntegralCache(E.O.bind(t.discrete.integralCache, integralCacheFn)) let yMappedContinuous: PointSetTypes.continuousShape = - t.continuous - |> Continuous.T.mapY(~fn) + continuous |> Continuous.updateIntegralSumCache( E.O.bind(t.continuous.integralSumCache, integralSumCacheFn), ) @@ -192,6 +188,26 @@ module T = Dist({ } } + // This pipes all ys (continuous and discrete) through fn. + // If mapY is a linear operation, we might be able to update the integralSumCaches as well; + // if not, they'll be set to None. + let mapY = ( + ~integralSumCacheFn=_ => None, + ~integralCacheFn=_ => None, + ~fn: float => float, + t: t, + ): t => { + let discrete = t.discrete |> Discrete.T.mapY(~fn) + let continuous = t.continuous |> Continuous.T.mapY(~fn) + createMixedFromContinuousDiscrete( + ~integralCacheFn, + ~integralSumCacheFn, + t, + discrete, + continuous, + ) + } + let mapYResult = ( ~integralSumCacheFn=_ => None, ~integralCacheFn=_ => None, @@ -202,27 +218,12 @@ module T = Dist({ Discrete.T.mapYResult(~fn, t.discrete), Continuous.T.mapYResult(~fn, t.continuous), )->E.R2.fmap(((discreteMapped, continuousMapped)) => { - let yMappedDiscrete: PointSetTypes.discreteShape = - discreteMapped - |> Discrete.updateIntegralSumCache( - E.O.bind(t.discrete.integralSumCache, integralSumCacheFn), - ) - |> Discrete.updateIntegralCache(E.O.bind(t.discrete.integralCache, integralCacheFn)) - - let yMappedContinuous: PointSetTypes.continuousShape = - continuousMapped - |> Continuous.updateIntegralSumCache( - E.O.bind(t.continuous.integralSumCache, integralSumCacheFn), - ) - |> Continuous.updateIntegralCache(E.O.bind(t.continuous.integralCache, integralCacheFn)) - - ( - { - discrete: yMappedDiscrete, - continuous: yMappedContinuous, - integralSumCache: E.O.bind(t.integralSumCache, integralSumCacheFn), - integralCache: E.O.bind(t.integralCache, integralCacheFn), - }: t + createMixedFromContinuousDiscrete( + ~integralCacheFn, + ~integralSumCacheFn, + t, + discreteMapped, + continuousMapped, ) }) } From 98bf4f81c747ccb6af01a6770736677158911fcf Mon Sep 17 00:00:00 2001 From: Sam Nolan Date: Sat, 23 Apr 2022 10:30:03 -0400 Subject: [PATCH 06/11] Rename assertOk to toExn --- .../Invariants/AlgebraicCombination_test.res | 18 ++++++++--------- .../Distributions/Invariants/Means_test.res | 20 +++++++++---------- .../SampleSetDist/SampleSetDist.res | 2 +- .../squiggle-lang/src/rescript/Utility/E.res | 6 +++--- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/packages/squiggle-lang/__tests__/Distributions/Invariants/AlgebraicCombination_test.res b/packages/squiggle-lang/__tests__/Distributions/Invariants/AlgebraicCombination_test.res index 718a4e14..a0f492a1 100644 --- a/packages/squiggle-lang/__tests__/Distributions/Invariants/AlgebraicCombination_test.res +++ b/packages/squiggle-lang/__tests__/Distributions/Invariants/AlgebraicCombination_test.res @@ -46,7 +46,7 @@ describe("(Algebraic) addition of distributions", () => { ->E.R2.fmap(DistributionTypes.Constructors.UsingDists.mean) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) - ->E.R.toExn + ->E.R.toExn("Expected float", _) ->expect ->toBe(Some(2.5e1)) }) @@ -60,7 +60,7 @@ describe("(Algebraic) addition of distributions", () => { ->E.R2.fmap(DistributionTypes.Constructors.UsingDists.mean) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) - ->E.R.toExn + ->E.R.toExn("Expected float", _) switch received { | None => "algebraicAdd has"->expect->toBe("failed") // This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad. @@ -77,7 +77,7 @@ describe("(Algebraic) addition of distributions", () => { ->E.R2.fmap(DistributionTypes.Constructors.UsingDists.mean) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) - ->E.R.toExn + ->E.R.toExn("Expected float", _) switch received { | None => "algebraicAdd has"->expect->toBe("failed") // This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad. @@ -158,7 +158,7 @@ describe("(Algebraic) addition of distributions", () => { ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.pdf(d, 1e1)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) - ->E.R.toExn + ->E.R.toExn("Expected float", _) switch received { | None => "algebraicAdd has"->expect->toBe("failed") // This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad. @@ -173,7 +173,7 @@ describe("(Algebraic) addition of distributions", () => { ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.pdf(d, 1e1)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) - ->E.R.toExn + ->E.R.toExn("Expected float", _) switch received { | None => "algebraicAdd has"->expect->toBe("failed") // This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad. @@ -249,7 +249,7 @@ describe("(Algebraic) addition of distributions", () => { ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.cdf(d, 1e1)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) - ->E.R.toExn + ->E.R.toExn("Expected float", _) switch received { | None => "algebraicAdd has"->expect->toBe("failed") // This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad. @@ -264,7 +264,7 @@ describe("(Algebraic) addition of distributions", () => { ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.cdf(d, 1e1)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) - ->E.R.toExn + ->E.R.toExn("Expected float", _) switch received { | None => "algebraicAdd has"->expect->toBe("failed") // This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad. @@ -341,7 +341,7 @@ describe("(Algebraic) addition of distributions", () => { ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.inv(d, 2e-2)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) - ->E.R.toExn + ->E.R.toExn("Expected float", _) switch received { | None => "algebraicAdd has"->expect->toBe("failed") // This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad. @@ -356,7 +356,7 @@ describe("(Algebraic) addition of distributions", () => { ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.inv(d, 2e-2)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) - ->E.R.toExn + ->E.R.toExn("Expected float", _) switch received { | None => "algebraicAdd has"->expect->toBe("failed") // This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad. diff --git a/packages/squiggle-lang/__tests__/Distributions/Invariants/Means_test.res b/packages/squiggle-lang/__tests__/Distributions/Invariants/Means_test.res index 3806c7f9..fbc17114 100644 --- a/packages/squiggle-lang/__tests__/Distributions/Invariants/Means_test.res +++ b/packages/squiggle-lang/__tests__/Distributions/Invariants/Means_test.res @@ -50,7 +50,7 @@ module Internals = { let dist1 = dist1'->DistributionTypes.Symbolic let dist2 = dist2'->DistributionTypes.Symbolic let received = - distOp(dist1, dist2)->E.R2.fmap(mean)->E.R2.fmap(run)->E.R2.fmap(toFloat)->E.R.toExn + distOp(dist1, dist2)->E.R2.fmap(mean)->E.R2.fmap(run)->E.R2.fmap(toFloat)->E.R.toExn("Expected float", _) let expected = floatOp(runMean(dist1), runMean(dist2)) switch received { | None => expectImpossiblePath(description) @@ -82,12 +82,12 @@ describe("Means are invariant", () => { let testAdditionMean = testOperationMean(algebraicAdd, "algebraicAdd", \"+.", ~epsilon) testAll("with two of the same distribution", distributions, dist => { - E.R.liftM2(testAdditionMean, dist, dist)->E.R.toExn + E.R.liftM2(testAdditionMean, dist, dist)->E.R.toExn("Means were not invariant", _) }) testAll("with two different distributions", pairsOfDifferentDistributions, dists => { let (dist1, dist2) = dists - E.R.liftM2(testAdditionMean, dist1, dist2)->E.R.toExn + E.R.liftM2(testAdditionMean, dist1, dist2)->E.R.toExn("Means were not invariant", _) }) testAll( @@ -95,7 +95,7 @@ describe("Means are invariant", () => { pairsOfDifferentDistributions, dists => { let (dist1, dist2) = dists - E.R.liftM2(testAdditionMean, dist2, dist1)->E.R.toExn + E.R.liftM2(testAdditionMean, dist2, dist1)->E.R.toExn("Means were not invariant", _) }, ) }) @@ -109,12 +109,12 @@ describe("Means are invariant", () => { ) testAll("with two of the same distribution", distributions, dist => { - E.R.liftM2(testSubtractionMean, dist, dist)->E.R.toExn + E.R.liftM2(testSubtractionMean, dist, dist)->E.R.toExn("Means were not invariant", _) }) testAll("with two different distributions", pairsOfDifferentDistributions, dists => { let (dist1, dist2) = dists - E.R.liftM2(testSubtractionMean, dist1, dist2)->E.R.toExn + E.R.liftM2(testSubtractionMean, dist1, dist2)->E.R.toExn("Means were not invariant", _) }) testAll( @@ -122,7 +122,7 @@ describe("Means are invariant", () => { pairsOfDifferentDistributions, dists => { let (dist1, dist2) = dists - E.R.liftM2(testSubtractionMean, dist2, dist1)->E.R.toExn + E.R.liftM2(testSubtractionMean, dist2, dist1)->E.R.toExn("Means were not invariant", _) }, ) }) @@ -136,12 +136,12 @@ describe("Means are invariant", () => { ) testAll("with two of the same distribution", distributions, dist => { - E.R.liftM2(testMultiplicationMean, dist, dist)->E.R.toExn + E.R.liftM2(testMultiplicationMean, dist, dist)->E.R.toExn("Means were not invariant", _) }) testAll("with two different distributions", pairsOfDifferentDistributions, dists => { let (dist1, dist2) = dists - E.R.liftM2(testMultiplicationMean, dist1, dist2)->E.R.toExn + E.R.liftM2(testMultiplicationMean, dist1, dist2)->E.R.toExn("Means were not invariant", _) }) testAll( @@ -149,7 +149,7 @@ describe("Means are invariant", () => { pairsOfDifferentDistributions, dists => { let (dist1, dist2) = dists - E.R.liftM2(testMultiplicationMean, dist2, dist1)->E.R.toExn + E.R.liftM2(testMultiplicationMean, dist2, dist1)->E.R.toExn("Means were not invariant", _) }, ) }) diff --git a/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSetDist.res b/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSetDist.res index bb5f17d6..d62396f8 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSetDist.res +++ b/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSetDist.res @@ -96,6 +96,6 @@ let map2 = ( // I could prove this to the type system (say, creating a {first: float, second: float, ..., fifth: float, rest: array} // But doing so would take too much time, so I'll leave it as an assertion E.A.R.firstErrorOrOpen(samples)->E.R2.fmap(x => - E.R.assertOk("Input of samples should be larger than 5", make(x)) + E.R.toExn("Input of samples should be larger than 5", make(x)) ) } diff --git a/packages/squiggle-lang/src/rescript/Utility/E.res b/packages/squiggle-lang/src/rescript/Utility/E.res index 779ab03a..3b011877 100644 --- a/packages/squiggle-lang/src/rescript/Utility/E.res +++ b/packages/squiggle-lang/src/rescript/Utility/E.res @@ -160,12 +160,12 @@ module R = { let id = e => e |> result(U.id, U.id) let fmap = Rationale.Result.fmap let bind = Rationale.Result.bind - let toExn = Belt.Result.getExn - let assertOk = (message: string, x: result<'a, 'b>): 'a => + let toExn = (msg: string, x: result<'a, 'b>): 'a => switch x { | Ok(r) => r - | Error(_) => raise(Assertion(message)) + | Error(_) => raise(Assertion(msg)) } + let default = (default, res: Belt.Result.t<'a, 'b>) => switch res { | Ok(r) => r From c7e601e15b7a46cf5cd742d6c54289caf85f81ac Mon Sep 17 00:00:00 2001 From: Sam Nolan Date: Sat, 23 Apr 2022 14:09:06 -0400 Subject: [PATCH 07/11] Remove NaN from pointwise operations --- .../Distributions/Invariants/Means_test.res | 6 +- .../ReducerInterface_Distribution_test.res | 6 -- packages/squiggle-lang/jest.config.js | 1 + .../DistributionOperation.res | 8 +-- .../Distributions/DistributionTypes.res | 37 ++-------- .../Distributions/GenericDist/GenericDist.res | 24 +++---- .../GenericDist/GenericDist.resi | 8 +-- .../AlgebraicShapeCombination.res | 11 +-- .../Distributions/PointSetDist/Continuous.res | 27 ++++++-- .../Distributions/PointSetDist/Discrete.res | 4 +- .../Distributions/PointSetDist/Mixed.res | 22 +++--- .../PointSetDist/PointSetDist.res | 23 +++++-- .../ReducerInterface_GenericDistribution.res | 8 +-- .../squiggle-lang/src/rescript/Utility/E.res | 26 +++++++ .../src/rescript/Utility/Operation.res | 69 +++++++++---------- .../src/rescript/Utility/XYShape.res | 26 ++++++- packages/website/docs/Features/Functions.mdx | 10 --- 17 files changed, 168 insertions(+), 148 deletions(-) diff --git a/packages/squiggle-lang/__tests__/Distributions/Invariants/Means_test.res b/packages/squiggle-lang/__tests__/Distributions/Invariants/Means_test.res index fbc17114..03f2e52d 100644 --- a/packages/squiggle-lang/__tests__/Distributions/Invariants/Means_test.res +++ b/packages/squiggle-lang/__tests__/Distributions/Invariants/Means_test.res @@ -50,7 +50,11 @@ module Internals = { let dist1 = dist1'->DistributionTypes.Symbolic let dist2 = dist2'->DistributionTypes.Symbolic let received = - distOp(dist1, dist2)->E.R2.fmap(mean)->E.R2.fmap(run)->E.R2.fmap(toFloat)->E.R.toExn("Expected float", _) + distOp(dist1, dist2) + ->E.R2.fmap(mean) + ->E.R2.fmap(run) + ->E.R2.fmap(toFloat) + ->E.R.toExn("Expected float", _) let expected = floatOp(runMean(dist1), runMean(dist2)) switch received { | None => expectImpossiblePath(description) diff --git a/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res b/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res index 3af5c0cc..d4199f89 100644 --- a/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res +++ b/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res @@ -97,12 +97,6 @@ describe("eval on distribution functions", () => { testEval("log10(uniform(5,8))", "Ok(Sample Set Distribution)") }) - describe("dotLog", () => { - testEval("dotLog(normal(5,2), 3)", "Ok(Point Set Distribution)") - testEval("dotLog(normal(5,2), 3)", "Ok(Point Set Distribution)") - testEval("dotLog(normal(5,2), normal(10,1))", "Ok(Point Set Distribution)") - }) - describe("dotAdd", () => { testEval("dotAdd(normal(5,2), lognormal(10,2))", "Ok(Point Set Distribution)") testEval("dotAdd(normal(5,2), 3)", "Ok(Point Set Distribution)") diff --git a/packages/squiggle-lang/jest.config.js b/packages/squiggle-lang/jest.config.js index 71babb3c..520f4693 100644 --- a/packages/squiggle-lang/jest.config.js +++ b/packages/squiggle-lang/jest.config.js @@ -2,6 +2,7 @@ module.exports = { preset: "ts-jest", testEnvironment: "node", + bail: true, setupFilesAfterEnv: [ "/../../node_modules/bisect_ppx/src/runtime/js/jest.bs.js", ], diff --git a/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.res b/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.res index 77d6acc5..21bf2543 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.res +++ b/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.res @@ -160,14 +160,14 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => { ->GenericDist.algebraicCombination(~toPointSetFn, ~toSampleSetFn, ~arithmeticOperation, ~t2) ->E.R2.fmap(r => Dist(r)) ->OutputLocal.fromResult - | ToDistCombination(Pointwise, arithmeticOperation, #Dist(t2)) => + | ToDistCombination(Pointwise, algebraicCombination, #Dist(t2)) => dist - ->GenericDist.pointwiseCombination(~toPointSetFn, ~arithmeticOperation, ~t2) + ->GenericDist.pointwiseCombination(~toPointSetFn, ~algebraicCombination, ~t2) ->E.R2.fmap(r => Dist(r)) ->OutputLocal.fromResult - | ToDistCombination(Pointwise, arithmeticOperation, #Float(f)) => + | ToDistCombination(Pointwise, algebraicCombination, #Float(f)) => dist - ->GenericDist.pointwiseCombinationFloat(~toPointSetFn, ~arithmeticOperation, ~f) + ->GenericDist.pointwiseCombinationFloat(~toPointSetFn, ~algebraicCombination, ~f) ->E.R2.fmap(r => Dist(r)) ->OutputLocal.fromResult } diff --git a/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res b/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res index 8a3541c0..07b8a353 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res +++ b/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res @@ -46,30 +46,14 @@ module Error = { } @genType -module Operation = { +module DistributionOperation = { + @genType + type pointsetXSelection = [#Linear | #ByWeight] + type direction = | Algebraic | Pointwise - type arithmeticOperation = [ - | #Add - | #Multiply - | #Subtract - | #Divide - | #Power - | #Logarithm - ] - - let arithmeticToFn = (arithmetic: arithmeticOperation) => - switch arithmetic { - | #Add => \"+." - | #Multiply => \"*." - | #Subtract => \"-." - | #Power => \"**" - | #Divide => \"/." - | #Logarithm => (a, b) => log(a) /. log(b) - } - type toFloat = [ | #Cdf(float) | #Inv(float) @@ -78,11 +62,6 @@ module Operation = { | #Sample ] - @genType - type pointsetXSelection = [#Linear | #ByWeight] -} - -module DistributionOperation = { type toDist = | Normalize | ToPointSet @@ -99,13 +78,9 @@ module DistributionOperation = { | ToSparkline(int) type fromDist = - | ToFloat(Operation.toFloat) + | ToFloat(toFloat) | ToDist(toDist) - | ToDistCombination( - Operation.direction, - Operation.arithmeticOperation, - [#Dist(genericDist) | #Float(float)], - ) + | ToDistCombination(direction, Operation.Algebraic.t, [#Dist(genericDist) | #Float(float)]) | ToString(toString) | ToBool(toBool) diff --git a/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.res b/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.res index 55dfe746..6061e528 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.res +++ b/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.res @@ -68,7 +68,7 @@ let toPointSet = ( t, ~xyPointLength, ~sampleCount, - ~xSelection: DistributionTypes.Operation.pointsetXSelection=#ByWeight, + ~xSelection: DistributionTypes.DistributionOperation.pointsetXSelection=#ByWeight, (), ): result => { switch (t: t) { @@ -148,7 +148,7 @@ let truncate = Truncate.run */ module AlgebraicCombination = { let tryAnalyticalSimplification = ( - arithmeticOperation: DistributionTypes.Operation.arithmeticOperation, + arithmeticOperation: Operation.algebraicOperation, t1: t, t2: t, ): option> => @@ -174,7 +174,7 @@ module AlgebraicCombination = { let runMonteCarlo = ( toSampleSet: toSampleSetFn, - arithmeticOperation: DistributionTypes.Operation.arithmeticOperation, + arithmeticOperation: Operation.algebraicOperation, t1: t, t2: t, ): result => { @@ -241,27 +241,23 @@ let algebraicCombination = AlgebraicCombination.run let pointwiseCombination = ( t1: t, ~toPointSetFn: toPointSetFn, - ~arithmeticOperation, + ~algebraicCombination: Operation.algebraicOperation, ~t2: t, ): result => { - E.R.merge(toPointSetFn(t1), toPointSetFn(t2)) - ->E.R2.fmap(((t1, t2)) => - PointSetDist.combinePointwise( - DistributionTypes.Operation.arithmeticToFn(arithmeticOperation), - t1, - t2, - ) + E.R.merge(toPointSetFn(t1), toPointSetFn(t2))->E.R.bind(((t1, t2)) => + PointSetDist.combinePointwise(Operation.Algebraic.toFn(algebraicCombination), t1, t2) + ->E.R2.fmap(r => DistributionTypes.PointSet(r)) + ->E.R2.errMap(err => DistributionTypes.OperationError(err)) ) - ->E.R2.fmap(r => DistributionTypes.PointSet(r)) } let pointwiseCombinationFloat = ( t: t, ~toPointSetFn: toPointSetFn, - ~arithmeticOperation: DistributionTypes.Operation.arithmeticOperation, + ~algebraicCombination: Operation.algebraicOperation, ~f: float, ): result => { - let m = switch arithmeticOperation { + let m = switch algebraicCombination { | #Add | #Subtract => Error(DistributionTypes.DistributionVerticalShiftIsInvalid) | (#Multiply | #Divide | #Power | #Logarithm) as arithmeticOperation => toPointSetFn(t)->E.R.bind(t => { diff --git a/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.resi b/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.resi index 276ca4e3..ce32a39b 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.resi +++ b/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.resi @@ -28,7 +28,7 @@ let toPointSet: ( t, ~xyPointLength: int, ~sampleCount: int, - ~xSelection: DistributionTypes.Operation.pointsetXSelection=?, + ~xSelection: DistributionTypes.DistributionOperation.pointsetXSelection=?, unit, ) => result let toSparkline: (t, ~sampleCount: int, ~bucketCount: int=?, unit) => result @@ -45,21 +45,21 @@ let algebraicCombination: ( t, ~toPointSetFn: toPointSetFn, ~toSampleSetFn: toSampleSetFn, - ~arithmeticOperation: DistributionTypes.Operation.arithmeticOperation, + ~arithmeticOperation: Operation.algebraicOperation, ~t2: t, ) => result let pointwiseCombination: ( t, ~toPointSetFn: toPointSetFn, - ~arithmeticOperation: DistributionTypes.Operation.arithmeticOperation, + ~algebraicCombination: Operation.algebraicOperation, ~t2: t, ) => result let pointwiseCombinationFloat: ( t, ~toPointSetFn: toPointSetFn, - ~arithmeticOperation: DistributionTypes.Operation.arithmeticOperation, + ~algebraicCombination: Operation.algebraicOperation, ~f: float, ) => result diff --git a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/AlgebraicShapeCombination.res b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/AlgebraicShapeCombination.res index 908d100b..fd7871cd 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/AlgebraicShapeCombination.res +++ b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/AlgebraicShapeCombination.res @@ -243,10 +243,13 @@ let combineShapesContinuousDiscrete = ( outXYShapes |> E.A.fmap(XYShape.T.fromZippedArray) |> E.A.fold_left( - XYShape.PointwiseCombination.combine( - \"+.", - XYShape.XtoY.continuousInterpolator(#Linear, #UseZero), - ), + (acc, x) => + XYShape.PointwiseCombination.combine( + (a, b) => Ok(a +. b), + XYShape.XtoY.continuousInterpolator(#Linear, #UseZero), + acc, + x, + )->E.R.toExn("Error, unexpected failure", _), XYShape.T.empty, ) } diff --git a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Continuous.res b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Continuous.res index 5512b5cb..0536cc83 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Continuous.res +++ b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Continuous.res @@ -88,10 +88,10 @@ let stepwiseToLinear = (t: t): t => let combinePointwise = ( ~integralSumCachesFn=(_, _) => None, ~distributionType: PointSetTypes.distributionType=#PDF, - fn: (float, float) => float, + fn: (float, float) => result, t1: PointSetTypes.continuousShape, t2: PointSetTypes.continuousShape, -): PointSetTypes.continuousShape => { +): result => { // If we're adding the distributions, and we know the total of each, then we // can just sum them up. Otherwise, all bets are off. let combinedIntegralSum = Common.combineIntegralSums( @@ -119,9 +119,8 @@ let combinePointwise = ( let interpolator = XYShape.XtoY.continuousInterpolator(t1.interpolation, extrapolation) - make( - ~integralSumCache=combinedIntegralSum, - XYShape.PointwiseCombination.combine(fn, interpolator, t1.xyShape, t2.xyShape), + XYShape.PointwiseCombination.combine(fn, interpolator, t1.xyShape, t2.xyShape)->E.R2.fmap(x => + make(~integralSumCache=combinedIntegralSum, x) ) } @@ -140,11 +139,25 @@ let updateIntegralSumCache = (integralSumCache, t: t): t => { let updateIntegralCache = (integralCache, t: t): t => {...t, integralCache: integralCache} +let sum = ( + ~integralSumCachesFn: (float, float) => option=(_, _) => None, + continuousShapes, +): t => + continuousShapes |> E.A.fold_left( + (x, y) => + combinePointwise(~integralSumCachesFn, (a, b) => Ok(a +. b), x, y)->E.R.toExn( + "Addition should never fail", + _, + ), + empty, + ) + let reduce = ( ~integralSumCachesFn: (float, float) => option=(_, _) => None, - fn, + fn: (float, float) => result, continuousShapes, -) => continuousShapes |> E.A.fold_left(combinePointwise(~integralSumCachesFn, fn), empty) +): result => + continuousShapes |> E.A.R.foldM(combinePointwise(~integralSumCachesFn, fn), empty) let mapYResult = ( ~integralSumCacheFn=_ => None, diff --git a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Discrete.res b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Discrete.res index 92fdc7f0..fdc921c6 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Discrete.res +++ b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Discrete.res @@ -49,11 +49,11 @@ let combinePointwise = ( make( ~integralSumCache=combinedIntegralSum, XYShape.PointwiseCombination.combine( - \"+.", + (a, b) => Ok(a +. b), XYShape.XtoY.discreteInterpolator, t1.xyShape, t2.xyShape, - ), + )->E.R.toExn("Addition operation should never fail", _), ) } diff --git a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Mixed.res b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Mixed.res index 62f9d8dd..9961b51d 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Mixed.res +++ b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Mixed.res @@ -146,8 +146,7 @@ module T = Dist({ let discreteIntegral = Continuous.stepwiseToLinear(Discrete.T.Integral.get(t.discrete)) Continuous.make( - XYShape.PointwiseCombination.combine( - \"+.", + XYShape.PointwiseCombination.addCombine( XYShape.XtoY.continuousInterpolator(#Linear, #UseOutermostPoints), Continuous.getShape(continuousIntegral), Continuous.getShape(discreteIntegral), @@ -280,7 +279,7 @@ let combineAlgebraically = (op: Operation.convolutionOperation, t1: t, t2: t): t let ccConvResult = Continuous.combineAlgebraically(op, t1.continuous, t2.continuous) let dcConvResult = Continuous.combineAlgebraicallyWithDiscrete(op, t2.continuous, t1.discrete) let cdConvResult = Continuous.combineAlgebraicallyWithDiscrete(op, t1.continuous, t2.discrete) - let continuousConvResult = Continuous.reduce(\"+.", [ccConvResult, dcConvResult, cdConvResult]) + let continuousConvResult = Continuous.sum([ccConvResult, dcConvResult, cdConvResult]) // ... finally, discrete (*) discrete => discrete, obviously: let discreteConvResult = Discrete.combineAlgebraically(op, t1.discrete, t2.discrete) @@ -302,10 +301,10 @@ let combineAlgebraically = (op: Operation.convolutionOperation, t1: t, t2: t): t let combinePointwise = ( ~integralSumCachesFn=(_, _) => None, ~integralCachesFn=(_, _) => None, - fn, + fn: (float, float) => result, t1: t, t2: t, -): t => { +): result => { let reducedDiscrete = [t1, t2] |> E.A.fmap(toDiscrete) |> E.A.O.concatSomes |> Discrete.reduce(~integralSumCachesFn) @@ -326,11 +325,12 @@ let combinePointwise = ( t1.integralCache, t2.integralCache, ) - - make( - ~integralSumCache=combinedIntegralSum, - ~integralCache=combinedIntegral, - ~discrete=reducedDiscrete, - ~continuous=reducedContinuous, + reducedContinuous->E.R2.fmap(continuous => + make( + ~integralSumCache=combinedIntegralSum, + ~integralCache=combinedIntegral, + ~discrete=reducedDiscrete, + ~continuous, + ) ) } diff --git a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetDist.res b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetDist.res index c2cf0d9c..798d90ca 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetDist.res +++ b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetDist.res @@ -60,19 +60,28 @@ let combinePointwise = ( PointSetTypes.continuousShape, PointSetTypes.continuousShape, ) => option=(_, _) => None, - fn, + fn: (float, float) => result, t1: t, t2: t, -) => +): result => switch (t1, t2) { | (Continuous(m1), Continuous(m2)) => - PointSetTypes.Continuous(Continuous.combinePointwise(~integralSumCachesFn, fn, m1, m2)) + Continuous.combinePointwise( + ~integralSumCachesFn, + fn, + m1, + m2, + )->E.R2.fmap(x => PointSetTypes.Continuous(x)) | (Discrete(m1), Discrete(m2)) => - PointSetTypes.Discrete(Discrete.combinePointwise(~integralSumCachesFn, m1, m2)) + Ok(PointSetTypes.Discrete(Discrete.combinePointwise(~integralSumCachesFn, m1, m2))) | (m1, m2) => - PointSetTypes.Mixed( - Mixed.combinePointwise(~integralSumCachesFn, ~integralCachesFn, fn, toMixed(m1), toMixed(m2)), - ) + Mixed.combinePointwise( + ~integralSumCachesFn, + ~integralCachesFn, + fn, + toMixed(m1), + toMixed(m2), + )->E.R2.fmap(x => PointSetTypes.Mixed(x)) } module T = Dist({ diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res index 68ac7393..eb28a3e5 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res @@ -24,7 +24,6 @@ module Helpers = { | "dotPow" => #Power | "multiply" => #Multiply | "dotMultiply" => #Multiply - | "dotLog" => #Logarithm | _ => #Multiply } @@ -41,7 +40,7 @@ module Helpers = { } let toFloatFn = ( - fnCall: DistributionTypes.Operation.toFloat, + fnCall: DistributionTypes.DistributionOperation.toFloat, dist: DistributionTypes.genericDist, ) => { FromDist(DistributionTypes.DistributionOperation.ToFloat(fnCall), dist) @@ -243,15 +242,12 @@ let dispatchToGenericOutput = (call: ExpressionValue.functionCall): option< | "dotMultiply" | "dotSubtract" | "dotDivide" - | "dotPow" - | "dotLog") as arithmetic, + | "dotPow") as arithmetic, [_, _] as args, ) => Helpers.catchAndConvertTwoArgsToDists(args)->E.O2.fmap(((fst, snd)) => Helpers.twoDiststoDistFn(Pointwise, arithmetic, fst, snd) ) - | ("dotLog", [EvDistribution(a)]) => - Helpers.twoDiststoDistFn(Pointwise, "dotLog", a, GenericDist.fromFloat(Math.e))->Some | ("dotExp", [EvDistribution(a)]) => Helpers.twoDiststoDistFn(Pointwise, "dotPow", GenericDist.fromFloat(Math.e), a)->Some | _ => None diff --git a/packages/squiggle-lang/src/rescript/Utility/E.res b/packages/squiggle-lang/src/rescript/Utility/E.res index 3b011877..78c872ff 100644 --- a/packages/squiggle-lang/src/rescript/Utility/E.res +++ b/packages/squiggle-lang/src/rescript/Utility/E.res @@ -192,6 +192,7 @@ module R = { | Ok(f) => fmap(f, a) | Error(err) => Error(err) } + // (a1 -> a2 -> r) -> m a1 -> m a2 -> m r // not in Rationale let liftM2: (('a, 'b) => 'c, result<'a, 'd>, result<'b, 'd>) => result<'c, 'd> = (op, xR, yR) => { ap'(fmap(op, xR), yR) @@ -444,6 +445,31 @@ module A = { bringErrorUp |> Belt.Result.map(_, forceOpen) } let filterOk = (x: array>): array<'a> => fmap(R.toOption, x)->O.concatSomes + + let forM = (x: array<'a>, fn: 'a => result<'b, 'c>): result, 'c> => + firstErrorOrOpen(fmap(fn, x)) + + let foldM = (fn: ('c, 'a) => result<'b, 'e>, init: 'c, x: array<'a>): result<'c, 'e> => { + let acc = ref(init) + let final = ref(Ok()) + let break = ref(false) + let i = ref(0) + + while break.contents != true && i.contents < length(x) { + switch fn(acc.contents, x[i.contents]) { + | Ok(r) => acc := r + | Error(err) => { + final := Error(err) + break := true + } + } + i := i.contents + 1 + } + switch final.contents { + | Ok(_) => Ok(acc.contents) + | Error(err) => Error(err) + } + } } module Sorted = { diff --git a/packages/squiggle-lang/src/rescript/Utility/Operation.res b/packages/squiggle-lang/src/rescript/Utility/Operation.res index 59adf1c5..7bf732ea 100644 --- a/packages/squiggle-lang/src/rescript/Utility/Operation.res +++ b/packages/squiggle-lang/src/rescript/Utility/Operation.res @@ -51,6 +51,31 @@ module Error = { } } +let power = (a: float, b: float): result => + if a >= 0.0 { + Ok(a ** b) + } else { + Error(ComplexNumberError) + } + +let divide = (a: float, b: float): result => + if b != 0.0 { + Ok(a /. b) + } else { + Error(DivisionByZeroError) + } + +let logarithm = (a: float, b: float): result => + if b == 1. { + Error(DivisionByZeroError) + } else if b == 0. { + Ok(0.) + } else if a > 0.0 && b > 0.0 { + Ok(log(a) /. log(b)) + } else { + Error(ComplexNumberError) + } + module Algebraic = { type t = algebraicOperation let toFn: (t, float, float) => result = (x, a, b) => @@ -58,26 +83,9 @@ module Algebraic = { | #Add => Ok(a +. b) | #Subtract => Ok(a -. b) | #Multiply => Ok(a *. b) - | #Power => - if a >= 0.0 { - Ok(a ** b) - } else { - Error(ComplexNumberError) - } - | #Divide => - if b != 0.0 { - Ok(a /. b) - } else { - Error(DivisionByZeroError) - } - | #Logarithm => - if b == 1. { - Error(DivisionByZeroError) - } else if a > 0.0 && b > 0.0 { - Ok(log(a) /. log(b)) - } else { - Error(ComplexNumberError) - } + | #Power => power(a, b) + | #Divide => divide(a, b) + | #Logarithm => logarithm(a, b) } let toString = x => @@ -124,24 +132,9 @@ module Scale = { let toFn = (x: t, a: float, b: float): result => switch x { | #Multiply => Ok(a *. b) - | #Divide => - if b != 0.0 { - Ok(a /. b) - } else { - Error(DivisionByZeroError) - } - | #Power => - if a > 0.0 { - Ok(a ** b) - } else { - Error(ComplexNumberError) - } - | #Logarithm => - if a > 0.0 && b > 0.0 { - Ok(log(a) /. log(b)) - } else { - Error(DivisionByZeroError) - } + | #Divide => divide(a, b) + | #Power => power(a, b) + | #Logarithm => logarithm(a, b) } let format = (operation: t, value, scaleBy) => diff --git a/packages/squiggle-lang/src/rescript/Utility/XYShape.res b/packages/squiggle-lang/src/rescript/Utility/XYShape.res index d792f70e..4bc19dc1 100644 --- a/packages/squiggle-lang/src/rescript/Utility/XYShape.res +++ b/packages/squiggle-lang/src/rescript/Utility/XYShape.res @@ -233,7 +233,12 @@ module Zipped = { module PointwiseCombination = { // t1Interpolator and t2Interpolator are functions from XYShape.XtoY, e.g. linearBetweenPointsExtrapolateFlat. - let combine: ((float, float) => float, interpolator, T.t, T.t) => T.t = %raw(` + let combine: ( + (float, float) => result, + interpolator, + T.t, + T.t, + ) => result = %raw(` // This function combines two xyShapes by looping through both of them simultaneously. // It always moves on to the next smallest x, whether that's in the first or second input's xs, // and interpolates the value on the other side, thus accumulating xs and ys. @@ -281,13 +286,28 @@ module PointwiseCombination = { } outX.push(x); - outY.push(fn(ya, yb)); + + // Here I check whether the operation was a success. If it was + // keep going. Otherwise, stop and throw the error back to user + let newY = fn(ya, yb); + if(newY.TAG === 0){ + outY.push(newY._0); + } + else { + return newY; + } } - return {xs: outX, ys: outY}; + return {TAG: 0, _0: {xs: outX, ys: outY}, [Symbol.for("name")]: "Ok"}; } `) + let addCombine = (interpolator: interpolator, t1: T.t, t2: T.t): T.t => + combine((a, b) => Ok(a +. b), interpolator, t1, t2)->E.R.toExn( + "Add operation should never fail", + _, + ) + let combineEvenXs = (~fn, ~xToYSelection, sampleCount, t1: T.t, t2: T.t) => switch (E.A.length(t1.xs), E.A.length(t2.xs)) { | (0, 0) => T.empty diff --git a/packages/website/docs/Features/Functions.mdx b/packages/website/docs/Features/Functions.mdx index c85a3438..bc8be820 100644 --- a/packages/website/docs/Features/Functions.mdx +++ b/packages/website/docs/Features/Functions.mdx @@ -255,16 +255,6 @@ dist2 = triangular(1,2,3) dist1 .^ dist2`} /> -### Pointwise logarithm - -TODO: write about the semantics and the case handling re scalar vs. dist and log base. - - - ## Standard functions on distributions ### Probability density function From 7e8c16b923a94a5207e021d10d96e142ff430e22 Mon Sep 17 00:00:00 2001 From: Sam Nolan Date: Sat, 23 Apr 2022 14:13:38 -0400 Subject: [PATCH 08/11] Rename Other to OtherError --- .../DistributionOperation/DistributionOperation.res | 4 ++-- .../src/rescript/Distributions/DistributionTypes.res | 6 +++--- .../src/rescript/Distributions/GenericDist/GenericDist.res | 2 +- .../ReducerInterface_GenericDistribution.res | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.res b/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.res index 21bf2543..9cb514fe 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.res +++ b/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.res @@ -201,8 +201,8 @@ module Output = { | (FromDist(fromDist), Dist(o)) => Ok(FromDist(fromDist, o)) | (FromFloat(fromDist), Float(o)) => Ok(FromFloat(fromDist, o)) | (_, GenDistError(r)) => Error(r) - | (FromDist(_), _) => Error(Other("Expected dist, got something else")) - | (FromFloat(_), _) => Error(Other("Expected float, got something else")) + | (FromDist(_), _) => Error(OtherError("Expected dist, got something else")) + | (FromFloat(_), _) => Error(OtherError("Expected float, got something else")) } newFnCall->E.R2.fmap(run(~env))->OutputLocal.fromResult } diff --git a/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res b/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res index 07b8a353..9f69f848 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res +++ b/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res @@ -14,13 +14,13 @@ type error = | OperationError(Operation.Error.invalidOperationError) | PointSetConversionError(SampleSetDist.pointsetConversionError) | SparklineError(PointSetTypes.sparklineError) // This type of error is for when we find a sparkline of a discrete distribution. This should probably at some point be actually implemented - | Other(string) + | OtherError(string) @genType module Error = { type t = error - let fromString = (s: string): t => Other(s) + let fromString = (s: string): t => OtherError(s) @genType let toString = (err: error): string => @@ -33,7 +33,7 @@ module Error = { | OperationError(err) => Operation.Error.invalidOperationErrorToString(err) | PointSetConversionError(err) => SampleSetDist.pointsetConversionErrorToString(err) | SparklineError(err) => PointSetTypes.sparklineErrorToString(err) - | Other(s) => s + | OtherError(s) => s } let resultStringToResultError: result<'a, string> => result<'a, error> = n => diff --git a/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.res b/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.res index 6061e528..454bdc77 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.res +++ b/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.res @@ -284,7 +284,7 @@ let mixture = ( ~pointwiseAddFn: pointwiseAddFn, ) => { if E.A.length(values) == 0 { - Error(DistributionTypes.Other("Mixture error: mixture must have at least 1 element")) + Error(DistributionTypes.OtherError("Mixture error: mixture must have at least 1 element")) } else { let totalWeight = values->E.A2.fmap(E.Tuple2.second)->E.A.Floats.sum let properlyWeightedValues = diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res index eb28a3e5..f39dc932 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res @@ -172,7 +172,7 @@ module SymbolicConstructors = { ): option => switch symbolicResult { | Ok(r) => Some(Dist(Symbolic(r))) - | Error(r) => Some(GenDistError(Other(r))) + | Error(r) => Some(GenDistError(OtherError(r))) } } From b44a955338f094c3ba66f953526bf248260f8095 Mon Sep 17 00:00:00 2001 From: Sam Nolan Date: Sat, 23 Apr 2022 14:16:11 -0400 Subject: [PATCH 09/11] Fix bundle errors --- packages/squiggle-lang/src/rescript/Utility/Operation.res | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/squiggle-lang/src/rescript/Utility/Operation.res b/packages/squiggle-lang/src/rescript/Utility/Operation.res index 7bf732ea..de3f9c59 100644 --- a/packages/squiggle-lang/src/rescript/Utility/Operation.res +++ b/packages/squiggle-lang/src/rescript/Utility/Operation.res @@ -76,7 +76,9 @@ let logarithm = (a: float, b: float): result Error(ComplexNumberError) } +@genType module Algebraic = { + @genType type t = algebraicOperation let toFn: (t, float, float) => result = (x, a, b) => switch x { From 45442684293d760d13d6c33febd4ba1626e7e929 Mon Sep 17 00:00:00 2001 From: Sam Nolan Date: Sat, 23 Apr 2022 14:25:08 -0400 Subject: [PATCH 10/11] Clean up means testing to remove some duplication --- .../Distributions/Invariants/Means_test.res | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/packages/squiggle-lang/__tests__/Distributions/Invariants/Means_test.res b/packages/squiggle-lang/__tests__/Distributions/Invariants/Means_test.res index 03f2e52d..99e3e5a3 100644 --- a/packages/squiggle-lang/__tests__/Distributions/Invariants/Means_test.res +++ b/packages/squiggle-lang/__tests__/Distributions/Invariants/Means_test.res @@ -84,14 +84,16 @@ let {testOperationMean, distributions, pairsOfDifferentDistributions, epsilon} = describe("Means are invariant", () => { describe("for addition", () => { let testAdditionMean = testOperationMean(algebraicAdd, "algebraicAdd", \"+.", ~epsilon) + let testAddInvariant = (t1, t2) => + E.R.liftM2(testAdditionMean, t1, t2)->E.R.toExn("Means were not invariant", _) testAll("with two of the same distribution", distributions, dist => { - E.R.liftM2(testAdditionMean, dist, dist)->E.R.toExn("Means were not invariant", _) + testAddInvariant(dist, dist) }) testAll("with two different distributions", pairsOfDifferentDistributions, dists => { let (dist1, dist2) = dists - E.R.liftM2(testAdditionMean, dist1, dist2)->E.R.toExn("Means were not invariant", _) + testAddInvariant(dist1, dist2) }) testAll( @@ -99,7 +101,7 @@ describe("Means are invariant", () => { pairsOfDifferentDistributions, dists => { let (dist1, dist2) = dists - E.R.liftM2(testAdditionMean, dist2, dist1)->E.R.toExn("Means were not invariant", _) + testAddInvariant(dist1, dist2) }, ) }) @@ -111,14 +113,16 @@ describe("Means are invariant", () => { \"-.", ~epsilon, ) + let testSubtractInvariant = (t1, t2) => + E.R.liftM2(testSubtractionMean, t1, t2)->E.R.toExn("Means were not invariant", _) testAll("with two of the same distribution", distributions, dist => { - E.R.liftM2(testSubtractionMean, dist, dist)->E.R.toExn("Means were not invariant", _) + testSubtractInvariant(dist, dist) }) testAll("with two different distributions", pairsOfDifferentDistributions, dists => { let (dist1, dist2) = dists - E.R.liftM2(testSubtractionMean, dist1, dist2)->E.R.toExn("Means were not invariant", _) + testSubtractInvariant(dist1, dist2) }) testAll( @@ -126,7 +130,7 @@ describe("Means are invariant", () => { pairsOfDifferentDistributions, dists => { let (dist1, dist2) = dists - E.R.liftM2(testSubtractionMean, dist2, dist1)->E.R.toExn("Means were not invariant", _) + testSubtractInvariant(dist1, dist2) }, ) }) @@ -138,14 +142,16 @@ describe("Means are invariant", () => { \"*.", ~epsilon, ) + let testMultiplicationInvariant = (t1, t2) => + E.R.liftM2(testMultiplicationMean, t1, t2)->E.R.toExn("Means were not invariant", _) testAll("with two of the same distribution", distributions, dist => { - E.R.liftM2(testMultiplicationMean, dist, dist)->E.R.toExn("Means were not invariant", _) + testMultiplicationInvariant(dist, dist) }) testAll("with two different distributions", pairsOfDifferentDistributions, dists => { let (dist1, dist2) = dists - E.R.liftM2(testMultiplicationMean, dist1, dist2)->E.R.toExn("Means were not invariant", _) + testMultiplicationInvariant(dist1, dist2) }) testAll( @@ -153,7 +159,7 @@ describe("Means are invariant", () => { pairsOfDifferentDistributions, dists => { let (dist1, dist2) = dists - E.R.liftM2(testMultiplicationMean, dist2, dist1)->E.R.toExn("Means were not invariant", _) + testMultiplicationInvariant(dist1, dist2) }, ) }) From df4b734a4903b2211283366b6db196c280d1925e Mon Sep 17 00:00:00 2001 From: Sam Nolan Date: Sat, 23 Apr 2022 14:35:49 -0400 Subject: [PATCH 11/11] Rename invalidOperationError --- .../Distributions/DistributionTypes.res | 4 ++-- .../Distributions/GenericDist/GenericDist.res | 2 +- .../Distributions/PointSetDist/Continuous.res | 2 +- .../PointSetDist/PointSetDist.res | 4 ++-- .../SampleSetDist/SampleSetDist.res | 9 ++++----- .../SymbolicDist/SymbolicDistTypes.res | 2 +- .../src/rescript/Utility/Operation.res | 20 ++++++++++--------- .../src/rescript/Utility/XYShape.res | 4 ++-- 8 files changed, 24 insertions(+), 23 deletions(-) diff --git a/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res b/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res index 9f69f848..98c49a21 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res +++ b/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res @@ -11,7 +11,7 @@ type error = | DistributionVerticalShiftIsInvalid | TooFewSamples | ArgumentError(string) - | OperationError(Operation.Error.invalidOperationError) + | OperationError(Operation.Error.t) | PointSetConversionError(SampleSetDist.pointsetConversionError) | SparklineError(PointSetTypes.sparklineError) // This type of error is for when we find a sparkline of a discrete distribution. This should probably at some point be actually implemented | OtherError(string) @@ -30,7 +30,7 @@ module Error = { | DistributionVerticalShiftIsInvalid => "Distribution Vertical Shift is Invalid" | ArgumentError(s) => `Argument Error ${s}` | TooFewSamples => "Too Few Samples" - | OperationError(err) => Operation.Error.invalidOperationErrorToString(err) + | OperationError(err) => Operation.Error.toString(err) | PointSetConversionError(err) => SampleSetDist.pointsetConversionErrorToString(err) | SparklineError(err) => PointSetTypes.sparklineErrorToString(err) | OtherError(s) => s diff --git a/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.res b/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.res index 454bdc77..47fb2c01 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.res +++ b/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.res @@ -151,7 +151,7 @@ module AlgebraicCombination = { arithmeticOperation: Operation.algebraicOperation, t1: t, t2: t, - ): option> => + ): option> => switch (arithmeticOperation, t1, t2) { | (arithmeticOperation, Symbolic(d1), Symbolic(d2)) => switch SymbolicDist.T.tryAnalyticalSimplification(d1, d2, arithmeticOperation) { diff --git a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Continuous.res b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Continuous.res index 0536cc83..a8542bae 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Continuous.res +++ b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Continuous.res @@ -88,7 +88,7 @@ let stepwiseToLinear = (t: t): t => let combinePointwise = ( ~integralSumCachesFn=(_, _) => None, ~distributionType: PointSetTypes.distributionType=#PDF, - fn: (float, float) => result, + fn: (float, float) => result, t1: PointSetTypes.continuousShape, t2: PointSetTypes.continuousShape, ): result => { diff --git a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetDist.res b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetDist.res index 798d90ca..6dacc40f 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetDist.res +++ b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetDist.res @@ -60,10 +60,10 @@ let combinePointwise = ( PointSetTypes.continuousShape, PointSetTypes.continuousShape, ) => option=(_, _) => None, - fn: (float, float) => result, + fn: (float, float) => result, t1: t, t2: t, -): result => +): result => switch (t1, t2) { | (Continuous(m1), Continuous(m2)) => Continuous.combinePointwise( diff --git a/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSetDist.res b/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSetDist.res index d62396f8..74dba954 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSetDist.res +++ b/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSetDist.res @@ -83,11 +83,10 @@ let sampleN = (t: t, n) => { } //TODO: Figure out what to do if distributions are different lengths. ``zip`` is kind of inelegant for this. -let map2 = ( - ~fn: (float, float) => result, - ~t1: t, - ~t2: t, -): result => { +let map2 = (~fn: (float, float) => result, ~t1: t, ~t2: t): result< + t, + Operation.Error.t, +> => { let samples = Belt.Array.zip(get(t1), get(t2))->E.A2.fmap(((a, b)) => fn(a, b)) // This assertion should never be reached. In order for it to be reached, one diff --git a/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDistTypes.res b/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDistTypes.res index 274a908e..333e2e63 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDistTypes.res +++ b/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDistTypes.res @@ -45,6 +45,6 @@ type symbolicDist = [ type analyticalSimplificationResult = [ | #AnalyticalSolution(symbolicDist) - | #Error(Operation.Error.invalidOperationError) + | #Error(Operation.Error.t) | #NoSolution ] diff --git a/packages/squiggle-lang/src/rescript/Utility/Operation.res b/packages/squiggle-lang/src/rescript/Utility/Operation.res index de3f9c59..ac83ceea 100644 --- a/packages/squiggle-lang/src/rescript/Utility/Operation.res +++ b/packages/squiggle-lang/src/rescript/Utility/Operation.res @@ -37,35 +37,37 @@ module Convolution = { } } +type operationError = + | DivisionByZeroError + | ComplexNumberError + @genType module Error = { @genType - type invalidOperationError = - | DivisionByZeroError - | ComplexNumberError + type t = operationError - let invalidOperationErrorToString = (err: invalidOperationError): string => + let toString = (err: t): string => switch err { | DivisionByZeroError => "Cannot divide by zero" | ComplexNumberError => "Operation returned complex result" } } -let power = (a: float, b: float): result => +let power = (a: float, b: float): result => if a >= 0.0 { Ok(a ** b) } else { Error(ComplexNumberError) } -let divide = (a: float, b: float): result => +let divide = (a: float, b: float): result => if b != 0.0 { Ok(a /. b) } else { Error(DivisionByZeroError) } -let logarithm = (a: float, b: float): result => +let logarithm = (a: float, b: float): result => if b == 1. { Error(DivisionByZeroError) } else if b == 0. { @@ -80,7 +82,7 @@ let logarithm = (a: float, b: float): result module Algebraic = { @genType type t = algebraicOperation - let toFn: (t, float, float) => result = (x, a, b) => + let toFn: (t, float, float) => result = (x, a, b) => switch x { | #Add => Ok(a +. b) | #Subtract => Ok(a -. b) @@ -131,7 +133,7 @@ module DistToFloat = { // Note that different logarithms don't really do anything. module Scale = { type t = scaleOperation - let toFn = (x: t, a: float, b: float): result => + let toFn = (x: t, a: float, b: float): result => switch x { | #Multiply => Ok(a *. b) | #Divide => divide(a, b) diff --git a/packages/squiggle-lang/src/rescript/Utility/XYShape.res b/packages/squiggle-lang/src/rescript/Utility/XYShape.res index 4bc19dc1..97974884 100644 --- a/packages/squiggle-lang/src/rescript/Utility/XYShape.res +++ b/packages/squiggle-lang/src/rescript/Utility/XYShape.res @@ -234,11 +234,11 @@ module Zipped = { module PointwiseCombination = { // t1Interpolator and t2Interpolator are functions from XYShape.XtoY, e.g. linearBetweenPointsExtrapolateFlat. let combine: ( - (float, float) => result, + (float, float) => result, interpolator, T.t, T.t, - ) => result = %raw(` + ) => result = %raw(` // This function combines two xyShapes by looping through both of them simultaneously. // It always moves on to the next smallest x, whether that's in the first or second input's xs, // and interpolates the value on the other side, thus accumulating xs and ys.