Change NaN operations to results

- Also deleting old GenericDist_Types
- Also removing some string errors
This commit is contained in:
Sam Nolan 2022-04-22 16:27:17 -04:00
parent b629759542
commit 508f673873
28 changed files with 459 additions and 334 deletions

View File

@ -30,7 +30,7 @@ let toExt: option<'a> => 'a = E.O.toExt(
describe("sparkline", () => { describe("sparkline", () => {
let runTest = ( let runTest = (
name: string, name: string,
dist: GenericDist_Types.genericDist, dist: DistributionTypes.genericDist,
expected: DistributionOperation.outputType, expected: DistributionOperation.outputType,
) => { ) => {
test(name, () => { test(name, () => {

View File

@ -1,14 +1,14 @@
let normalDist5: GenericDist_Types.genericDist = Symbolic(#Normal({mean: 5.0, stdev: 2.0})) let normalDist5: DistributionTypes.genericDist = Symbolic(#Normal({mean: 5.0, stdev: 2.0}))
let normalDist10: GenericDist_Types.genericDist = Symbolic(#Normal({mean: 10.0, stdev: 2.0})) let normalDist10: DistributionTypes.genericDist = Symbolic(#Normal({mean: 10.0, stdev: 2.0}))
let normalDist20: GenericDist_Types.genericDist = Symbolic(#Normal({mean: 20.0, stdev: 2.0})) let normalDist20: DistributionTypes.genericDist = Symbolic(#Normal({mean: 20.0, stdev: 2.0}))
let normalDist: GenericDist_Types.genericDist = normalDist5 let normalDist: DistributionTypes.genericDist = normalDist5
let betaDist: GenericDist_Types.genericDist = Symbolic(#Beta({alpha: 2.0, beta: 5.0})) let betaDist: DistributionTypes.genericDist = Symbolic(#Beta({alpha: 2.0, beta: 5.0}))
let lognormalDist: GenericDist_Types.genericDist = Symbolic(#Lognormal({mu: 0.0, sigma: 1.0})) let lognormalDist: DistributionTypes.genericDist = Symbolic(#Lognormal({mu: 0.0, sigma: 1.0}))
let cauchyDist: GenericDist_Types.genericDist = Symbolic(#Cauchy({local: 1.0, scale: 1.0})) let cauchyDist: DistributionTypes.genericDist = Symbolic(#Cauchy({local: 1.0, scale: 1.0}))
let triangularDist: GenericDist_Types.genericDist = Symbolic( let triangularDist: DistributionTypes.genericDist = Symbolic(
#Triangular({low: 1.0, medium: 2.0, high: 3.0}), #Triangular({low: 1.0, medium: 2.0, high: 3.0}),
) )
let exponentialDist: GenericDist_Types.genericDist = Symbolic(#Exponential({rate: 2.0})) let exponentialDist: DistributionTypes.genericDist = Symbolic(#Exponential({rate: 2.0}))
let uniformDist: GenericDist_Types.genericDist = Symbolic(#Uniform({low: 9.0, high: 10.0})) let uniformDist: DistributionTypes.genericDist = Symbolic(#Uniform({low: 9.0, high: 10.0}))
let floatDist: GenericDist_Types.genericDist = Symbolic(#Float(1e1)) let floatDist: DistributionTypes.genericDist = Symbolic(#Float(1e1))

View File

@ -43,7 +43,7 @@ describe("(Algebraic) addition of distributions", () => {
test("normal(mean=5) + normal(mean=20)", () => { test("normal(mean=5) + normal(mean=20)", () => {
normalDist5 normalDist5
->algebraicAdd(normalDist20) ->algebraicAdd(normalDist20)
->E.R2.fmap(GenericDist_Types.Constructors.UsingDists.mean) ->E.R2.fmap(DistributionTypes.Constructors.UsingDists.mean)
->E.R2.fmap(run) ->E.R2.fmap(run)
->E.R2.fmap(toFloat) ->E.R2.fmap(toFloat)
->E.R.toExn ->E.R.toExn
@ -57,7 +57,7 @@ describe("(Algebraic) addition of distributions", () => {
let received = let received =
uniformDist uniformDist
->algebraicAdd(betaDist) ->algebraicAdd(betaDist)
->E.R2.fmap(GenericDist_Types.Constructors.UsingDists.mean) ->E.R2.fmap(DistributionTypes.Constructors.UsingDists.mean)
->E.R2.fmap(run) ->E.R2.fmap(run)
->E.R2.fmap(toFloat) ->E.R2.fmap(toFloat)
->E.R.toExn ->E.R.toExn
@ -74,7 +74,7 @@ describe("(Algebraic) addition of distributions", () => {
let received = let received =
betaDist betaDist
->algebraicAdd(uniformDist) ->algebraicAdd(uniformDist)
->E.R2.fmap(GenericDist_Types.Constructors.UsingDists.mean) ->E.R2.fmap(DistributionTypes.Constructors.UsingDists.mean)
->E.R2.fmap(run) ->E.R2.fmap(run)
->E.R2.fmap(toFloat) ->E.R2.fmap(toFloat)
->E.R.toExn ->E.R.toExn
@ -95,7 +95,7 @@ describe("(Algebraic) addition of distributions", () => {
let received = let received =
normalDist10 // this should be normal(10, sqrt(8)) normalDist10 // this should be normal(10, sqrt(8))
->Ok ->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(run)
->E.R2.fmap(toFloat) ->E.R2.fmap(toFloat)
->E.R.toOption ->E.R.toOption
@ -103,7 +103,7 @@ describe("(Algebraic) addition of distributions", () => {
let calculated = let calculated =
normalDist5 normalDist5
->algebraicAdd(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(run)
->E.R2.fmap(toFloat) ->E.R2.fmap(toFloat)
->E.R.toOption ->E.R.toOption
@ -126,7 +126,7 @@ describe("(Algebraic) addition of distributions", () => {
let received = let received =
normalDist20 normalDist20
->Ok ->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(run)
->E.R2.fmap(toFloat) ->E.R2.fmap(toFloat)
->E.R.toOption ->E.R.toOption
@ -134,7 +134,7 @@ describe("(Algebraic) addition of distributions", () => {
let calculated = let calculated =
normalDist10 normalDist10
->algebraicAdd(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(run)
->E.R2.fmap(toFloat) ->E.R2.fmap(toFloat)
->E.R.toOption ->E.R.toOption
@ -155,7 +155,7 @@ describe("(Algebraic) addition of distributions", () => {
let received = let received =
uniformDist uniformDist
->algebraicAdd(betaDist) ->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(run)
->E.R2.fmap(toFloat) ->E.R2.fmap(toFloat)
->E.R.toExn ->E.R.toExn
@ -170,7 +170,7 @@ describe("(Algebraic) addition of distributions", () => {
let received = let received =
betaDist betaDist
->algebraicAdd(uniformDist) ->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(run)
->E.R2.fmap(toFloat) ->E.R2.fmap(toFloat)
->E.R.toExn ->E.R.toExn
@ -187,7 +187,7 @@ describe("(Algebraic) addition of distributions", () => {
let received = let received =
normalDist10 normalDist10
->Ok ->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(run)
->E.R2.fmap(toFloat) ->E.R2.fmap(toFloat)
->E.R.toOption ->E.R.toOption
@ -195,7 +195,7 @@ describe("(Algebraic) addition of distributions", () => {
let calculated = let calculated =
normalDist5 normalDist5
->algebraicAdd(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(run)
->E.R2.fmap(toFloat) ->E.R2.fmap(toFloat)
->E.R.toOption ->E.R.toOption
@ -217,7 +217,7 @@ describe("(Algebraic) addition of distributions", () => {
let received = let received =
normalDist20 normalDist20
->Ok ->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(run)
->E.R2.fmap(toFloat) ->E.R2.fmap(toFloat)
->E.R.toOption ->E.R.toOption
@ -225,7 +225,7 @@ describe("(Algebraic) addition of distributions", () => {
let calculated = let calculated =
normalDist10 normalDist10
->algebraicAdd(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(run)
->E.R2.fmap(toFloat) ->E.R2.fmap(toFloat)
->E.R.toOption ->E.R.toOption
@ -246,7 +246,7 @@ describe("(Algebraic) addition of distributions", () => {
let received = let received =
uniformDist uniformDist
->algebraicAdd(betaDist) ->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(run)
->E.R2.fmap(toFloat) ->E.R2.fmap(toFloat)
->E.R.toExn ->E.R.toExn
@ -261,7 +261,7 @@ describe("(Algebraic) addition of distributions", () => {
let received = let received =
betaDist betaDist
->algebraicAdd(uniformDist) ->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(run)
->E.R2.fmap(toFloat) ->E.R2.fmap(toFloat)
->E.R.toExn ->E.R.toExn
@ -279,7 +279,7 @@ describe("(Algebraic) addition of distributions", () => {
let received = let received =
normalDist10 normalDist10
->Ok ->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(run)
->E.R2.fmap(toFloat) ->E.R2.fmap(toFloat)
->E.R.toOption ->E.R.toOption
@ -287,7 +287,7 @@ describe("(Algebraic) addition of distributions", () => {
let calculated = let calculated =
normalDist5 normalDist5
->algebraicAdd(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(run)
->E.R2.fmap(toFloat) ->E.R2.fmap(toFloat)
->E.R.toOption ->E.R.toOption
@ -309,7 +309,7 @@ describe("(Algebraic) addition of distributions", () => {
let received = let received =
normalDist20 normalDist20
->Ok ->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(run)
->E.R2.fmap(toFloat) ->E.R2.fmap(toFloat)
->E.R.toOption ->E.R.toOption
@ -317,7 +317,7 @@ describe("(Algebraic) addition of distributions", () => {
let calculated = let calculated =
normalDist10 normalDist10
->algebraicAdd(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(run)
->E.R2.fmap(toFloat) ->E.R2.fmap(toFloat)
->E.R.toOption ->E.R.toOption
@ -338,7 +338,7 @@ describe("(Algebraic) addition of distributions", () => {
let received = let received =
uniformDist uniformDist
->algebraicAdd(betaDist) ->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(run)
->E.R2.fmap(toFloat) ->E.R2.fmap(toFloat)
->E.R.toExn ->E.R.toExn
@ -353,7 +353,7 @@ describe("(Algebraic) addition of distributions", () => {
let received = let received =
betaDist betaDist
->algebraicAdd(uniformDist) ->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(run)
->E.R2.fmap(toFloat) ->E.R2.fmap(toFloat)
->E.R.toExn ->E.R.toExn

View File

@ -15,7 +15,7 @@ open TestHelpers
module Internals = { module Internals = {
let epsilon = 5e1 let epsilon = 5e1
let mean = GenericDist_Types.Constructors.UsingDists.mean let mean = DistributionTypes.Constructors.UsingDists.mean
let expectImpossiblePath: string => assertion = algebraicOp => let expectImpossiblePath: string => assertion = algebraicOp =>
`${algebraicOp} has`->expect->toEqual("failed") `${algebraicOp} has`->expect->toEqual("failed")

View File

@ -22,7 +22,7 @@ describe("eval on distribution functions", () => {
testEval("mean(-normal(5,2))", "Ok(-5)") testEval("mean(-normal(5,2))", "Ok(-5)")
}) })
describe("to", () => { 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,5)", "Ok(Lognormal(1.1512925464970227,0.27853260523016377))")
testEval("to(-2,2)", "Ok(Normal(0,1.2159136638235384))") testEval("to(-2,2)", "Ok(Normal(0,1.2159136638235384))")
}) })
@ -88,8 +88,14 @@ describe("eval on distribution functions", () => {
describe("log", () => { describe("log", () => {
testEval("log(2, uniform(5,8))", "Ok(Sample Set Distribution)") testEval("log(2, uniform(5,8))", "Ok(Sample Set Distribution)")
testEval("log(normal(5,2), 3)", "Ok(Sample Set Distribution)") testEval(
testEval("log(normal(5,2), normal(10,1))", "Ok(Sample Set Distribution)") "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("log(uniform(5,8))", "Ok(Sample Set Distribution)")
testEval("log10(uniform(5,8))", "Ok(Sample Set Distribution)") testEval("log10(uniform(5,8))", "Ok(Sample Set Distribution)")
}) })

View File

@ -12,7 +12,7 @@ describe("Symbolic mean", () => {
expect(squiggleResult.value).toBeCloseTo((x + y + z) / 3); expect(squiggleResult.value).toBeCloseTo((x + y + z) / 3);
} catch (err) { } catch (err) {
expect((err as Error).message).toEqual( 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."
); );
} }
} }

View File

@ -1,4 +1,4 @@
type functionCallInfo = GenericDist_Types.Operation.genericFunctionCallInfo type functionCallInfo = DistributionTypes.DistributionOperation.genericFunctionCallInfo
type genericDist = DistributionTypes.genericDist type genericDist = DistributionTypes.genericDist
type error = DistributionTypes.error type error = DistributionTypes.error
@ -120,7 +120,10 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => {
(), (),
)->OutputLocal.toDistR )->OutputLocal.toDistR
let fromDistFn = (subFnName: GenericDist_Types.Operation.fromDist, dist: genericDist) => { let fromDistFn = (
subFnName: DistributionTypes.DistributionOperation.fromDist,
dist: genericDist,
) => {
let response = switch subFnName { let response = switch subFnName {
| ToFloat(distToFloatOperation) => | ToFloat(distToFloatOperation) =>
GenericDist.toFloatOperation(dist, ~toPointSetFn, ~distToFloatOperation) GenericDist.toFloatOperation(dist, ~toPointSetFn, ~distToFloatOperation)
@ -162,9 +165,9 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => {
->GenericDist.pointwiseCombination(~toPointSetFn, ~arithmeticOperation, ~t2) ->GenericDist.pointwiseCombination(~toPointSetFn, ~arithmeticOperation, ~t2)
->E.R2.fmap(r => Dist(r)) ->E.R2.fmap(r => Dist(r))
->OutputLocal.fromResult ->OutputLocal.fromResult
| ToDistCombination(Pointwise, arithmeticOperation, #Float(float)) => | ToDistCombination(Pointwise, arithmeticOperation, #Float(f)) =>
dist dist
->GenericDist.pointwiseCombinationFloat(~toPointSetFn, ~arithmeticOperation, ~float) ->GenericDist.pointwiseCombinationFloat(~toPointSetFn, ~arithmeticOperation, ~f)
->E.R2.fmap(r => Dist(r)) ->E.R2.fmap(r => Dist(r))
->OutputLocal.fromResult ->OutputLocal.fromResult
} }
@ -192,7 +195,7 @@ module Output = {
let fmap = ( let fmap = (
~env, ~env,
input: outputType, input: outputType,
functionCallInfo: GenericDist_Types.Operation.singleParamaterFunction, functionCallInfo: DistributionTypes.DistributionOperation.singleParamaterFunction,
): outputType => { ): outputType => {
let newFnCall: result<functionCallInfo, error> = switch (functionCallInfo, input) { let newFnCall: result<functionCallInfo, error> = switch (functionCallInfo, input) {
| (FromDist(fromDist), Dist(o)) => Ok(FromDist(fromDist, o)) | (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 // 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 Constructors = {
module C = GenericDist_Types.Constructors.UsingDists module C = DistributionTypes.Constructors.UsingDists
open OutputLocal open OutputLocal
let mean = (~env, dist) => C.mean(dist)->run(~env)->toFloatR let mean = (~env, dist) => C.mean(dist)->run(~env)->toFloatR
let sample = (~env, dist) => C.sample(dist)->run(~env)->toFloatR let sample = (~env, dist) => C.sample(dist)->run(~env)->toFloatR

View File

@ -4,7 +4,7 @@ type env = {
xyPointLength: int, xyPointLength: int,
} }
open GenericDist_Types open DistributionTypes
@genType @genType
type outputType = type outputType =
@ -15,15 +15,15 @@ type outputType =
| GenDistError(error) | GenDistError(error)
@genType @genType
let run: (~env: env, GenericDist_Types.Operation.genericFunctionCallInfo) => outputType let run: (~env: env, DistributionTypes.DistributionOperation.genericFunctionCallInfo) => outputType
let runFromDist: ( let runFromDist: (
~env: env, ~env: env,
~functionCallInfo: GenericDist_Types.Operation.fromDist, ~functionCallInfo: DistributionTypes.DistributionOperation.fromDist,
genericDist, genericDist,
) => outputType ) => outputType
let runFromFloat: ( let runFromFloat: (
~env: env, ~env: env,
~functionCallInfo: GenericDist_Types.Operation.fromDist, ~functionCallInfo: DistributionTypes.DistributionOperation.fromDist,
float, float,
) => outputType ) => outputType
@ -38,7 +38,7 @@ module Output: {
let toBool: t => option<bool> let toBool: t => option<bool>
let toBoolR: t => result<bool, error> let toBoolR: t => result<bool, error>
let toError: t => option<error> let toError: t => option<error>
let fmap: (~env: env, t, GenericDist_Types.Operation.singleParamaterFunction) => t let fmap: (~env: env, t, DistributionTypes.DistributionOperation.singleParamaterFunction) => t
} }
module Constructors: { module Constructors: {

View File

@ -9,9 +9,43 @@ type error =
| NotYetImplemented | NotYetImplemented
| Unreachable | Unreachable
| DistributionVerticalShiftIsInvalid | DistributionVerticalShiftIsInvalid
| TooFewSamples
| ArgumentError(string) | 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) | 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 = { module Operation = {
type direction = type direction =
| Algebraic | Algebraic
@ -43,6 +77,9 @@ module Operation = {
| #Mean | #Mean
| #Sample | #Sample
] ]
@genType
type pointsetXSelection = [#Linear | #ByWeight]
} }
module DistributionOperation = { module DistributionOperation = {
@ -55,6 +92,12 @@ module DistributionOperation = {
type toFloatArray = Sample(int) type toFloatArray = Sample(int)
type toBool = IsNormalized
type toString =
| ToString
| ToSparkline(int)
type fromDist = type fromDist =
| ToFloat(Operation.toFloat) | ToFloat(Operation.toFloat)
| ToDist(toDist) | ToDist(toDist)
@ -63,7 +106,8 @@ module DistributionOperation = {
Operation.arithmeticOperation, Operation.arithmeticOperation,
[#Dist(genericDist) | #Float(float)], [#Dist(genericDist) | #Float(float)],
) )
| ToString | ToString(toString)
| ToBool(toBool)
type singleParamaterFunction = type singleParamaterFunction =
| FromDist(fromDist) | FromDist(fromDist)
@ -86,7 +130,9 @@ module DistributionOperation = {
| ToDist(ToSampleSet(r)) => `toSampleSet(${E.I.toString(r)})` | ToDist(ToSampleSet(r)) => `toSampleSet(${E.I.toString(r)})`
| ToDist(Truncate(_, _)) => `truncate` | ToDist(Truncate(_, _)) => `truncate`
| ToDist(Inspect) => `inspect` | ToDist(Inspect) => `inspect`
| ToString => `toString` | ToString(ToString) => `toString`
| ToString(ToSparkline(n)) => `toSparkline(${E.I.toString(n)})`
| ToBool(IsNormalized) => `isNormalized`
| ToDistCombination(Algebraic, _, _) => `algebraic` | ToDistCombination(Algebraic, _, _) => `algebraic`
| ToDistCombination(Pointwise, _, _) => `pointwise` | ToDistCombination(Pointwise, _, _) => `pointwise`
} }
@ -97,3 +143,71 @@ module DistributionOperation = {
| Mixture(_) => `mixture` | 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,
)
}
}

View File

@ -14,7 +14,7 @@ let sampleN = (t: t, n) =>
} }
let toSampleSetDist = (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)) let fromFloat = (f: float): t => Symbolic(SymbolicDist.Float.make(f))
@ -68,7 +68,7 @@ let toPointSet = (
t, t,
~xyPointLength, ~xyPointLength,
~sampleCount, ~sampleCount,
~xSelection: GenericDist_Types.Operation.pointsetXSelection=#ByWeight, ~xSelection: DistributionTypes.Operation.pointsetXSelection=#ByWeight,
(), (),
): result<PointSetTypes.pointSetDist, error> => { ): result<PointSetTypes.pointSetDist, error> => {
switch (t: t) { switch (t: t) {
@ -83,7 +83,7 @@ let toPointSet = (
pointSetDistLength: xyPointLength, pointSetDistLength: xyPointLength,
kernelWidth: None, 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, ()): result<st
t t
->toPointSet(~xSelection=#Linear, ~xyPointLength=bucketCount * 3, ~sampleCount, ()) ->toPointSet(~xSelection=#Linear, ~xyPointLength=bucketCount * 3, ~sampleCount, ())
->E.R.bind(r => ->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 = { module Truncate = {
@ -148,10 +148,10 @@ let truncate = Truncate.run
*/ */
module AlgebraicCombination = { module AlgebraicCombination = {
let tryAnalyticalSimplification = ( let tryAnalyticalSimplification = (
arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation, arithmeticOperation: DistributionTypes.Operation.arithmeticOperation,
t1: t, t1: t,
t2: t, t2: t,
): option<result<SymbolicDistTypes.symbolicDist, string>> => ): option<result<SymbolicDistTypes.symbolicDist, Operation.invalidOperationError>> =>
switch (arithmeticOperation, t1, t2) { switch (arithmeticOperation, t1, t2) {
| (arithmeticOperation, Symbolic(d1), Symbolic(d2)) => | (arithmeticOperation, Symbolic(d1), Symbolic(d2)) =>
switch SymbolicDist.T.tryAnalyticalSimplification(d1, d2, arithmeticOperation) { switch SymbolicDist.T.tryAnalyticalSimplification(d1, d2, arithmeticOperation) {
@ -174,14 +174,14 @@ module AlgebraicCombination = {
let runMonteCarlo = ( let runMonteCarlo = (
toSampleSet: toSampleSetFn, toSampleSet: toSampleSetFn,
arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation, arithmeticOperation: DistributionTypes.Operation.arithmeticOperation,
t1: t, t1: t,
t2: t, t2: t,
) => { ): result<t, error> => {
let fn = Operation.Algebraic.toFn(arithmeticOperation) let fn = Operation.Algebraic.toFn(arithmeticOperation)
E.R.merge(toSampleSet(t1), toSampleSet(t2)) E.R.merge(toSampleSet(t1), toSampleSet(t2))
->E.R.bind(((t1, 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)) ->E.R2.fmap(r => DistributionTypes.SampleSet(r))
} }
@ -224,7 +224,7 @@ module AlgebraicCombination = {
): result<t, error> => { ): result<t, error> => {
switch tryAnalyticalSimplification(arithmeticOperation, t1, t2) { switch tryAnalyticalSimplification(arithmeticOperation, t1, t2) {
| Some(Ok(symbolicDist)) => Ok(Symbolic(symbolicDist)) | Some(Ok(symbolicDist)) => Ok(Symbolic(symbolicDist))
| Some(Error(e)) => Error(Other(e)) | Some(Error(e)) => Error(OperationError(e))
| None => | None =>
switch chooseConvolutionOrMonteCarlo(arithmeticOperation, t1, t2) { switch chooseConvolutionOrMonteCarlo(arithmeticOperation, t1, t2) {
| MonteCarlo => runMonteCarlo(toSampleSetFn, arithmeticOperation, t1, t2) | MonteCarlo => runMonteCarlo(toSampleSetFn, arithmeticOperation, t1, t2)
@ -247,7 +247,7 @@ let pointwiseCombination = (
E.R.merge(toPointSetFn(t1), toPointSetFn(t2)) E.R.merge(toPointSetFn(t1), toPointSetFn(t2))
->E.R2.fmap(((t1, t2)) => ->E.R2.fmap(((t1, t2)) =>
PointSetDist.combinePointwise( PointSetDist.combinePointwise(
GenericDist_Types.Operation.arithmeticToFn(arithmeticOperation), DistributionTypes.Operation.arithmeticToFn(arithmeticOperation),
t1, t1,
t2, t2,
) )
@ -258,23 +258,23 @@ let pointwiseCombination = (
let pointwiseCombinationFloat = ( let pointwiseCombinationFloat = (
t: t, t: t,
~toPointSetFn: toPointSetFn, ~toPointSetFn: toPointSetFn,
~arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation, ~arithmeticOperation: DistributionTypes.Operation.arithmeticOperation,
~float: float, ~f: float,
): result<t, error> => { ): result<t, error> => {
let m = switch arithmeticOperation { let m = switch arithmeticOperation {
| #Add | #Subtract => Error(DistributionTypes.DistributionVerticalShiftIsInvalid) | #Add | #Subtract => Error(DistributionTypes.DistributionVerticalShiftIsInvalid)
| (#Multiply | #Divide | #Power | #Logarithm) as arithmeticOperation => | (#Multiply | #Divide | #Power | #Logarithm) as arithmeticOperation =>
toPointSetFn(t)->E.R2.fmap(t => { toPointSetFn(t)->E.R.bind(t => {
//TODO: Move to PointSet codebase //TODO: Move to PointSet codebase
let fn = (secondary, main) => Operation.Scale.toFn(arithmeticOperation, main, secondary) let fn = (secondary, main) => Operation.Scale.toFn(arithmeticOperation, main, secondary)
let integralSumCacheFn = Operation.Scale.toIntegralSumCacheFn(arithmeticOperation) let integralSumCacheFn = Operation.Scale.toIntegralSumCacheFn(arithmeticOperation)
let integralCacheFn = Operation.Scale.toIntegralCacheFn(arithmeticOperation) let integralCacheFn = Operation.Scale.toIntegralCacheFn(arithmeticOperation)
PointSetDist.T.mapY( PointSetDist.T.mapYResult(
~integralSumCacheFn=integralSumCacheFn(float), ~integralSumCacheFn=integralSumCacheFn(f),
~integralCacheFn=integralCacheFn(float), ~integralCacheFn=integralCacheFn(f),
~fn=fn(float), ~fn=fn(f),
t, t,
) )->E.R2.errMap(x => DistributionTypes.OperationError(x))
}) })
} }
m->E.R2.fmap(r => DistributionTypes.PointSet(r)) m->E.R2.fmap(r => DistributionTypes.PointSet(r))

View File

@ -1,5 +1,5 @@
type t = GenericDist_Types.genericDist type t = DistributionTypes.genericDist
type error = GenericDist_Types.error type error = DistributionTypes.error
type toPointSetFn = t => result<PointSetTypes.pointSetDist, error> type toPointSetFn = t => result<PointSetTypes.pointSetDist, error>
type toSampleSetFn = t => result<SampleSetDist.t, error> type toSampleSetFn = t => result<SampleSetDist.t, error>
type scaleMultiplyFn = (t, float) => result<t, error> type scaleMultiplyFn = (t, float) => result<t, error>
@ -28,7 +28,7 @@ let toPointSet: (
t, t,
~xyPointLength: int, ~xyPointLength: int,
~sampleCount: int, ~sampleCount: int,
~xSelection: GenericDist_Types.Operation.pointsetXSelection=?, ~xSelection: DistributionTypes.Operation.pointsetXSelection=?,
unit, unit,
) => result<PointSetTypes.pointSetDist, error> ) => result<PointSetTypes.pointSetDist, error>
let toSparkline: (t, ~sampleCount: int, ~bucketCount: int=?, unit) => result<string, error> let toSparkline: (t, ~sampleCount: int, ~bucketCount: int=?, unit) => result<string, error>
@ -45,22 +45,22 @@ let algebraicCombination: (
t, t,
~toPointSetFn: toPointSetFn, ~toPointSetFn: toPointSetFn,
~toSampleSetFn: toSampleSetFn, ~toSampleSetFn: toSampleSetFn,
~arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation, ~arithmeticOperation: DistributionTypes.Operation.arithmeticOperation,
~t2: t, ~t2: t,
) => result<t, error> ) => result<t, error>
let pointwiseCombination: ( let pointwiseCombination: (
t, t,
~toPointSetFn: toPointSetFn, ~toPointSetFn: toPointSetFn,
~arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation, ~arithmeticOperation: DistributionTypes.Operation.arithmeticOperation,
~t2: t, ~t2: t,
) => result<t, error> ) => result<t, error>
let pointwiseCombinationFloat: ( let pointwiseCombinationFloat: (
t, t,
~toPointSetFn: toPointSetFn, ~toPointSetFn: toPointSetFn,
~arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation, ~arithmeticOperation: DistributionTypes.Operation.arithmeticOperation,
~float: float, ~f: float,
) => result<t, error> ) => result<t, error>
let mixture: ( let mixture: (

View File

@ -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<float>, option<float>)
| 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,
)
}
}

View File

@ -146,7 +146,27 @@ let reduce = (
continuousShapes, continuousShapes,
) => continuousShapes |> E.A.fold_left(combinePointwise(~integralSumCachesFn, fn), empty) ) => 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<float, 'e>,
t: t,
): result<t, 'e> =>
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( make(
~interpolation=t.interpolation, ~interpolation=t.interpolation,
~integralSumCache=t.integralSumCache |> E.O.bind(_, integralSumCacheFn), ~integralSumCache=t.integralSumCache |> E.O.bind(_, integralSumCacheFn),
@ -170,6 +190,7 @@ module T = Dist({
let minX = shapeFn(XYShape.T.minX) let minX = shapeFn(XYShape.T.minX)
let maxX = shapeFn(XYShape.T.maxX) let maxX = shapeFn(XYShape.T.maxX)
let mapY = mapY let mapY = mapY
let mapYResult = mapYResult
let updateIntegralCache = updateIntegralCache let updateIntegralCache = updateIntegralCache
let toDiscreteProbabilityMassFraction = _ => 0.0 let toDiscreteProbabilityMassFraction = _ => 0.0
let toPointSetDist = (t: t): PointSetTypes.pointSetDist => Continuous(t) let toPointSetDist = (t: t): PointSetTypes.pointSetDist => Continuous(t)

View File

@ -103,7 +103,26 @@ let combineAlgebraically = (op: Operation.convolutionOperation, t1: t, t2: t): t
make(~integralSumCache=combinedIntegralSum, combinedShape) make(~integralSumCache=combinedIntegralSum, combinedShape)
} }
let mapY = (~integralSumCacheFn=_ => None, ~integralCacheFn=_ => None, ~fn, t: t) => let mapYResult = (
~integralSumCacheFn=_ => None,
~integralCacheFn=_ => None,
~fn: float => result<float, 'e>,
t: t,
): result<t, 'e> =>
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( make(
~integralSumCache=t.integralSumCache |> E.O.bind(_, integralSumCacheFn), ~integralSumCache=t.integralSumCache |> E.O.bind(_, integralSumCacheFn),
~integralCache=t.integralCache |> E.O.bind(_, integralCacheFn), ~integralCache=t.integralCache |> E.O.bind(_, integralCacheFn),
@ -143,6 +162,7 @@ module T = Dist({
let maxX = shapeFn(XYShape.T.maxX) let maxX = shapeFn(XYShape.T.maxX)
let toDiscreteProbabilityMassFraction = _ => 1.0 let toDiscreteProbabilityMassFraction = _ => 1.0
let mapY = mapY let mapY = mapY
let mapYResult = mapYResult
let updateIntegralCache = updateIntegralCache let updateIntegralCache = updateIntegralCache
let toPointSetDist = (t: t): PointSetTypes.pointSetDist => Discrete(t) let toPointSetDist = (t: t): PointSetTypes.pointSetDist => Discrete(t)
let toContinuous = _ => None let toContinuous = _ => None

View File

@ -9,6 +9,12 @@ module type dist = {
~fn: float => float, ~fn: float => float,
t, t,
) => t ) => t
let mapYResult: (
~integralSumCacheFn: float => option<float>=?,
~integralCacheFn: PointSetTypes.continuousShape => option<PointSetTypes.continuousShape>=?,
~fn: float => result<float, 'e>,
t,
) => result<t, 'e>
let xToY: (float, t) => PointSetTypes.mixedPoint let xToY: (float, t) => PointSetTypes.mixedPoint
let toPointSetDist: t => PointSetTypes.pointSetDist let toPointSetDist: t => PointSetTypes.pointSetDist
let toContinuous: t => option<PointSetTypes.continuousShape> let toContinuous: t => option<PointSetTypes.continuousShape>
@ -37,6 +43,7 @@ module Dist = (T: dist) => {
let integral = T.integral let integral = T.integral
let xTotalRange = (t: t) => maxX(t) -. minX(t) let xTotalRange = (t: t) => maxX(t) -. minX(t)
let mapY = T.mapY let mapY = T.mapY
let mapYResult = T.mapYResult
let xToY = T.xToY let xToY = T.xToY
let downsample = T.downsample let downsample = T.downsample
let toPointSetDist = T.toPointSetDist let toPointSetDist = T.toPointSetDist

View File

@ -164,7 +164,12 @@ module T = Dist({
// This pipes all ys (continuous and discrete) through fn. // 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 mapY is a linear operation, we might be able to update the integralSumCaches as well;
// if not, they'll be set to None. // 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 = let yMappedDiscrete: PointSetTypes.discreteShape =
t.discrete t.discrete
|> Discrete.T.mapY(~fn) |> Discrete.T.mapY(~fn)
@ -187,6 +192,41 @@ module T = Dist({
} }
} }
let mapYResult = (
~integralSumCacheFn=_ => None,
~integralCacheFn=_ => None,
~fn: float => result<float, 'e>,
t: t,
): result<t, 'e> => {
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 mean = ({discrete, continuous}: t): float => {
let discreteMean = Discrete.T.mean(discrete) let discreteMean = Discrete.T.mean(discrete)
let continuousMean = Continuous.T.mean(continuous) let continuousMean = Continuous.T.mean(continuous)

View File

@ -16,6 +16,13 @@ let fmap = ((fn1, fn2, fn3), t: t): t =>
| Continuous(m) => Continuous(fn3(m)) | Continuous(m) => Continuous(fn3(m))
} }
let fmapResult = ((fn1, fn2, fn3), t: t): result<t, 'e> =>
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(( let toMixed = mapToAll((
m => m, m => m,
d => d =>
@ -130,13 +137,26 @@ module T = Dist({
let integralYtoX = f => let integralYtoX = f =>
mapToAll((Mixed.T.Integral.yToX(f), Discrete.T.Integral.yToX(f), Continuous.T.Integral.yToX(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 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(( fmap((
Mixed.T.mapY(~integralSumCacheFn, ~integralCacheFn, ~fn), Mixed.T.mapY(~integralSumCacheFn, ~integralCacheFn, ~fn),
Discrete.T.mapY(~integralSumCacheFn, ~integralCacheFn, ~fn), Discrete.T.mapY(~integralSumCacheFn, ~integralCacheFn, ~fn),
Continuous.T.mapY(~integralSumCacheFn, ~integralCacheFn, ~fn), Continuous.T.mapY(~integralSumCacheFn, ~integralCacheFn, ~fn),
)) ))
let mapYResult = (
~integralSumCacheFn=_ => None,
~integralCacheFn=_ => None,
~fn: float => result<float, 'e>,
): (t => result<t, 'e>) =>
fmapResult((
Mixed.T.mapYResult(~integralSumCacheFn, ~integralCacheFn, ~fn),
Discrete.T.mapYResult(~integralSumCacheFn, ~integralCacheFn, ~fn),
Continuous.T.mapYResult(~integralSumCacheFn, ~integralCacheFn, ~fn),
))
let mean = (t: t): float => let mean = (t: t): float =>
switch t { switch t {
| Mixed(m) => Mixed.T.mean(m) | Mixed(m) => Mixed.T.mean(m)
@ -195,8 +215,16 @@ let operate = (distToFloatOp: Operation.distToFloatOperation, s): float =>
| #Mean => T.mean(s) | #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<string, sparklineError> =>
T.toContinuous(t) T.toContinuous(t)
->E.O2.fmap(Continuous.downsampleEquallyOverX(bucketCount)) ->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()) ->E.R2.fmap(r => Continuous.getShape(r).ys->Sparklines.create())

View File

@ -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 is used as a smart constructor. The only way to create a SampleSetDist.t is to call
this constructor. this constructor.
@ -8,7 +16,7 @@ module T: {
//When we get a good functional library in TS, we could refactor that out. //When we get a good functional library in TS, we could refactor that out.
@genType @genType
type t = array<float> type t = array<float>
let make: array<float> => result<t, string> let make: array<float> => result<t, sampleSetError>
let get: t => array<float> let get: t => array<float>
} = { } = {
type t = array<float> type t = array<float>
@ -16,7 +24,7 @@ module T: {
if E.A.length(a) > 5 { if E.A.length(a) > 5 {
Ok(a) Ok(a)
} else { } else {
Error("too small") Error(TooFewSamples)
} }
let get = (a: t) => a let get = (a: t) => a
} }
@ -25,19 +33,27 @@ include T
let length = (t: t) => get(t)->E.A.length 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 TODO: Refactor to get a more precise estimate. Also, this code is just fairly messy, could use
some refactoring. some refactoring.
*/ */
let toPointSetDist = (~samples: t, ~samplingInputs: SamplingInputs.samplingInputs): result< let toPointSetDist = (~samples: t, ~samplingInputs: SamplingInputs.samplingInputs): result<
PointSetTypes.pointSetDist, PointSetTypes.pointSetDist,
string, pointsetConversionError,
> => > =>
SampleSetDist_ToPointSet.toPointSetDist( SampleSetDist_ToPointSet.toPointSetDist(
~samples=get(samples), ~samples=get(samples),
~samplingInputs, ~samplingInputs,
(), (),
).pointSetDist->E.O2.toResult("Failed to convert to PointSetDist") ).pointSetDist->E.O2.toResult(TooFewSamplesForConversionToPointSet)
//Randomly get one sample from the distribution //Randomly get one sample from the distribution
let sample = (t: t): float => { 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. //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<float, Operation.invalidOperationError>,
~t1: t,
~t2: t,
): result<t, Operation.invalidOperationError> => {
let samples = Belt.Array.zip(get(t1), get(t2))->E.A2.fmap(((a, b)) => fn(a, b)) 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<float>}
// 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))
)
} }

View File

@ -83,7 +83,7 @@ let toPointSetDist = (
~samples: Internals.T.t, ~samples: Internals.T.t,
~samplingInputs: SamplingInputs.samplingInputs, ~samplingInputs: SamplingInputs.samplingInputs,
(), (),
) => { ): Internals.Types.outputs => {
Array.fast_sort(compare, samples) Array.fast_sort(compare, samples)
let (continuousPart, discretePart) = E.A.Sorted.Floats.split(samples) let (continuousPart, discretePart) = E.A.Sorted.Floats.split(samples)
let length = samples |> E.A.length |> float_of_int let length = samples |> E.A.length |> float_of_int

View File

@ -379,7 +379,7 @@ module T = {
): analyticalSimplificationResult => ): analyticalSimplificationResult =>
switch (d1, d2) { switch (d1, d2) {
| (#Float(v1), #Float(v2)) => | (#Float(v1), #Float(v2)) =>
switch Operation.Algebraic.applyFn(op, v1, v2) { switch Operation.Algebraic.toFn(op, v1, v2) {
| Ok(r) => #AnalyticalSolution(#Float(r)) | Ok(r) => #AnalyticalSolution(#Float(r))
| Error(n) => #Error(n) | Error(n) => #Error(n)
} }

View File

@ -45,6 +45,6 @@ type symbolicDist = [
type analyticalSimplificationResult = [ type analyticalSimplificationResult = [
| #AnalyticalSolution(symbolicDist) | #AnalyticalSolution(symbolicDist)
| #Error(string) | #Error(Operation.invalidOperationError)
| #NoSolution | #NoSolution
] ]

View File

@ -9,6 +9,7 @@ type errorValue =
| RERecordPropertyNotFound(string, string) | RERecordPropertyNotFound(string, string)
| RESymbolNotFound(string) | RESymbolNotFound(string)
| RESyntaxError(string) | RESyntaxError(string)
| REDistributionError(DistributionTypes.error)
| RETodo(string) // To do | RETodo(string) // To do
type t = errorValue type t = errorValue
@ -20,6 +21,7 @@ let errorToString = err =>
| REAssignmentExpected => "Assignment expected" | REAssignmentExpected => "Assignment expected"
| REExpressionExpected => "Expression expected" | REExpressionExpected => "Expression expected"
| REFunctionExpected(msg) => `Function expected: ${msg}` | REFunctionExpected(msg) => `Function expected: ${msg}`
| REDistributionError(err) => `Math Error: ${DistributionTypes.Error.toString(err)}`
| REJavaScriptExn(omsg, oname) => { | REJavaScriptExn(omsg, oname) => {
let answer = "JS Exception:" let answer = "JS Exception:"
let answer = switch oname { let answer = switch oname {

View File

@ -10,7 +10,7 @@ type rec expressionValue =
| EvArray(array<expressionValue>) | EvArray(array<expressionValue>)
| EvBool(bool) | EvBool(bool)
| EvCall(string) // External function call | EvCall(string) // External function call
| EvDistribution(GenericDist_Types.genericDist) | EvDistribution(DistributionTypes.genericDist)
| EvNumber(float) | EvNumber(float)
| EvRecord(Js.Dict.t<expressionValue>) | EvRecord(Js.Dict.t<expressionValue>)
| EvString(string) | EvString(string)

View File

@ -29,8 +29,8 @@ module Helpers = {
} }
let catchAndConvertTwoArgsToDists = (args: array<expressionValue>): option<( let catchAndConvertTwoArgsToDists = (args: array<expressionValue>): option<(
GenericDist_Types.genericDist, DistributionTypes.genericDist,
GenericDist_Types.genericDist, DistributionTypes.genericDist,
)> => { )> => {
switch args { switch args {
| [EvDistribution(a), EvDistribution(b)] => Some((a, b)) | [EvDistribution(a), EvDistribution(b)] => Some((a, b))
@ -41,33 +41,41 @@ module Helpers = {
} }
let toFloatFn = ( let toFloatFn = (
fnCall: GenericDist_Types.Operation.toFloat, fnCall: DistributionTypes.Operation.toFloat,
dist: GenericDist_Types.genericDist, dist: DistributionTypes.genericDist,
) => { ) => {
FromDist(GenericDist_Types.Operation.ToFloat(fnCall), dist)->runGenericOperation->Some FromDist(DistributionTypes.DistributionOperation.ToFloat(fnCall), dist)
->runGenericOperation
->Some
} }
let toStringFn = ( let toStringFn = (
fnCall: GenericDist_Types.Operation.toString, fnCall: DistributionTypes.DistributionOperation.toString,
dist: GenericDist_Types.genericDist, dist: DistributionTypes.genericDist,
) => { ) => {
FromDist(GenericDist_Types.Operation.ToString(fnCall), dist)->runGenericOperation->Some FromDist(DistributionTypes.DistributionOperation.ToString(fnCall), dist)
->runGenericOperation
->Some
} }
let toBoolFn = ( let toBoolFn = (
fnCall: GenericDist_Types.Operation.toBool, fnCall: DistributionTypes.DistributionOperation.toBool,
dist: GenericDist_Types.genericDist, 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) => { let toDistFn = (fnCall: DistributionTypes.DistributionOperation.toDist, dist) => {
FromDist(GenericDist_Types.Operation.ToDist(fnCall), dist)->runGenericOperation->Some FromDist(DistributionTypes.DistributionOperation.ToDist(fnCall), dist)
->runGenericOperation
->Some
} }
let twoDiststoDistFn = (direction, arithmetic, dist1, dist2) => { let twoDiststoDistFn = (direction, arithmetic, dist1, dist2) => {
FromDist( FromDist(
GenericDist_Types.Operation.ToDistCombination( DistributionTypes.DistributionOperation.ToDistCombination(
direction, direction,
arithmeticMap(arithmetic), arithmeticMap(arithmetic),
#Dist(dist2), #Dist(dist2),
@ -84,7 +92,7 @@ module Helpers = {
let parseNumberArray = (ags: array<expressionValue>): Belt.Result.t<array<float>, string> => let parseNumberArray = (ags: array<expressionValue>): Belt.Result.t<array<float>, string> =>
E.A.fmap(parseNumber, ags) |> E.A.R.firstErrorOrOpen E.A.fmap(parseNumber, ags) |> E.A.R.firstErrorOrOpen
let parseDist = (args: expressionValue): Belt.Result.t<GenericDist_Types.genericDist, string> => let parseDist = (args: expressionValue): Belt.Result.t<DistributionTypes.genericDist, string> =>
switch args { switch args {
| EvDistribution(x) => Ok(x) | EvDistribution(x) => Ok(x)
| EvNumber(x) => Ok(GenericDist.fromFloat(x)) | EvNumber(x) => Ok(GenericDist.fromFloat(x))
@ -92,12 +100,12 @@ module Helpers = {
} }
let parseDistributionArray = (ags: array<expressionValue>): Belt.Result.t< let parseDistributionArray = (ags: array<expressionValue>): Belt.Result.t<
array<GenericDist_Types.genericDist>, array<DistributionTypes.genericDist>,
string, string,
> => E.A.fmap(parseDist, ags) |> E.A.R.firstErrorOrOpen > => E.A.fmap(parseDist, ags) |> E.A.R.firstErrorOrOpen
let mixtureWithGivenWeights = ( let mixtureWithGivenWeights = (
distributions: array<GenericDist_Types.genericDist>, distributions: array<DistributionTypes.genericDist>,
weights: array<float>, weights: array<float>,
): DistributionOperation.outputType => ): DistributionOperation.outputType =>
E.A.length(distributions) == E.A.length(weights) E.A.length(distributions) == E.A.length(weights)
@ -107,7 +115,7 @@ module Helpers = {
) )
let mixtureWithDefaultWeights = ( let mixtureWithDefaultWeights = (
distributions: array<GenericDist_Types.genericDist>, distributions: array<DistributionTypes.genericDist>,
): DistributionOperation.outputType => { ): DistributionOperation.outputType => {
let length = E.A.length(distributions) let length = E.A.length(distributions)
let weights = Belt.Array.make(length, 1.0 /. Belt.Int.toFloat(length)) 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)) | Float(d) => Ok(EvNumber(d))
| String(d) => Ok(EvString(d)) | String(d) => Ok(EvString(d))
| Bool(d) => Ok(EvBool(d)) | Bool(d) => Ok(EvBool(d))
| GenDistError(NotYetImplemented) => Error(RETodo("Function not yet implemented")) | GenDistError(err) => Error(REDistributionError(err))
| 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))
} }
let dispatch = call => { let dispatch = call => {

View File

@ -53,4 +53,4 @@ type continuousShape = PointSetTypes.continuousShape
let errorValueToString = Reducer_ErrorValue.errorToString let errorValueToString = Reducer_ErrorValue.errorToString
@genType @genType
let distributionErrorToString = GenericDist_Types.Error.toString let distributionErrorToString = DistributionTypes.Error.toString

View File

@ -152,6 +152,8 @@ module I = {
let toString = Js.Int.toString let toString = Js.Int.toString
} }
exception Assertion(string)
/* R for Result */ /* R for Result */
module R = { module R = {
let result = Rationale.Result.result let result = Rationale.Result.result
@ -159,6 +161,11 @@ module R = {
let fmap = Rationale.Result.fmap let fmap = Rationale.Result.fmap
let bind = Rationale.Result.bind let bind = Rationale.Result.bind
let toExn = Belt.Result.getExn 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>) => let default = (default, res: Belt.Result.t<'a, 'b>) =>
switch res { switch res {
| Ok(r) => r | Ok(r) => r
@ -210,10 +217,10 @@ module R2 = {
let bind = (a, b) => R.bind(b, a) let bind = (a, b) => R.bind(b, a)
//Converts result type to change error type only //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 { switch a {
| Ok(r) => Ok(r) | Ok(r) => Ok(r)
| Error(e) => map(e) | Error(e) => Error(map(e))
} }
let fmap2 = (xR, f) => let fmap2 = (xR, f) =>
@ -436,6 +443,7 @@ module A = {
r |> Belt.Array.map(_, r => Belt.Result.getExn(r)) r |> Belt.Array.map(_, r => Belt.Result.getExn(r))
bringErrorUp |> Belt.Result.map(_, forceOpen) bringErrorUp |> Belt.Result.map(_, forceOpen)
} }
let filterOk = (x: array<result<'a, 'b>>): array<'a> => fmap(R.toOption, x)->O.concatSomes
} }
module Sorted = { module Sorted = {

View File

@ -37,22 +37,42 @@ module Convolution = {
} }
} }
module Algebraic = { @genType
type t = algebraicOperation type invalidOperationError =
let toFn: (t, float, float) => float = x => | DivisionByZeroError
switch x { | ComplexNumberError
| #Add => \"+."
| #Subtract => \"-." let invalidOperationErrorToString = (err: invalidOperationError): string =>
| #Multiply => \"*." switch err {
| #Power => \"**" | DivisionByZeroError => "Cannot divide by zero"
| #Divide => \"/." | ComplexNumberError => "Operation returned complex result"
| #Logarithm => (a, b) => log(a) /. log(b)
} }
let applyFn = (t, f1, f2) => module Algebraic = {
switch (t, f1, f2) { type t = algebraicOperation
| (#Divide, _, 0.) => Error("Cannot divide $v1 by zero.") let toFn: (t, float, float) => result<float, invalidOperationError> = (x, a, b) =>
| _ => Ok(toFn(t, f1, f2)) switch x {
| #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 => let toString = x =>
@ -96,12 +116,27 @@ module DistToFloat = {
// Note that different logarithms don't really do anything. // Note that different logarithms don't really do anything.
module Scale = { module Scale = {
type t = scaleOperation type t = scaleOperation
let toFn = x => let toFn = (x: t, a: float, b: float): result<float, invalidOperationError> =>
switch x { switch x {
| #Multiply => \"*." | #Multiply => Ok(a *. b)
| #Divide => \"/." | #Divide =>
| #Power => \"**" if b != 0.0 {
| #Logarithm => (a, b) => log(a) /. log(b) 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) => let format = (operation: t, value, scaleBy) =>

View File

@ -43,6 +43,10 @@ module T = {
let xTotalRange = (t: t) => maxX(t) -. minX(t) let xTotalRange = (t: t) => maxX(t) -. minX(t)
let mapX = (fn, t: t): t => {xs: E.A.fmap(fn, t.xs), ys: t.ys} let mapX = (fn, t: t): t => {xs: E.A.fmap(fn, t.xs), ys: t.ys}
let mapY = (fn, t: t): t => {xs: t.xs, ys: E.A.fmap(fn, t.ys)} let mapY = (fn, t: t): t => {xs: t.xs, ys: E.A.fmap(fn, t.ys)}
let mapYResult = (fn: float => result<float, 'e>, t: t): result<t, 'e> => {
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 square = mapX(x => x ** 2.0)
let zip = ({xs, ys}: t) => Belt.Array.zip(xs, ys) let zip = ({xs, ys}: t) => Belt.Array.zip(xs, ys)
let fromArray = ((xs, ys)): t => {xs: xs, ys: ys} let fromArray = ((xs, ys)): t => {xs: xs, ys: ys}