Merge pull request #589 from quantified-uncertainty/scoring-cleanup-three

Scoring cleanup
This commit is contained in:
Ozzie Gooen 2022-07-13 10:21:40 -07:00 committed by GitHub
commit 606cbd8859
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 717 additions and 462 deletions

View File

@ -1,7 +1,7 @@
open Jest open Jest
open Expect open Expect
let env: DistributionOperation.env = { let env: GenericDist.env = {
sampleCount: 100, sampleCount: 100,
xyPointLength: 100, xyPointLength: 100,
} }
@ -34,7 +34,7 @@ describe("sparkline", () => {
expected: DistributionOperation.outputType, expected: DistributionOperation.outputType,
) => { ) => {
test(name, () => { test(name, () => {
let result = DistributionOperation.run(~env, FromDist(ToString(ToSparkline(20)), dist)) let result = DistributionOperation.run(~env, FromDist(#ToString(ToSparkline(20)), dist))
expect(result)->toEqual(expected) expect(result)->toEqual(expected)
}) })
} }
@ -81,8 +81,8 @@ describe("sparkline", () => {
describe("toPointSet", () => { describe("toPointSet", () => {
test("on symbolic normal distribution", () => { test("on symbolic normal distribution", () => {
let result = let result =
run(FromDist(ToDist(ToPointSet), normalDist5)) run(FromDist(#ToDist(ToPointSet), normalDist5))
->outputMap(FromDist(ToFloat(#Mean))) ->outputMap(FromDist(#ToFloat(#Mean)))
->toFloat ->toFloat
->toExt ->toExt
expect(result)->toBeSoCloseTo(5.0, ~digits=0) expect(result)->toBeSoCloseTo(5.0, ~digits=0)
@ -90,10 +90,10 @@ describe("toPointSet", () => {
test("on sample set", () => { test("on sample set", () => {
let result = let result =
run(FromDist(ToDist(ToPointSet), normalDist5)) run(FromDist(#ToDist(ToPointSet), normalDist5))
->outputMap(FromDist(ToDist(ToSampleSet(1000)))) ->outputMap(FromDist(#ToDist(ToSampleSet(1000))))
->outputMap(FromDist(ToDist(ToPointSet))) ->outputMap(FromDist(#ToDist(ToPointSet)))
->outputMap(FromDist(ToFloat(#Mean))) ->outputMap(FromDist(#ToFloat(#Mean)))
->toFloat ->toFloat
->toExt ->toExt
expect(result)->toBeSoCloseTo(5.0, ~digits=-1) expect(result)->toBeSoCloseTo(5.0, ~digits=-1)

View File

@ -19,7 +19,6 @@ exception MixtureFailed
let float1 = 1.0 let float1 = 1.0
let float2 = 2.0 let float2 = 2.0
let float3 = 3.0 let float3 = 3.0
let {mkDelta} = module(TestHelpers) let point1 = TestHelpers.mkDelta(float1)
let point1 = mkDelta(float1) let point2 = TestHelpers.mkDelta(float2)
let point2 = mkDelta(float2) let point3 = TestHelpers.mkDelta(float3)
let point3 = mkDelta(float3)

View File

@ -11,7 +11,7 @@ describe("mixture", () => {
let (mean1, mean2) = tup let (mean1, mean2) = tup
let meanValue = { let meanValue = {
run(Mixture([(mkNormal(mean1, 9e-1), 0.5), (mkNormal(mean2, 9e-1), 0.5)]))->outputMap( run(Mixture([(mkNormal(mean1, 9e-1), 0.5), (mkNormal(mean2, 9e-1), 0.5)]))->outputMap(
FromDist(ToFloat(#Mean)), FromDist(#ToFloat(#Mean)),
) )
} }
meanValue->unpackFloat->expect->toBeSoCloseTo((mean1 +. mean2) /. 2.0, ~digits=-1) meanValue->unpackFloat->expect->toBeSoCloseTo((mean1 +. mean2) /. 2.0, ~digits=-1)
@ -28,7 +28,7 @@ describe("mixture", () => {
let meanValue = { let meanValue = {
run( run(
Mixture([(mkBeta(alpha, beta), betaWeight), (mkExponential(rate), exponentialWeight)]), Mixture([(mkBeta(alpha, beta), betaWeight), (mkExponential(rate), exponentialWeight)]),
)->outputMap(FromDist(ToFloat(#Mean))) )->outputMap(FromDist(#ToFloat(#Mean)))
} }
let betaMean = 1.0 /. (1.0 +. beta /. alpha) let betaMean = 1.0 /. (1.0 +. beta /. alpha)
let exponentialMean = 1.0 /. rate let exponentialMean = 1.0 /. rate
@ -52,7 +52,7 @@ describe("mixture", () => {
(mkUniform(low, high), uniformWeight), (mkUniform(low, high), uniformWeight),
(mkLognormal(mu, sigma), lognormalWeight), (mkLognormal(mu, sigma), lognormalWeight),
]), ]),
)->outputMap(FromDist(ToFloat(#Mean))) )->outputMap(FromDist(#ToFloat(#Mean)))
} }
let uniformMean = (low +. high) /. 2.0 let uniformMean = (low +. high) /. 2.0
let lognormalMean = mu +. sigma ** 2.0 /. 2.0 let lognormalMean = mu +. sigma ** 2.0 /. 2.0

View File

@ -3,6 +3,7 @@ open Expect
open TestHelpers open TestHelpers
open GenericDist_Fixtures open GenericDist_Fixtures
let klDivergence = DistributionOperation.Constructors.LogScore.distEstimateDistAnswer(~env)
// integral from low to high of 1 / (high - low) log(normal(mean, stdev)(x) / (1 / (high - low))) dx // integral from low to high of 1 / (high - low) log(normal(mean, stdev)(x) / (1 / (high - low))) dx
let klNormalUniform = (mean, stdev, low, high): float => let klNormalUniform = (mean, stdev, low, high): float =>
-.Js.Math.log((high -. low) /. Js.Math.sqrt(2.0 *. MagicNumbers.Math.pi *. stdev ** 2.0)) +. -.Js.Math.log((high -. low) /. Js.Math.sqrt(2.0 *. MagicNumbers.Math.pi *. stdev ** 2.0)) +.
@ -11,8 +12,6 @@ let klNormalUniform = (mean, stdev, low, high): float =>
(mean ** 2.0 -. (high +. low) *. mean +. (low ** 2.0 +. high *. low +. high ** 2.0) /. 3.0) (mean ** 2.0 -. (high +. low) *. mean +. (low ** 2.0 +. high *. low +. high ** 2.0) /. 3.0)
describe("klDivergence: continuous -> continuous -> float", () => { describe("klDivergence: continuous -> continuous -> float", () => {
let klDivergence = DistributionOperation.Constructors.klDivergence(~env)
let testUniform = (lowAnswer, highAnswer, lowPrediction, highPrediction) => { let testUniform = (lowAnswer, highAnswer, lowPrediction, highPrediction) => {
test("of two uniforms is equal to the analytic expression", () => { test("of two uniforms is equal to the analytic expression", () => {
let answer = let answer =
@ -58,7 +57,7 @@ describe("klDivergence: continuous -> continuous -> float", () => {
let kl = E.R.liftJoin2(klDivergence, prediction, answer) let kl = E.R.liftJoin2(klDivergence, prediction, answer)
switch kl { switch kl {
| Ok(kl') => kl'->expect->toBeSoCloseTo(analyticalKl, ~digits=3) | Ok(kl') => kl'->expect->toBeSoCloseTo(analyticalKl, ~digits=2)
| Error(err) => { | Error(err) => {
Js.Console.log(DistributionTypes.Error.toString(err)) Js.Console.log(DistributionTypes.Error.toString(err))
raise(KlFailed) raise(KlFailed)
@ -82,7 +81,6 @@ describe("klDivergence: continuous -> continuous -> float", () => {
}) })
describe("klDivergence: discrete -> discrete -> float", () => { describe("klDivergence: discrete -> discrete -> float", () => {
let klDivergence = DistributionOperation.Constructors.klDivergence(~env)
let mixture = a => DistributionTypes.DistributionOperation.Mixture(a) let mixture = a => DistributionTypes.DistributionOperation.Mixture(a)
let a' = [(point1, 1e0), (point2, 1e0)]->mixture->run let a' = [(point1, 1e0), (point2, 1e0)]->mixture->run
let b' = [(point1, 1e0), (point2, 1e0), (point3, 1e0)]->mixture->run let b' = [(point1, 1e0), (point2, 1e0), (point3, 1e0)]->mixture->run
@ -117,7 +115,6 @@ describe("klDivergence: discrete -> discrete -> float", () => {
}) })
describe("klDivergence: mixed -> mixed -> float", () => { describe("klDivergence: mixed -> mixed -> float", () => {
let klDivergence = DistributionOperation.Constructors.klDivergence(~env)
let mixture' = a => DistributionTypes.DistributionOperation.Mixture(a) let mixture' = a => DistributionTypes.DistributionOperation.Mixture(a)
let mixture = a => { let mixture = a => {
let dist' = a->mixture'->run let dist' = a->mixture'->run
@ -189,15 +186,15 @@ describe("combineAlongSupportOfSecondArgument0", () => {
uniformMakeR(lowPrediction, highPrediction)->E.R2.errMap(s => DistributionTypes.ArgumentError( uniformMakeR(lowPrediction, highPrediction)->E.R2.errMap(s => DistributionTypes.ArgumentError(
s, s,
)) ))
let answerWrapped = E.R.fmap(a => run(FromDist(ToDist(ToPointSet), a)), answer) let answerWrapped = E.R.fmap(a => run(FromDist(#ToDist(ToPointSet), a)), answer)
let predictionWrapped = E.R.fmap(a => run(FromDist(ToDist(ToPointSet), a)), prediction) let predictionWrapped = E.R.fmap(a => run(FromDist(#ToDist(ToPointSet), a)), prediction)
let interpolator = XYShape.XtoY.continuousInterpolator(#Stepwise, #UseZero) let interpolator = XYShape.XtoY.continuousInterpolator(#Stepwise, #UseZero)
let integrand = PointSetDist_Scoring.KLDivergence.integrand let integrand = PointSetDist_Scoring.WithDistAnswer.integrand
let result = switch (answerWrapped, predictionWrapped) { let result = switch (answerWrapped, predictionWrapped) {
| (Ok(Dist(PointSet(Continuous(a)))), Ok(Dist(PointSet(Continuous(b))))) => | (Ok(Dist(PointSet(Continuous(a)))), Ok(Dist(PointSet(Continuous(b))))) =>
Some(combineAlongSupportOfSecondArgument(integrand, interpolator, a.xyShape, b.xyShape)) Some(combineAlongSupportOfSecondArgument(interpolator, integrand, a.xyShape, b.xyShape))
| _ => None | _ => None
} }
result result

View File

@ -0,0 +1,68 @@
open Jest
open Expect
open TestHelpers
open GenericDist_Fixtures
exception ScoreFailed
describe("WithScalarAnswer: discrete -> scalar -> score", () => {
let mixture = a => DistributionTypes.DistributionOperation.Mixture(a)
let pointA = mkDelta(3.0)
let pointB = mkDelta(2.0)
let pointC = mkDelta(1.0)
let pointD = mkDelta(0.0)
test("score: agrees with analytical answer when finite", () => {
let prediction' = [(pointA, 0.25), (pointB, 0.25), (pointC, 0.25), (pointD, 0.25)]->mixture->run
let prediction = switch prediction' {
| Dist(PointSet(p)) => p
| _ => raise(MixtureFailed)
}
let answer = 2.0 // So this is: assigning 100% probability to 2.0
let result = PointSetDist_Scoring.WithScalarAnswer.score(~estimate=prediction, ~answer)
switch result {
| Ok(x) => x->expect->toEqual(-.Js.Math.log(0.25 /. 1.0))
| _ => raise(ScoreFailed)
}
})
test("score: agrees with analytical answer when finite", () => {
let prediction' = [(pointA, 0.75), (pointB, 0.25)]->mixture->run
let prediction = switch prediction' {
| Dist(PointSet(p)) => p
| _ => raise(MixtureFailed)
}
let answer = 3.0 // So this is: assigning 100% probability to 2.0
let result = PointSetDist_Scoring.WithScalarAnswer.score(~estimate=prediction, ~answer)
switch result {
| Ok(x) => x->expect->toEqual(-.Js.Math.log(0.75 /. 1.0))
| _ => raise(ScoreFailed)
}
})
test("scoreWithPrior: agrees with analytical answer when finite", () => {
let prior' = [(pointA, 0.5), (pointB, 0.5)]->mixture->run
let prediction' = [(pointA, 0.75), (pointB, 0.25)]->mixture->run
let prediction = switch prediction' {
| Dist(PointSet(p)) => p
| _ => raise(MixtureFailed)
}
let prior = switch prior' {
| Dist(PointSet(p)) => p
| _ => raise(MixtureFailed)
}
let answer = 3.0 // So this is: assigning 100% probability to 2.0
let result = PointSetDist_Scoring.WithScalarAnswer.scoreWithPrior(
~estimate=prediction,
~answer,
~prior,
)
switch result {
| Ok(x) => x->expect->toEqual(-.Js.Math.log(0.75 /. 1.0) -. -.Js.Math.log(0.5 /. 1.0))
| _ => raise(ScoreFailed)
}
})
})

View File

@ -8,34 +8,34 @@ let mkNormal = (mean, stdev) => DistributionTypes.Symbolic(#Normal({mean: mean,
describe("(Symbolic) normalize", () => { describe("(Symbolic) normalize", () => {
testAll("has no impact on normal distributions", list{-1e8, -1e-2, 0.0, 1e-4, 1e16}, mean => { testAll("has no impact on normal distributions", list{-1e8, -1e-2, 0.0, 1e-4, 1e16}, mean => {
let normalValue = mkNormal(mean, 2.0) let normalValue = mkNormal(mean, 2.0)
let normalizedValue = run(FromDist(ToDist(Normalize), normalValue)) let normalizedValue = run(FromDist(#ToDist(Normalize), normalValue))
normalizedValue->unpackDist->expect->toEqual(normalValue) normalizedValue->unpackDist->expect->toEqual(normalValue)
}) })
}) })
describe("(Symbolic) mean", () => { describe("(Symbolic) mean", () => {
testAll("of normal distributions", list{-1e8, -16.0, -1e-2, 0.0, 1e-4, 32.0, 1e16}, mean => { testAll("of normal distributions", list{-1e8, -16.0, -1e-2, 0.0, 1e-4, 32.0, 1e16}, mean => {
run(FromDist(ToFloat(#Mean), mkNormal(mean, 4.0)))->unpackFloat->expect->toBeCloseTo(mean) run(FromDist(#ToFloat(#Mean), mkNormal(mean, 4.0)))->unpackFloat->expect->toBeCloseTo(mean)
}) })
Skip.test("of normal(0, -1) (it NaNs out)", () => { Skip.test("of normal(0, -1) (it NaNs out)", () => {
run(FromDist(ToFloat(#Mean), mkNormal(1e1, -1e0)))->unpackFloat->expect->ExpectJs.toBeFalsy run(FromDist(#ToFloat(#Mean), mkNormal(1e1, -1e0)))->unpackFloat->expect->ExpectJs.toBeFalsy
}) })
test("of normal(0, 1e-8) (it doesn't freak out at tiny stdev)", () => { test("of normal(0, 1e-8) (it doesn't freak out at tiny stdev)", () => {
run(FromDist(ToFloat(#Mean), mkNormal(0.0, 1e-8)))->unpackFloat->expect->toBeCloseTo(0.0) run(FromDist(#ToFloat(#Mean), mkNormal(0.0, 1e-8)))->unpackFloat->expect->toBeCloseTo(0.0)
}) })
testAll("of exponential distributions", list{1e-7, 2.0, 10.0, 100.0}, rate => { testAll("of exponential distributions", list{1e-7, 2.0, 10.0, 100.0}, rate => {
let meanValue = run( let meanValue = run(
FromDist(ToFloat(#Mean), DistributionTypes.Symbolic(#Exponential({rate: rate}))), FromDist(#ToFloat(#Mean), DistributionTypes.Symbolic(#Exponential({rate: rate}))),
) )
meanValue->unpackFloat->expect->toBeCloseTo(1.0 /. rate) // https://en.wikipedia.org/wiki/Exponential_distribution#Mean,_variance,_moments,_and_median meanValue->unpackFloat->expect->toBeCloseTo(1.0 /. rate) // https://en.wikipedia.org/wiki/Exponential_distribution#Mean,_variance,_moments,_and_median
}) })
test("of a cauchy distribution", () => { test("of a cauchy distribution", () => {
let meanValue = run( let meanValue = run(
FromDist(ToFloat(#Mean), DistributionTypes.Symbolic(#Cauchy({local: 1.0, scale: 1.0}))), FromDist(#ToFloat(#Mean), DistributionTypes.Symbolic(#Cauchy({local: 1.0, scale: 1.0}))),
) )
meanValue->unpackFloat->expect->toBeSoCloseTo(1.0098094001641797, ~digits=5) meanValue->unpackFloat->expect->toBeSoCloseTo(1.0098094001641797, ~digits=5)
//-> toBe(GenDistError(Other("Cauchy distributions may have no mean value."))) //-> toBe(GenDistError(Other("Cauchy distributions may have no mean value.")))
@ -48,7 +48,7 @@ describe("(Symbolic) mean", () => {
let (low, medium, high) = tup let (low, medium, high) = tup
let meanValue = run( let meanValue = run(
FromDist( FromDist(
ToFloat(#Mean), #ToFloat(#Mean),
DistributionTypes.Symbolic(#Triangular({low: low, medium: medium, high: high})), DistributionTypes.Symbolic(#Triangular({low: low, medium: medium, high: high})),
), ),
) )
@ -63,7 +63,7 @@ describe("(Symbolic) mean", () => {
tup => { tup => {
let (alpha, beta) = tup let (alpha, beta) = tup
let meanValue = run( let meanValue = run(
FromDist(ToFloat(#Mean), DistributionTypes.Symbolic(#Beta({alpha: alpha, beta: beta}))), FromDist(#ToFloat(#Mean), DistributionTypes.Symbolic(#Beta({alpha: alpha, beta: beta}))),
) )
meanValue->unpackFloat->expect->toBeCloseTo(1.0 /. (1.0 +. beta /. alpha)) // https://en.wikipedia.org/wiki/Beta_distribution#Mean meanValue->unpackFloat->expect->toBeCloseTo(1.0 /. (1.0 +. beta /. alpha)) // https://en.wikipedia.org/wiki/Beta_distribution#Mean
}, },
@ -72,7 +72,7 @@ describe("(Symbolic) mean", () => {
// TODO: When we have our theory of validators we won't want this to be NaN but to be an error. // TODO: When we have our theory of validators we won't want this to be NaN but to be an error.
test("of beta(0, 0)", () => { test("of beta(0, 0)", () => {
let meanValue = run( let meanValue = run(
FromDist(ToFloat(#Mean), DistributionTypes.Symbolic(#Beta({alpha: 0.0, beta: 0.0}))), FromDist(#ToFloat(#Mean), DistributionTypes.Symbolic(#Beta({alpha: 0.0, beta: 0.0}))),
) )
meanValue->unpackFloat->expect->ExpectJs.toBeFalsy meanValue->unpackFloat->expect->ExpectJs.toBeFalsy
}) })
@ -85,7 +85,7 @@ describe("(Symbolic) mean", () => {
let betaDistribution = SymbolicDist.Beta.fromMeanAndStdev(mean, stdev) let betaDistribution = SymbolicDist.Beta.fromMeanAndStdev(mean, stdev)
let meanValue = let meanValue =
betaDistribution->E.R2.fmap(d => betaDistribution->E.R2.fmap(d =>
run(FromDist(ToFloat(#Mean), d->DistributionTypes.Symbolic)) run(FromDist(#ToFloat(#Mean), d->DistributionTypes.Symbolic))
) )
switch meanValue { switch meanValue {
| Ok(value) => value->unpackFloat->expect->toBeCloseTo(mean) | Ok(value) => value->unpackFloat->expect->toBeCloseTo(mean)
@ -100,7 +100,7 @@ describe("(Symbolic) mean", () => {
tup => { tup => {
let (mu, sigma) = tup let (mu, sigma) = tup
let meanValue = run( let meanValue = run(
FromDist(ToFloat(#Mean), DistributionTypes.Symbolic(#Lognormal({mu: mu, sigma: sigma}))), FromDist(#ToFloat(#Mean), DistributionTypes.Symbolic(#Lognormal({mu: mu, sigma: sigma}))),
) )
meanValue->unpackFloat->expect->toBeCloseTo(Js.Math.exp(mu +. sigma ** 2.0 /. 2.0)) // https://brilliant.org/wiki/log-normal-distribution/ meanValue->unpackFloat->expect->toBeCloseTo(Js.Math.exp(mu +. sigma ** 2.0 /. 2.0)) // https://brilliant.org/wiki/log-normal-distribution/
}, },
@ -112,14 +112,14 @@ describe("(Symbolic) mean", () => {
tup => { tup => {
let (low, high) = tup let (low, high) = tup
let meanValue = run( let meanValue = run(
FromDist(ToFloat(#Mean), DistributionTypes.Symbolic(#Uniform({low: low, high: high}))), FromDist(#ToFloat(#Mean), DistributionTypes.Symbolic(#Uniform({low: low, high: high}))),
) )
meanValue->unpackFloat->expect->toBeCloseTo((low +. high) /. 2.0) // https://en.wikipedia.org/wiki/Continuous_uniform_distribution#Moments meanValue->unpackFloat->expect->toBeCloseTo((low +. high) /. 2.0) // https://en.wikipedia.org/wiki/Continuous_uniform_distribution#Moments
}, },
) )
test("of a float", () => { test("of a float", () => {
let meanValue = run(FromDist(ToFloat(#Mean), DistributionTypes.Symbolic(#Float(7.7)))) let meanValue = run(FromDist(#ToFloat(#Mean), DistributionTypes.Symbolic(#Float(7.7))))
meanValue->unpackFloat->expect->toBeCloseTo(7.7) meanValue->unpackFloat->expect->toBeCloseTo(7.7)
}) })
}) })

View File

@ -29,7 +29,7 @@ let {toFloat, toDist, toString, toError, fmap} = module(DistributionOperation.Ou
let fnImage = (theFn, inps) => Js.Array.map(theFn, inps) let fnImage = (theFn, inps) => Js.Array.map(theFn, inps)
let env: DistributionOperation.env = { let env: GenericDist.env = {
sampleCount: MagicNumbers.Environment.defaultSampleCount, sampleCount: MagicNumbers.Environment.defaultSampleCount,
xyPointLength: MagicNumbers.Environment.defaultXYPointLength, xyPointLength: MagicNumbers.Environment.defaultXYPointLength,
} }

View File

@ -4,12 +4,9 @@ type error = DistributionTypes.error
// TODO: It could be great to use a cache for some calculations (basically, do memoization). Also, better analytics/tracking could go a long way. // TODO: It could be great to use a cache for some calculations (basically, do memoization). Also, better analytics/tracking could go a long way.
type env = { type env = GenericDist.env
sampleCount: int,
xyPointLength: int,
}
let defaultEnv = { let defaultEnv: env = {
sampleCount: MagicNumbers.Environment.defaultSampleCount, sampleCount: MagicNumbers.Environment.defaultSampleCount,
xyPointLength: MagicNumbers.Environment.defaultXYPointLength, xyPointLength: MagicNumbers.Environment.defaultXYPointLength,
} }
@ -93,7 +90,7 @@ module OutputLocal = {
} }
} }
let rec run = (~env, functionCallInfo: functionCallInfo): outputType => { let rec run = (~env: env, functionCallInfo: functionCallInfo): outputType => {
let {sampleCount, xyPointLength} = env let {sampleCount, xyPointLength} = env
let reCall = (~env=env, ~functionCallInfo=functionCallInfo, ()) => { let reCall = (~env=env, ~functionCallInfo=functionCallInfo, ()) => {
@ -101,14 +98,14 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => {
} }
let toPointSetFn = r => { let toPointSetFn = r => {
switch reCall(~functionCallInfo=FromDist(ToDist(ToPointSet), r), ()) { switch reCall(~functionCallInfo=FromDist(#ToDist(ToPointSet), r), ()) {
| Dist(PointSet(p)) => Ok(p) | Dist(PointSet(p)) => Ok(p)
| e => Error(OutputLocal.toErrorOrUnreachable(e)) | e => Error(OutputLocal.toErrorOrUnreachable(e))
} }
} }
let toSampleSetFn = r => { let toSampleSetFn = r => {
switch reCall(~functionCallInfo=FromDist(ToDist(ToSampleSet(sampleCount)), r), ()) { switch reCall(~functionCallInfo=FromDist(#ToDist(ToSampleSet(sampleCount)), r), ()) {
| Dist(SampleSet(p)) => Ok(p) | Dist(SampleSet(p)) => Ok(p)
| e => Error(OutputLocal.toErrorOrUnreachable(e)) | e => Error(OutputLocal.toErrorOrUnreachable(e))
} }
@ -116,13 +113,13 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => {
let scaleMultiply = (r, weight) => let scaleMultiply = (r, weight) =>
reCall( reCall(
~functionCallInfo=FromDist(ToDistCombination(Pointwise, #Multiply, #Float(weight)), r), ~functionCallInfo=FromDist(#ToDistCombination(Pointwise, #Multiply, #Float(weight)), r),
(), (),
)->OutputLocal.toDistR )->OutputLocal.toDistR
let pointwiseAdd = (r1, r2) => let pointwiseAdd = (r1, r2) =>
reCall( reCall(
~functionCallInfo=FromDist(ToDistCombination(Pointwise, #Add, #Dist(r2)), r1), ~functionCallInfo=FromDist(#ToDistCombination(Pointwise, #Add, #Dist(r2)), r1),
(), (),
)->OutputLocal.toDistR )->OutputLocal.toDistR
@ -131,49 +128,40 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => {
dist: genericDist, dist: genericDist,
): outputType => { ): outputType => {
let response = switch subFnName { let response = switch subFnName {
| ToFloat(distToFloatOperation) => | #ToFloat(distToFloatOperation) =>
GenericDist.toFloatOperation(dist, ~toPointSetFn, ~distToFloatOperation) GenericDist.toFloatOperation(dist, ~toPointSetFn, ~distToFloatOperation)
->E.R2.fmap(r => Float(r)) ->E.R2.fmap(r => Float(r))
->OutputLocal.fromResult ->OutputLocal.fromResult
| ToString(ToString) => dist->GenericDist.toString->String | #ToString(ToString) => dist->GenericDist.toString->String
| ToString(ToSparkline(bucketCount)) => | #ToString(ToSparkline(bucketCount)) =>
GenericDist.toSparkline(dist, ~sampleCount, ~bucketCount, ()) GenericDist.toSparkline(dist, ~sampleCount, ~bucketCount, ())
->E.R2.fmap(r => String(r)) ->E.R2.fmap(r => String(r))
->OutputLocal.fromResult ->OutputLocal.fromResult
| ToDist(Inspect) => { | #ToDist(Inspect) => {
Js.log2("Console log requested: ", dist) Js.log2("Console log requested: ", dist)
Dist(dist) Dist(dist)
} }
| ToDist(Normalize) => dist->GenericDist.normalize->Dist | #ToDist(Normalize) => dist->GenericDist.normalize->Dist
| ToScore(KLDivergence(t2)) => | #ToScore(LogScore(answer, prior)) =>
GenericDist.Score.klDivergence(dist, t2, ~toPointSetFn) GenericDist.Score.logScore(~estimate=dist, ~answer, ~prior, ~env)
->E.R2.fmap(r => Float(r)) ->E.R2.fmap(s => Float(s))
->OutputLocal.fromResult ->OutputLocal.fromResult
| ToScore(LogScore(answer, prior)) => | #ToBool(IsNormalized) => dist->GenericDist.isNormalized->Bool
GenericDist.Score.logScoreWithPointResolution( | #ToDist(Truncate(leftCutoff, rightCutoff)) =>
~prediction=dist,
~answer,
~prior,
~toPointSetFn,
)
->E.R2.fmap(r => Float(r))
->OutputLocal.fromResult
| ToBool(IsNormalized) => dist->GenericDist.isNormalized->Bool
| ToDist(Truncate(leftCutoff, rightCutoff)) =>
GenericDist.truncate(~toPointSetFn, ~leftCutoff, ~rightCutoff, dist, ()) GenericDist.truncate(~toPointSetFn, ~leftCutoff, ~rightCutoff, dist, ())
->E.R2.fmap(r => Dist(r)) ->E.R2.fmap(r => Dist(r))
->OutputLocal.fromResult ->OutputLocal.fromResult
| ToDist(ToSampleSet(n)) => | #ToDist(ToSampleSet(n)) =>
dist dist
->GenericDist.toSampleSetDist(n) ->GenericDist.toSampleSetDist(n)
->E.R2.fmap(r => Dist(SampleSet(r))) ->E.R2.fmap(r => Dist(SampleSet(r)))
->OutputLocal.fromResult ->OutputLocal.fromResult
| ToDist(ToPointSet) => | #ToDist(ToPointSet) =>
dist dist
->GenericDist.toPointSet(~xyPointLength, ~sampleCount, ()) ->GenericDist.toPointSet(~xyPointLength, ~sampleCount, ())
->E.R2.fmap(r => Dist(PointSet(r))) ->E.R2.fmap(r => Dist(PointSet(r)))
->OutputLocal.fromResult ->OutputLocal.fromResult
| ToDist(Scale(#LogarithmWithThreshold(eps), f)) => | #ToDist(Scale(#LogarithmWithThreshold(eps), f)) =>
dist dist
->GenericDist.pointwiseCombinationFloat( ->GenericDist.pointwiseCombinationFloat(
~toPointSetFn, ~toPointSetFn,
@ -182,23 +170,23 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => {
) )
->E.R2.fmap(r => Dist(r)) ->E.R2.fmap(r => Dist(r))
->OutputLocal.fromResult ->OutputLocal.fromResult
| ToDist(Scale(#Multiply, f)) => | #ToDist(Scale(#Multiply, f)) =>
dist dist
->GenericDist.pointwiseCombinationFloat(~toPointSetFn, ~algebraicCombination=#Multiply, ~f) ->GenericDist.pointwiseCombinationFloat(~toPointSetFn, ~algebraicCombination=#Multiply, ~f)
->E.R2.fmap(r => Dist(r)) ->E.R2.fmap(r => Dist(r))
->OutputLocal.fromResult ->OutputLocal.fromResult
| ToDist(Scale(#Logarithm, f)) => | #ToDist(Scale(#Logarithm, f)) =>
dist dist
->GenericDist.pointwiseCombinationFloat(~toPointSetFn, ~algebraicCombination=#Logarithm, ~f) ->GenericDist.pointwiseCombinationFloat(~toPointSetFn, ~algebraicCombination=#Logarithm, ~f)
->E.R2.fmap(r => Dist(r)) ->E.R2.fmap(r => Dist(r))
->OutputLocal.fromResult ->OutputLocal.fromResult
| ToDist(Scale(#Power, f)) => | #ToDist(Scale(#Power, f)) =>
dist dist
->GenericDist.pointwiseCombinationFloat(~toPointSetFn, ~algebraicCombination=#Power, ~f) ->GenericDist.pointwiseCombinationFloat(~toPointSetFn, ~algebraicCombination=#Power, ~f)
->E.R2.fmap(r => Dist(r)) ->E.R2.fmap(r => Dist(r))
->OutputLocal.fromResult ->OutputLocal.fromResult
| ToDistCombination(Algebraic(_), _, #Float(_)) => GenDistError(NotYetImplemented) | #ToDistCombination(Algebraic(_), _, #Float(_)) => GenDistError(NotYetImplemented)
| ToDistCombination(Algebraic(strategy), arithmeticOperation, #Dist(t2)) => | #ToDistCombination(Algebraic(strategy), arithmeticOperation, #Dist(t2)) =>
dist dist
->GenericDist.algebraicCombination( ->GenericDist.algebraicCombination(
~strategy, ~strategy,
@ -209,12 +197,12 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => {
) )
->E.R2.fmap(r => Dist(r)) ->E.R2.fmap(r => Dist(r))
->OutputLocal.fromResult ->OutputLocal.fromResult
| ToDistCombination(Pointwise, algebraicCombination, #Dist(t2)) => | #ToDistCombination(Pointwise, algebraicCombination, #Dist(t2)) =>
dist dist
->GenericDist.pointwiseCombination(~toPointSetFn, ~algebraicCombination, ~t2) ->GenericDist.pointwiseCombination(~toPointSetFn, ~algebraicCombination, ~t2)
->E.R2.fmap(r => Dist(r)) ->E.R2.fmap(r => Dist(r))
->OutputLocal.fromResult ->OutputLocal.fromResult
| ToDistCombination(Pointwise, algebraicCombination, #Float(f)) => | #ToDistCombination(Pointwise, algebraicCombination, #Float(f)) =>
dist dist
->GenericDist.pointwiseCombinationFloat(~toPointSetFn, ~algebraicCombination, ~f) ->GenericDist.pointwiseCombinationFloat(~toPointSetFn, ~algebraicCombination, ~f)
->E.R2.fmap(r => Dist(r)) ->E.R2.fmap(r => Dist(r))
@ -225,8 +213,7 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => {
switch functionCallInfo { switch functionCallInfo {
| FromDist(subFnName, dist) => fromDistFn(subFnName, dist) | FromDist(subFnName, dist) => fromDistFn(subFnName, dist)
| FromFloat(subFnName, float) => | FromFloat(subFnName, x) => reCall(~functionCallInfo=FromFloat(subFnName, x), ())
reCall(~functionCallInfo=FromDist(subFnName, GenericDist.fromFloat(float)), ())
| Mixture(dists) => | Mixture(dists) =>
dists dists
->GenericDist.mixture(~scaleMultiplyFn=scaleMultiply, ~pointwiseAddFn=pointwiseAdd) ->GenericDist.mixture(~scaleMultiplyFn=scaleMultiply, ~pointwiseAddFn=pointwiseAdd)
@ -278,13 +265,16 @@ module Constructors = {
let pdf = (~env, dist, f) => C.pdf(dist, f)->run(~env)->toFloatR let pdf = (~env, dist, f) => C.pdf(dist, f)->run(~env)->toFloatR
let normalize = (~env, dist) => C.normalize(dist)->run(~env)->toDistR let normalize = (~env, dist) => C.normalize(dist)->run(~env)->toDistR
let isNormalized = (~env, dist) => C.isNormalized(dist)->run(~env)->toBoolR let isNormalized = (~env, dist) => C.isNormalized(dist)->run(~env)->toBoolR
let klDivergence = (~env, dist1, dist2) => C.klDivergence(dist1, dist2)->run(~env)->toFloatR module LogScore = {
let logScoreWithPointResolution = ( let distEstimateDistAnswer = (~env, estimate, answer) =>
~env, C.LogScore.distEstimateDistAnswer(estimate, answer)->run(~env)->toFloatR
~prediction: DistributionTypes.genericDist, let distEstimateDistAnswerWithPrior = (~env, estimate, answer, prior) =>
~answer: float, C.LogScore.distEstimateDistAnswerWithPrior(estimate, answer, prior)->run(~env)->toFloatR
~prior: option<DistributionTypes.genericDist>, let distEstimateScalarAnswer = (~env, estimate, answer) =>
) => C.logScoreWithPointResolution(~prediction, ~answer, ~prior)->run(~env)->toFloatR C.LogScore.distEstimateScalarAnswer(estimate, answer)->run(~env)->toFloatR
let distEstimateScalarAnswerWithPrior = (~env, estimate, answer, prior) =>
C.LogScore.distEstimateScalarAnswerWithPrior(estimate, answer, prior)->run(~env)->toFloatR
}
let toPointSet = (~env, dist) => C.toPointSet(dist)->run(~env)->toDistR let toPointSet = (~env, dist) => C.toPointSet(dist)->run(~env)->toDistR
let toSampleSet = (~env, dist, n) => C.toSampleSet(dist, n)->run(~env)->toDistR let toSampleSet = (~env, dist, n) => C.toSampleSet(dist, n)->run(~env)->toDistR
let fromSamples = (~env, xs) => C.fromSamples(xs)->run(~env)->toDistR let fromSamples = (~env, xs) => C.fromSamples(xs)->run(~env)->toDistR

View File

@ -1,11 +1,5 @@
@genType @genType
type env = { let defaultEnv: GenericDist.env
sampleCount: int,
xyPointLength: int,
}
@genType
let defaultEnv: env
open DistributionTypes open DistributionTypes
@ -19,15 +13,18 @@ type outputType =
| GenDistError(error) | GenDistError(error)
@genType @genType
let run: (~env: env, DistributionTypes.DistributionOperation.genericFunctionCallInfo) => outputType let run: (
~env: GenericDist.env,
DistributionTypes.DistributionOperation.genericFunctionCallInfo,
) => outputType
let runFromDist: ( let runFromDist: (
~env: env, ~env: GenericDist.env,
~functionCallInfo: DistributionTypes.DistributionOperation.fromDist, ~functionCallInfo: DistributionTypes.DistributionOperation.fromDist,
genericDist, genericDist,
) => outputType ) => outputType
let runFromFloat: ( let runFromFloat: (
~env: env, ~env: GenericDist.env,
~functionCallInfo: DistributionTypes.DistributionOperation.fromDist, ~functionCallInfo: DistributionTypes.DistributionOperation.fromFloat,
float, float,
) => outputType ) => outputType
@ -42,79 +39,147 @@ 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, DistributionTypes.DistributionOperation.singleParamaterFunction) => t let fmap: (
~env: GenericDist.env,
t,
DistributionTypes.DistributionOperation.singleParamaterFunction,
) => t
} }
module Constructors: { module Constructors: {
@genType @genType
let mean: (~env: env, genericDist) => result<float, error> let mean: (~env: GenericDist.env, genericDist) => result<float, error>
@genType @genType
let stdev: (~env: env, genericDist) => result<float, error> let stdev: (~env: GenericDist.env, genericDist) => result<float, error>
@genType @genType
let variance: (~env: env, genericDist) => result<float, error> let variance: (~env: GenericDist.env, genericDist) => result<float, error>
@genType @genType
let sample: (~env: env, genericDist) => result<float, error> let sample: (~env: GenericDist.env, genericDist) => result<float, error>
@genType @genType
let cdf: (~env: env, genericDist, float) => result<float, error> let cdf: (~env: GenericDist.env, genericDist, float) => result<float, error>
@genType @genType
let inv: (~env: env, genericDist, float) => result<float, error> let inv: (~env: GenericDist.env, genericDist, float) => result<float, error>
@genType @genType
let pdf: (~env: env, genericDist, float) => result<float, error> let pdf: (~env: GenericDist.env, genericDist, float) => result<float, error>
@genType @genType
let normalize: (~env: env, genericDist) => result<genericDist, error> let normalize: (~env: GenericDist.env, genericDist) => result<genericDist, error>
@genType @genType
let isNormalized: (~env: env, genericDist) => result<bool, error> let isNormalized: (~env: GenericDist.env, genericDist) => result<bool, error>
module LogScore: {
@genType
let distEstimateDistAnswer: (
~env: GenericDist.env,
genericDist,
genericDist,
) => result<float, error>
@genType
let distEstimateDistAnswerWithPrior: (
~env: GenericDist.env,
genericDist,
genericDist,
genericDist,
) => result<float, error>
@genType
let distEstimateScalarAnswer: (
~env: GenericDist.env,
genericDist,
float,
) => result<float, error>
@genType
let distEstimateScalarAnswerWithPrior: (
~env: GenericDist.env,
genericDist,
float,
genericDist,
) => result<float, error>
}
@genType @genType
let klDivergence: (~env: env, genericDist, genericDist) => result<float, error> let toPointSet: (~env: GenericDist.env, genericDist) => result<genericDist, error>
@genType @genType
let logScoreWithPointResolution: ( let toSampleSet: (~env: GenericDist.env, genericDist, int) => result<genericDist, error>
~env: env,
~prediction: genericDist,
~answer: float,
~prior: option<genericDist>,
) => result<float, error>
@genType @genType
let toPointSet: (~env: env, genericDist) => result<genericDist, error> let fromSamples: (~env: GenericDist.env, SampleSetDist.t) => result<genericDist, error>
@genType @genType
let toSampleSet: (~env: env, genericDist, int) => result<genericDist, error> let truncate: (
~env: GenericDist.env,
genericDist,
option<float>,
option<float>,
) => result<genericDist, error>
@genType @genType
let fromSamples: (~env: env, SampleSetDist.t) => result<genericDist, error> let inspect: (~env: GenericDist.env, genericDist) => result<genericDist, error>
@genType @genType
let truncate: (~env: env, genericDist, option<float>, option<float>) => result<genericDist, error> let toString: (~env: GenericDist.env, genericDist) => result<string, error>
@genType @genType
let inspect: (~env: env, genericDist) => result<genericDist, error> let toSparkline: (~env: GenericDist.env, genericDist, int) => result<string, error>
@genType @genType
let toString: (~env: env, genericDist) => result<string, error> let algebraicAdd: (~env: GenericDist.env, genericDist, genericDist) => result<genericDist, error>
@genType @genType
let toSparkline: (~env: env, genericDist, int) => result<string, error> let algebraicMultiply: (
~env: GenericDist.env,
genericDist,
genericDist,
) => result<genericDist, error>
@genType @genType
let algebraicAdd: (~env: env, genericDist, genericDist) => result<genericDist, error> let algebraicDivide: (
~env: GenericDist.env,
genericDist,
genericDist,
) => result<genericDist, error>
@genType @genType
let algebraicMultiply: (~env: env, genericDist, genericDist) => result<genericDist, error> let algebraicSubtract: (
~env: GenericDist.env,
genericDist,
genericDist,
) => result<genericDist, error>
@genType @genType
let algebraicDivide: (~env: env, genericDist, genericDist) => result<genericDist, error> let algebraicLogarithm: (
~env: GenericDist.env,
genericDist,
genericDist,
) => result<genericDist, error>
@genType @genType
let algebraicSubtract: (~env: env, genericDist, genericDist) => result<genericDist, error> let algebraicPower: (
~env: GenericDist.env,
genericDist,
genericDist,
) => result<genericDist, error>
@genType @genType
let algebraicLogarithm: (~env: env, genericDist, genericDist) => result<genericDist, error> let scaleLogarithm: (~env: GenericDist.env, genericDist, float) => result<genericDist, error>
@genType @genType
let algebraicPower: (~env: env, genericDist, genericDist) => result<genericDist, error> let scaleMultiply: (~env: GenericDist.env, genericDist, float) => result<genericDist, error>
@genType @genType
let scaleLogarithm: (~env: env, genericDist, float) => result<genericDist, error> let scalePower: (~env: GenericDist.env, genericDist, float) => result<genericDist, error>
@genType @genType
let scaleMultiply: (~env: env, genericDist, float) => result<genericDist, error> let pointwiseAdd: (~env: GenericDist.env, genericDist, genericDist) => result<genericDist, error>
@genType @genType
let scalePower: (~env: env, genericDist, float) => result<genericDist, error> let pointwiseMultiply: (
~env: GenericDist.env,
genericDist,
genericDist,
) => result<genericDist, error>
@genType @genType
let pointwiseAdd: (~env: env, genericDist, genericDist) => result<genericDist, error> let pointwiseDivide: (
~env: GenericDist.env,
genericDist,
genericDist,
) => result<genericDist, error>
@genType @genType
let pointwiseMultiply: (~env: env, genericDist, genericDist) => result<genericDist, error> let pointwiseSubtract: (
~env: GenericDist.env,
genericDist,
genericDist,
) => result<genericDist, error>
@genType @genType
let pointwiseDivide: (~env: env, genericDist, genericDist) => result<genericDist, error> let pointwiseLogarithm: (
~env: GenericDist.env,
genericDist,
genericDist,
) => result<genericDist, error>
@genType @genType
let pointwiseSubtract: (~env: env, genericDist, genericDist) => result<genericDist, error> let pointwisePower: (
@genType ~env: GenericDist.env,
let pointwiseLogarithm: (~env: env, genericDist, genericDist) => result<genericDist, error> genericDist,
@genType genericDist,
let pointwisePower: (~env: env, genericDist, genericDist) => result<genericDist, error> ) => result<genericDist, error>
} }

View File

@ -98,61 +98,86 @@ module DistributionOperation = {
| ToString | ToString
| ToSparkline(int) | ToSparkline(int)
type toScore = KLDivergence(genericDist) | LogScore(float, option<genericDist>) type genericDistOrScalar = Score_Dist(genericDist) | Score_Scalar(float)
type fromDist = type toScore = LogScore(genericDistOrScalar, option<genericDist>)
| ToFloat(toFloat)
| ToDist(toDist) type fromFloat = [
| ToScore(toScore) | #ToFloat(toFloat)
| ToDistCombination(direction, Operation.Algebraic.t, [#Dist(genericDist) | #Float(float)]) | #ToDist(toDist)
| ToString(toString) | #ToDistCombination(direction, Operation.Algebraic.t, [#Dist(genericDist) | #Float(float)])
| ToBool(toBool) | #ToString(toString)
| #ToBool(toBool)
]
type fromDist = [
| fromFloat
| #ToScore(toScore)
]
type singleParamaterFunction = type singleParamaterFunction =
| FromDist(fromDist) | FromDist(fromDist)
| FromFloat(fromDist) | FromFloat(fromFloat)
type genericFunctionCallInfo = type genericFunctionCallInfo =
| FromDist(fromDist, genericDist) | FromDist(fromDist, genericDist)
| FromFloat(fromDist, float) | FromFloat(fromFloat, float)
| FromSamples(array<float>) | FromSamples(array<float>)
| Mixture(array<(genericDist, float)>) | Mixture(array<(genericDist, float)>)
let distCallToString = (distFunction: fromDist): string => let floatCallToString = (floatFunction: fromFloat): string =>
switch distFunction { switch floatFunction {
| ToFloat(#Cdf(r)) => `cdf(${E.Float.toFixed(r)})` | #ToFloat(#Cdf(r)) => `cdf(${E.Float.toFixed(r)})`
| ToFloat(#Inv(r)) => `inv(${E.Float.toFixed(r)})` | #ToFloat(#Inv(r)) => `inv(${E.Float.toFixed(r)})`
| ToFloat(#Mean) => `mean` | #ToFloat(#Mean) => `mean`
| ToFloat(#Min) => `min` | #ToFloat(#Min) => `min`
| ToFloat(#Max) => `max` | #ToFloat(#Max) => `max`
| ToFloat(#Stdev) => `stdev` | #ToFloat(#Stdev) => `stdev`
| ToFloat(#Variance) => `variance` | #ToFloat(#Variance) => `variance`
| ToFloat(#Mode) => `mode` | #ToFloat(#Mode) => `mode`
| ToFloat(#Pdf(r)) => `pdf(${E.Float.toFixed(r)})` | #ToFloat(#Pdf(r)) => `pdf(${E.Float.toFixed(r)})`
| ToFloat(#Sample) => `sample` | #ToFloat(#Sample) => `sample`
| ToFloat(#IntegralSum) => `integralSum` | #ToFloat(#IntegralSum) => `integralSum`
| ToScore(KLDivergence(_)) => `klDivergence` | #ToDist(Normalize) => `normalize`
| ToScore(LogScore(x, _)) => `logScore against ${E.Float.toFixed(x)}` | #ToDist(ToPointSet) => `toPointSet`
| ToDist(Normalize) => `normalize` | #ToDist(ToSampleSet(r)) => `toSampleSet(${E.I.toString(r)})`
| ToDist(ToPointSet) => `toPointSet` | #ToDist(Truncate(_, _)) => `truncate`
| ToDist(ToSampleSet(r)) => `toSampleSet(${E.I.toString(r)})` | #ToDist(Inspect) => `inspect`
| ToDist(Truncate(_, _)) => `truncate` | #ToDist(Scale(#Power, r)) => `scalePower(${E.Float.toFixed(r)})`
| ToDist(Inspect) => `inspect` | #ToDist(Scale(#Multiply, r)) => `scaleMultiply(${E.Float.toFixed(r)})`
| ToDist(Scale(#Power, r)) => `scalePower(${E.Float.toFixed(r)})` | #ToDist(Scale(#Logarithm, r)) => `scaleLog(${E.Float.toFixed(r)})`
| ToDist(Scale(#Multiply, r)) => `scaleMultiply(${E.Float.toFixed(r)})` | #ToDist(Scale(#LogarithmWithThreshold(eps), r)) =>
| ToDist(Scale(#Logarithm, r)) => `scaleLog(${E.Float.toFixed(r)})`
| ToDist(Scale(#LogarithmWithThreshold(eps), r)) =>
`scaleLogWithThreshold(${E.Float.toFixed(r)}, epsilon=${E.Float.toFixed(eps)})` `scaleLogWithThreshold(${E.Float.toFixed(r)}, epsilon=${E.Float.toFixed(eps)})`
| ToString(ToString) => `toString` | #ToString(ToString) => `toString`
| ToString(ToSparkline(n)) => `sparkline(${E.I.toString(n)})` | #ToString(ToSparkline(n)) => `sparkline(${E.I.toString(n)})`
| ToBool(IsNormalized) => `isNormalized` | #ToBool(IsNormalized) => `isNormalized`
| ToDistCombination(Algebraic(_), _, _) => `algebraic` | #ToDistCombination(Algebraic(_), _, _) => `algebraic`
| ToDistCombination(Pointwise, _, _) => `pointwise` | #ToDistCombination(Pointwise, _, _) => `pointwise`
}
let distCallToString = (
distFunction: [
| #ToFloat(toFloat)
| #ToDist(toDist)
| #ToDistCombination(direction, Operation.Algebraic.t, [#Dist(genericDist) | #Float(float)])
| #ToString(toString)
| #ToBool(toBool)
| #ToScore(toScore)
],
): string =>
switch distFunction {
| #ToScore(_) => `logScore`
| #ToFloat(x) => floatCallToString(#ToFloat(x))
| #ToDist(x) => floatCallToString(#ToDist(x))
| #ToString(x) => floatCallToString(#ToString(x))
| #ToBool(x) => floatCallToString(#ToBool(x))
| #ToDistCombination(x, y, z) => floatCallToString(#ToDistCombination(x, y, z))
} }
let toString = (d: genericFunctionCallInfo): string => let toString = (d: genericFunctionCallInfo): string =>
switch d { switch d {
| FromDist(f, _) | FromFloat(f, _) => distCallToString(f) | FromDist(f, _) => distCallToString(f)
| FromFloat(f, _) => floatCallToString(f)
| Mixture(_) => `mixture` | Mixture(_) => `mixture`
| FromSamples(_) => `fromSamples` | FromSamples(_) => `fromSamples`
} }
@ -162,80 +187,93 @@ module Constructors = {
module UsingDists = { module UsingDists = {
@genType @genType
let mean = (dist): t => FromDist(ToFloat(#Mean), dist) let mean = (dist): t => FromDist(#ToFloat(#Mean), dist)
let stdev = (dist): t => FromDist(ToFloat(#Stdev), dist) let stdev = (dist): t => FromDist(#ToFloat(#Stdev), dist)
let variance = (dist): t => FromDist(ToFloat(#Variance), dist) let variance = (dist): t => FromDist(#ToFloat(#Variance), dist)
let sample = (dist): t => FromDist(ToFloat(#Sample), dist) let sample = (dist): t => FromDist(#ToFloat(#Sample), dist)
let cdf = (dist, x): t => FromDist(ToFloat(#Cdf(x)), dist) let cdf = (dist, x): t => FromDist(#ToFloat(#Cdf(x)), dist)
let inv = (dist, x): t => FromDist(ToFloat(#Inv(x)), dist) let inv = (dist, x): t => FromDist(#ToFloat(#Inv(x)), dist)
let pdf = (dist, x): t => FromDist(ToFloat(#Pdf(x)), dist) let pdf = (dist, x): t => FromDist(#ToFloat(#Pdf(x)), dist)
let normalize = (dist): t => FromDist(ToDist(Normalize), dist) let normalize = (dist): t => FromDist(#ToDist(Normalize), dist)
let isNormalized = (dist): t => FromDist(ToBool(IsNormalized), dist) let isNormalized = (dist): t => FromDist(#ToBool(IsNormalized), dist)
let toPointSet = (dist): t => FromDist(ToDist(ToPointSet), dist) let toPointSet = (dist): t => FromDist(#ToDist(ToPointSet), dist)
let toSampleSet = (dist, r): t => FromDist(ToDist(ToSampleSet(r)), dist) let toSampleSet = (dist, r): t => FromDist(#ToDist(ToSampleSet(r)), dist)
let fromSamples = (xs): t => FromSamples(xs) let fromSamples = (xs): t => FromSamples(xs)
let truncate = (dist, left, right): t => FromDist(ToDist(Truncate(left, right)), dist) let truncate = (dist, left, right): t => FromDist(#ToDist(Truncate(left, right)), dist)
let inspect = (dist): t => FromDist(ToDist(Inspect), dist) let inspect = (dist): t => FromDist(#ToDist(Inspect), dist)
let klDivergence = (dist1, dist2): t => FromDist(ToScore(KLDivergence(dist2)), dist1) module LogScore = {
let logScoreWithPointResolution = (~prediction, ~answer, ~prior): t => FromDist( let distEstimateDistAnswer = (estimate, answer): t => FromDist(
ToScore(LogScore(answer, prior)), #ToScore(LogScore(Score_Dist(answer), None)),
prediction, estimate,
) )
let scaleMultiply = (dist, n): t => FromDist(ToDist(Scale(#Multiply, n)), dist) let distEstimateDistAnswerWithPrior = (estimate, answer, prior): t => FromDist(
let scalePower = (dist, n): t => FromDist(ToDist(Scale(#Power, n)), dist) #ToScore(LogScore(Score_Dist(answer), Some(prior))),
let scaleLogarithm = (dist, n): t => FromDist(ToDist(Scale(#Logarithm, n)), dist) estimate,
)
let distEstimateScalarAnswer = (estimate, answer): t => FromDist(
#ToScore(LogScore(Score_Scalar(answer), None)),
estimate,
)
let distEstimateScalarAnswerWithPrior = (estimate, answer, prior): t => FromDist(
#ToScore(LogScore(Score_Scalar(answer), Some(prior))),
estimate,
)
}
let scaleMultiply = (dist, n): t => FromDist(#ToDist(Scale(#Multiply, n)), dist)
let scalePower = (dist, n): t => FromDist(#ToDist(Scale(#Power, n)), dist)
let scaleLogarithm = (dist, n): t => FromDist(#ToDist(Scale(#Logarithm, n)), dist)
let scaleLogarithmWithThreshold = (dist, n, eps): t => FromDist( let scaleLogarithmWithThreshold = (dist, n, eps): t => FromDist(
ToDist(Scale(#LogarithmWithThreshold(eps), n)), #ToDist(Scale(#LogarithmWithThreshold(eps), n)),
dist, dist,
) )
let toString = (dist): t => FromDist(ToString(ToString), dist) let toString = (dist): t => FromDist(#ToString(ToString), dist)
let toSparkline = (dist, n): t => FromDist(ToString(ToSparkline(n)), dist) let toSparkline = (dist, n): t => FromDist(#ToString(ToSparkline(n)), dist)
let algebraicAdd = (dist1, dist2: genericDist): t => FromDist( let algebraicAdd = (dist1, dist2: genericDist): t => FromDist(
ToDistCombination(Algebraic(AsDefault), #Add, #Dist(dist2)), #ToDistCombination(Algebraic(AsDefault), #Add, #Dist(dist2)),
dist1, dist1,
) )
let algebraicMultiply = (dist1, dist2): t => FromDist( let algebraicMultiply = (dist1, dist2): t => FromDist(
ToDistCombination(Algebraic(AsDefault), #Multiply, #Dist(dist2)), #ToDistCombination(Algebraic(AsDefault), #Multiply, #Dist(dist2)),
dist1, dist1,
) )
let algebraicDivide = (dist1, dist2): t => FromDist( let algebraicDivide = (dist1, dist2): t => FromDist(
ToDistCombination(Algebraic(AsDefault), #Divide, #Dist(dist2)), #ToDistCombination(Algebraic(AsDefault), #Divide, #Dist(dist2)),
dist1, dist1,
) )
let algebraicSubtract = (dist1, dist2): t => FromDist( let algebraicSubtract = (dist1, dist2): t => FromDist(
ToDistCombination(Algebraic(AsDefault), #Subtract, #Dist(dist2)), #ToDistCombination(Algebraic(AsDefault), #Subtract, #Dist(dist2)),
dist1, dist1,
) )
let algebraicLogarithm = (dist1, dist2): t => FromDist( let algebraicLogarithm = (dist1, dist2): t => FromDist(
ToDistCombination(Algebraic(AsDefault), #Logarithm, #Dist(dist2)), #ToDistCombination(Algebraic(AsDefault), #Logarithm, #Dist(dist2)),
dist1, dist1,
) )
let algebraicPower = (dist1, dist2): t => FromDist( let algebraicPower = (dist1, dist2): t => FromDist(
ToDistCombination(Algebraic(AsDefault), #Power, #Dist(dist2)), #ToDistCombination(Algebraic(AsDefault), #Power, #Dist(dist2)),
dist1, dist1,
) )
let pointwiseAdd = (dist1, dist2): t => FromDist( let pointwiseAdd = (dist1, dist2): t => FromDist(
ToDistCombination(Pointwise, #Add, #Dist(dist2)), #ToDistCombination(Pointwise, #Add, #Dist(dist2)),
dist1, dist1,
) )
let pointwiseMultiply = (dist1, dist2): t => FromDist( let pointwiseMultiply = (dist1, dist2): t => FromDist(
ToDistCombination(Pointwise, #Multiply, #Dist(dist2)), #ToDistCombination(Pointwise, #Multiply, #Dist(dist2)),
dist1, dist1,
) )
let pointwiseDivide = (dist1, dist2): t => FromDist( let pointwiseDivide = (dist1, dist2): t => FromDist(
ToDistCombination(Pointwise, #Divide, #Dist(dist2)), #ToDistCombination(Pointwise, #Divide, #Dist(dist2)),
dist1, dist1,
) )
let pointwiseSubtract = (dist1, dist2): t => FromDist( let pointwiseSubtract = (dist1, dist2): t => FromDist(
ToDistCombination(Pointwise, #Subtract, #Dist(dist2)), #ToDistCombination(Pointwise, #Subtract, #Dist(dist2)),
dist1, dist1,
) )
let pointwiseLogarithm = (dist1, dist2): t => FromDist( let pointwiseLogarithm = (dist1, dist2): t => FromDist(
ToDistCombination(Pointwise, #Logarithm, #Dist(dist2)), #ToDistCombination(Pointwise, #Logarithm, #Dist(dist2)),
dist1, dist1,
) )
let pointwisePower = (dist1, dist2): t => FromDist( let pointwisePower = (dist1, dist2): t => FromDist(
ToDistCombination(Pointwise, #Power, #Dist(dist2)), #ToDistCombination(Pointwise, #Power, #Dist(dist2)),
dist1, dist1,
) )
} }

View File

@ -6,6 +6,11 @@ type toSampleSetFn = t => result<SampleSetDist.t, error>
type scaleMultiplyFn = (t, float) => result<t, error> type scaleMultiplyFn = (t, float) => result<t, error>
type pointwiseAddFn = (t, t) => result<t, error> type pointwiseAddFn = (t, t) => result<t, error>
type env = {
sampleCount: int,
xyPointLength: int,
}
let isPointSet = (t: t) => let isPointSet = (t: t) =>
switch t { switch t {
| PointSet(_) => true | PointSet(_) => true
@ -61,46 +66,6 @@ let integralEndY = (t: t): float =>
let isNormalized = (t: t): bool => Js.Math.abs_float(integralEndY(t) -. 1.0) < 1e-7 let isNormalized = (t: t): bool => Js.Math.abs_float(integralEndY(t) -. 1.0) < 1e-7
module Score = {
let klDivergence = (prediction, answer, ~toPointSetFn: toPointSetFn): result<float, error> => {
let pointSets = E.R.merge(toPointSetFn(prediction), toPointSetFn(answer))
pointSets |> E.R2.bind(((predi, ans)) =>
PointSetDist.T.klDivergence(predi, ans)->E.R2.errMap(x => DistributionTypes.OperationError(x))
)
}
let logScoreWithPointResolution = (
~prediction: DistributionTypes.genericDist,
~answer: float,
~prior: option<DistributionTypes.genericDist>,
~toPointSetFn: toPointSetFn,
): result<float, error> => {
switch prior {
| Some(prior') =>
E.R.merge(toPointSetFn(prior'), toPointSetFn(prediction))->E.R.bind(((
prior'',
prediction'',
)) =>
PointSetDist.T.logScoreWithPointResolution(
~prediction=prediction'',
~answer,
~prior=prior''->Some,
)->E.R2.errMap(x => DistributionTypes.OperationError(x))
)
| None =>
prediction
->toPointSetFn
->E.R.bind(x =>
PointSetDist.T.logScoreWithPointResolution(
~prediction=x,
~answer,
~prior=None,
)->E.R2.errMap(x => DistributionTypes.OperationError(x))
)
}
}
}
let toFloatOperation = ( let toFloatOperation = (
t, t,
~toPointSetFn: toPointSetFn, ~toPointSetFn: toPointSetFn,
@ -171,6 +136,70 @@ let toPointSet = (
} }
} }
module Score = {
type genericDistOrScalar = DistributionTypes.DistributionOperation.genericDistOrScalar
let argsMake = (~esti: t, ~answ: genericDistOrScalar, ~prior: option<t>, ~env: env): result<
PointSetDist_Scoring.scoreArgs,
error,
> => {
let toPointSetFn = t =>
toPointSet(
t,
~xyPointLength=env.xyPointLength,
~sampleCount=env.sampleCount,
~xSelection=#ByWeight,
(),
)
let prior': option<result<PointSetTypes.pointSetDist, error>> = switch prior {
| None => None
| Some(d) => toPointSetFn(d)->Some
}
let twoDists = (~toPointSetFn, esti': t, answ': t): result<
(PointSetTypes.pointSetDist, PointSetTypes.pointSetDist),
error,
> => E.R.merge(toPointSetFn(esti'), toPointSetFn(answ'))
switch (esti, answ, prior') {
| (esti', Score_Dist(answ'), None) =>
twoDists(~toPointSetFn, esti', answ')->E.R2.fmap(((esti'', answ'')) =>
{estimate: esti'', answer: answ'', prior: None}->PointSetDist_Scoring.DistAnswer
)
| (esti', Score_Dist(answ'), Some(Ok(prior''))) =>
twoDists(~toPointSetFn, esti', answ')->E.R2.fmap(((esti'', answ'')) =>
{
estimate: esti'',
answer: answ'',
prior: Some(prior''),
}->PointSetDist_Scoring.DistAnswer
)
| (esti', Score_Scalar(answ'), None) =>
toPointSetFn(esti')->E.R2.fmap(esti'' =>
{
estimate: esti'',
answer: answ',
prior: None,
}->PointSetDist_Scoring.ScalarAnswer
)
| (esti', Score_Scalar(answ'), Some(Ok(prior''))) =>
toPointSetFn(esti')->E.R2.fmap(esti'' =>
{
estimate: esti'',
answer: answ',
prior: Some(prior''),
}->PointSetDist_Scoring.ScalarAnswer
)
| (_, _, Some(Error(err))) => err->Error
}
}
let logScore = (~estimate: t, ~answer: genericDistOrScalar, ~prior: option<t>, ~env: env): result<
float,
error,
> =>
argsMake(~esti=estimate, ~answ=answer, ~prior, ~env)->E.R.bind(x =>
x->PointSetDist.logScore->E.R2.errMap(y => DistributionTypes.OperationError(y))
)
}
/* /*
PointSetDist.toSparkline calls "downsampleEquallyOverX", which downsamples it to n=bucketCount. PointSetDist.toSparkline calls "downsampleEquallyOverX", which downsamples it to n=bucketCount.
It first needs a pointSetDist, so we convert to a pointSetDist. In this process we want the It first needs a pointSetDist, so we convert to a pointSetDist. In this process we want the

View File

@ -5,6 +5,9 @@ type toSampleSetFn = t => result<SampleSetDist.t, error>
type scaleMultiplyFn = (t, float) => result<t, error> type scaleMultiplyFn = (t, float) => result<t, error>
type pointwiseAddFn = (t, t) => result<t, error> type pointwiseAddFn = (t, t) => result<t, error>
@genType
type env = {sampleCount: int, xyPointLength: int}
let sampleN: (t, int) => array<float> let sampleN: (t, int) => array<float>
let sample: t => float let sample: t => float
@ -25,12 +28,11 @@ let toFloatOperation: (
) => result<float, error> ) => result<float, error>
module Score: { module Score: {
let klDivergence: (t, t, ~toPointSetFn: toPointSetFn) => result<float, error> let logScore: (
let logScoreWithPointResolution: ( ~estimate: t,
~prediction: t, ~answer: DistributionTypes.DistributionOperation.genericDistOrScalar,
~answer: float,
~prior: option<t>, ~prior: option<t>,
~toPointSetFn: toPointSetFn, ~env: env,
) => result<float, error> ) => result<float, error>
} }

View File

@ -120,7 +120,7 @@ let combinePointwise = (
let interpolator = XYShape.XtoY.continuousInterpolator(t1.interpolation, extrapolation) let interpolator = XYShape.XtoY.continuousInterpolator(t1.interpolation, extrapolation)
combiner(fn, interpolator, t1.xyShape, t2.xyShape)->E.R2.fmap(x => combiner(interpolator, fn, t1.xyShape, t2.xyShape)->E.R2.fmap(x =>
make(~integralSumCache=combinedIntegralSum, x) make(~integralSumCache=combinedIntegralSum, x)
) )
} }
@ -270,20 +270,6 @@ module T = Dist({
} }
let variance = (t: t): float => let variance = (t: t): float =>
XYShape.Analysis.getVarianceDangerously(t, mean, Analysis.getMeanOfSquares) XYShape.Analysis.getVarianceDangerously(t, mean, Analysis.getMeanOfSquares)
let klDivergence = (prediction: t, answer: t) => {
let newShape = XYShape.PointwiseCombination.combineAlongSupportOfSecondArgument(
PointSetDist_Scoring.KLDivergence.integrand,
prediction.xyShape,
answer.xyShape,
)
newShape->E.R2.fmap(x => x->make->integralEndY)
}
let logScoreWithPointResolution = (~prediction: t, ~answer: float, ~prior: option<t>) => {
let priorPdf = prior->E.O2.fmap((shape, x) => XYShape.XtoY.linear(x, shape.xyShape))
let predictionPdf = x => XYShape.XtoY.linear(x, prediction.xyShape)
PointSetDist_Scoring.LogScoreWithPointResolution.score(~priorPdf, ~predictionPdf, ~answer)
}
}) })
let isNormalized = (t: t): bool => { let isNormalized = (t: t): bool => {

View File

@ -49,7 +49,7 @@ let combinePointwise = (
// TODO: does it ever make sense to pointwise combine the integrals here? // TODO: does it ever make sense to pointwise combine the integrals here?
// It could be done for pointwise additions, but is that ever needed? // It could be done for pointwise additions, but is that ever needed?
combiner(fn, XYShape.XtoY.discreteInterpolator, t1.xyShape, t2.xyShape)->E.R2.fmap(make) combiner(XYShape.XtoY.discreteInterpolator, fn, t1.xyShape, t2.xyShape)->E.R2.fmap(make)
} }
let reduce = ( let reduce = (
@ -222,15 +222,4 @@ module T = Dist({
let getMeanOfSquares = t => t |> shapeMap(XYShape.T.square) |> mean let getMeanOfSquares = t => t |> shapeMap(XYShape.T.square) |> mean
XYShape.Analysis.getVarianceDangerously(t, mean, getMeanOfSquares) XYShape.Analysis.getVarianceDangerously(t, mean, getMeanOfSquares)
} }
let klDivergence = (prediction: t, answer: t) => {
combinePointwise(
~fn=PointSetDist_Scoring.KLDivergence.integrand,
prediction,
answer,
)->E.R2.fmap(integralEndY)
}
let logScoreWithPointResolution = (~prediction: t, ~answer: float, ~prior: option<t>) => {
Error(Operation.NotYetImplemented)
}
}) })

View File

@ -33,12 +33,6 @@ module type dist = {
let mean: t => float let mean: t => float
let variance: t => float let variance: t => float
let klDivergence: (t, t) => result<float, Operation.Error.t>
let logScoreWithPointResolution: (
~prediction: t,
~answer: float,
~prior: option<t>,
) => result<float, Operation.Error.t>
} }
module Dist = (T: dist) => { module Dist = (T: dist) => {
@ -61,9 +55,6 @@ module Dist = (T: dist) => {
let mean = T.mean let mean = T.mean
let variance = T.variance let variance = T.variance
let integralEndY = T.integralEndY let integralEndY = T.integralEndY
let klDivergence = T.klDivergence
let logScoreWithPointResolution = T.logScoreWithPointResolution
let updateIntegralCache = T.updateIntegralCache let updateIntegralCache = T.updateIntegralCache
module Integral = { module Integral = {

View File

@ -302,15 +302,6 @@ module T = Dist({
| _ => XYShape.Analysis.getVarianceDangerously(t, mean, getMeanOfSquares) | _ => XYShape.Analysis.getVarianceDangerously(t, mean, getMeanOfSquares)
} }
} }
let klDivergence = (prediction: t, answer: t) => {
let klDiscretePart = Discrete.T.klDivergence(prediction.discrete, answer.discrete)
let klContinuousPart = Continuous.T.klDivergence(prediction.continuous, answer.continuous)
E.R.merge(klDiscretePart, klContinuousPart)->E.R2.fmap(t => fst(t) +. snd(t))
}
let logScoreWithPointResolution = (~prediction: t, ~answer: float, ~prior: option<t>) => {
Error(Operation.NotYetImplemented)
}
}) })
let combineAlgebraically = (op: Operation.convolutionOperation, t1: t, t2: t): t => { let combineAlgebraically = (op: Operation.convolutionOperation, t1: t, t2: t): t => {

View File

@ -66,6 +66,7 @@ let combineAlgebraically = (op: Operation.convolutionOperation, t1: t, t2: t): t
} }
let combinePointwise = ( let combinePointwise = (
~combiner=XYShape.PointwiseCombination.combine,
~integralSumCachesFn: (float, float) => option<float>=(_, _) => None, ~integralSumCachesFn: (float, float) => option<float>=(_, _) => None,
~integralCachesFn: ( ~integralCachesFn: (
PointSetTypes.continuousShape, PointSetTypes.continuousShape,
@ -78,6 +79,7 @@ let combinePointwise = (
switch (t1, t2) { switch (t1, t2) {
| (Continuous(m1), Continuous(m2)) => | (Continuous(m1), Continuous(m2)) =>
Continuous.combinePointwise( Continuous.combinePointwise(
~combiner,
~integralSumCachesFn, ~integralSumCachesFn,
fn, fn,
m1, m1,
@ -85,6 +87,7 @@ let combinePointwise = (
)->E.R2.fmap(x => PointSetTypes.Continuous(x)) )->E.R2.fmap(x => PointSetTypes.Continuous(x))
| (Discrete(m1), Discrete(m2)) => | (Discrete(m1), Discrete(m2)) =>
Discrete.combinePointwise( Discrete.combinePointwise(
~combiner,
~integralSumCachesFn, ~integralSumCachesFn,
~fn, ~fn,
m1, m1,
@ -195,25 +198,16 @@ module T = Dist({
| Discrete(m) => Discrete.T.variance(m) | Discrete(m) => Discrete.T.variance(m)
| Continuous(m) => Continuous.T.variance(m) | Continuous(m) => Continuous.T.variance(m)
} }
let klDivergence = (prediction: t, answer: t) =>
switch (prediction, answer) {
| (Continuous(t1), Continuous(t2)) => Continuous.T.klDivergence(t1, t2)
| (Discrete(t1), Discrete(t2)) => Discrete.T.klDivergence(t1, t2)
| (m1, m2) => Mixed.T.klDivergence(m1->toMixed, m2->toMixed)
}
let logScoreWithPointResolution = (~prediction: t, ~answer: float, ~prior: option<t>) => {
switch (prior, prediction) {
| (Some(Continuous(t1)), Continuous(t2)) =>
Continuous.T.logScoreWithPointResolution(~prediction=t2, ~answer, ~prior=t1->Some)
| (None, Continuous(t2)) =>
Continuous.T.logScoreWithPointResolution(~prediction=t2, ~answer, ~prior=None)
| _ => Error(Operation.NotYetImplemented)
}
}
}) })
let logScore = (args: PointSetDist_Scoring.scoreArgs): result<float, Operation.Error.t> =>
PointSetDist_Scoring.logScore(
args,
~combineFn=combinePointwise,
~integrateFn=T.Integral.sum,
~toMixedFn=toMixed,
)
let pdf = (f: float, t: t) => { let pdf = (f: float, t: t) => {
let mixedPoint: PointSetTypes.mixedPoint = T.xToY(f, t) let mixedPoint: PointSetTypes.mixedPoint = T.xToY(f, t)
mixedPoint.continuous +. mixedPoint.discrete mixedPoint.continuous +. mixedPoint.discrete

View File

@ -1,46 +1,149 @@
module KLDivergence = { type pointSetDist = PointSetTypes.pointSetDist
let logFn = Js.Math.log // base e
let integrand = (predictionElement: float, answerElement: float): result< type scalar = float
type score = float
type abstractScoreArgs<'a, 'b> = {estimate: 'a, answer: 'b, prior: option<'a>}
type scoreArgs =
| DistAnswer(abstractScoreArgs<pointSetDist, pointSetDist>)
| ScalarAnswer(abstractScoreArgs<pointSetDist, scalar>)
let logFn = Js.Math.log // base e
let minusScaledLogOfQuotient = (~esti, ~answ): result<float, Operation.Error.t> => {
let quot = esti /. answ
quot < 0.0 ? Error(Operation.ComplexNumberError) : Ok(-.answ *. logFn(quot))
}
module WithDistAnswer = {
// The Kullback-Leibler divergence
let integrand = (estimateElement: float, answerElement: float): result<
float, float,
Operation.Error.t, Operation.Error.t,
> => > =>
// We decided that negative infinity, not an error at answerElement = 0.0, is a desirable value. // We decided that 0.0, not an error at answerElement = 0.0, is a desirable value.
if answerElement == 0.0 { if answerElement == 0.0 {
Ok(0.0) Ok(0.0)
} else if predictionElement == 0.0 { } else if estimateElement == 0.0 {
Ok(infinity) Ok(infinity)
} else { } else {
let quot = predictionElement /. answerElement minusScaledLogOfQuotient(~esti=estimateElement, ~answ=answerElement)
quot < 0.0 ? Error(Operation.ComplexNumberError) : Ok(-.answerElement *. logFn(quot))
} }
}
module LogScoreWithPointResolution = { let sum = (
let logFn = Js.Math.log ~estimate: pointSetDist,
let score = ( ~answer: pointSetDist,
~priorPdf: option<float => float>, ~combineFn,
~predictionPdf: float => float, ~integrateFn,
~answer: float, ~toMixedFn,
): result<float, Operation.Error.t> => { ): result<score, Operation.Error.t> => {
let numerator = answer->predictionPdf let combineAndIntegrate = (estimate, answer) =>
if numerator < 0.0 { combineFn(integrand, estimate, answer)->E.R2.fmap(integrateFn)
Operation.PdfInvalidError->Error
} else if numerator == 0.0 { let getMixedSums = (estimate: pointSetDist, answer: pointSetDist) => {
infinity->Ok let esti = estimate->toMixedFn
} else { let answ = answer->toMixedFn
-.( switch (
switch priorPdf { Mixed.T.toContinuous(esti),
| None => numerator->logFn Mixed.T.toDiscrete(esti),
| Some(f) => { Mixed.T.toContinuous(answ),
let priorDensityOfAnswer = f(answer) Mixed.T.toDiscrete(answ),
if priorDensityOfAnswer == 0.0 { ) {
neg_infinity | (
} else { Some(estiContinuousPart),
(numerator /. priorDensityOfAnswer)->logFn Some(estiDiscretePart),
} Some(answContinuousPart),
} Some(answDiscretePart),
} ) =>
)->Ok E.R.merge(
combineAndIntegrate(
PointSetTypes.Discrete(estiDiscretePart),
PointSetTypes.Discrete(answDiscretePart),
),
combineAndIntegrate(Continuous(estiContinuousPart), Continuous(answContinuousPart)),
)
| (_, _, _, _) => `unreachable state`->Operation.Other->Error
}
}
switch (estimate, answer) {
| (Continuous(_), Continuous(_))
| (Discrete(_), Discrete(_)) =>
combineAndIntegrate(estimate, answer)
| (_, _) =>
getMixedSums(estimate, answer)->E.R2.fmap(((discretePart, continuousPart)) =>
discretePart +. continuousPart
)
} }
} }
let sumWithPrior = (
~estimate: pointSetDist,
~answer: pointSetDist,
~prior: pointSetDist,
~combineFn,
~integrateFn,
~toMixedFn,
): result<score, Operation.Error.t> => {
let kl1 = sum(~estimate, ~answer, ~combineFn, ~integrateFn, ~toMixedFn)
let kl2 = sum(~estimate=prior, ~answer, ~combineFn, ~integrateFn, ~toMixedFn)
E.R.merge(kl1, kl2)->E.R2.fmap(((kl1', kl2')) => kl1' -. kl2')
}
} }
module WithScalarAnswer = {
let sum = (mp: PointSetTypes.MixedPoint.t): float => mp.continuous +. mp.discrete
let score = (~estimate: pointSetDist, ~answer: scalar): result<score, Operation.Error.t> => {
let _score = (~estimatePdf: float => option<float>, ~answer: float): result<
score,
Operation.Error.t,
> => {
let density = answer->estimatePdf
switch density {
| None => Operation.PdfInvalidError->Error
| Some(density') =>
if density' < 0.0 {
Operation.PdfInvalidError->Error
} else if density' == 0.0 {
infinity->Ok
} else {
density'->logFn->(x => -.x)->Ok
}
}
}
let estimatePdf = x =>
switch estimate {
| Continuous(esti) => Continuous.T.xToY(x, esti)->sum->Some
| Discrete(esti) => Discrete.T.xToY(x, esti)->sum->Some
| Mixed(_) => None
}
_score(~estimatePdf, ~answer)
}
let scoreWithPrior = (~estimate: pointSetDist, ~answer: scalar, ~prior: pointSetDist): result<
score,
Operation.Error.t,
> => {
E.R.merge(score(~estimate, ~answer), score(~estimate=prior, ~answer))->E.R2.fmap(((s1, s2)) =>
s1 -. s2
)
}
}
let twoGenericDistsToTwoPointSetDists = (~toPointSetFn, estimate, answer): result<
(pointSetDist, pointSetDist),
'e,
> => E.R.merge(toPointSetFn(estimate, ()), toPointSetFn(answer, ()))
let logScore = (args: scoreArgs, ~combineFn, ~integrateFn, ~toMixedFn): result<
score,
Operation.Error.t,
> =>
switch args {
| DistAnswer({estimate, answer, prior: None}) =>
WithDistAnswer.sum(~estimate, ~answer, ~integrateFn, ~combineFn, ~toMixedFn)
| DistAnswer({estimate, answer, prior: Some(prior)}) =>
WithDistAnswer.sumWithPrior(~estimate, ~answer, ~prior, ~integrateFn, ~combineFn, ~toMixedFn)
| ScalarAnswer({estimate, answer, prior: None}) => WithScalarAnswer.score(~estimate, ~answer)
| ScalarAnswer({estimate, answer, prior: Some(prior)}) =>
WithScalarAnswer.scoreWithPrior(~estimate, ~answer, ~prior)
}

View File

@ -8,6 +8,7 @@ type rec frType =
| FRTypeNumber | FRTypeNumber
| FRTypeNumeric | FRTypeNumeric
| FRTypeDistOrNumber | FRTypeDistOrNumber
| FRTypeDist
| FRTypeLambda | FRTypeLambda
| FRTypeRecord(frTypeRecord) | FRTypeRecord(frTypeRecord)
| FRTypeDict(frType) | FRTypeDict(frType)
@ -41,7 +42,7 @@ and frValueDistOrNumber = FRValueNumber(float) | FRValueDist(DistributionTypes.g
type fnDefinition = { type fnDefinition = {
name: string, name: string,
inputs: array<frType>, inputs: array<frType>,
run: (array<frValue>, DistributionOperation.env) => result<internalExpressionValue, string>, run: (array<frValue>, GenericDist.env) => result<internalExpressionValue, string>,
} }
type function = { type function = {
@ -60,6 +61,7 @@ module FRType = {
switch t { switch t {
| FRTypeNumber => "number" | FRTypeNumber => "number"
| FRTypeNumeric => "numeric" | FRTypeNumeric => "numeric"
| FRTypeDist => "distribution"
| FRTypeDistOrNumber => "distribution|number" | FRTypeDistOrNumber => "distribution|number"
| FRTypeRecord(r) => { | FRTypeRecord(r) => {
let input = ((name, frType): frTypeRecordParam) => `${name}: ${toString(frType)}` let input = ((name, frType): frTypeRecordParam) => `${name}: ${toString(frType)}`
@ -98,6 +100,7 @@ module FRType = {
| (FRTypeDistOrNumber, IEvDistribution(Symbolic(#Float(f)))) => | (FRTypeDistOrNumber, IEvDistribution(Symbolic(#Float(f)))) =>
Some(FRValueDistOrNumber(FRValueNumber(f))) Some(FRValueDistOrNumber(FRValueNumber(f)))
| (FRTypeDistOrNumber, IEvDistribution(f)) => Some(FRValueDistOrNumber(FRValueDist(f))) | (FRTypeDistOrNumber, IEvDistribution(f)) => Some(FRValueDistOrNumber(FRValueDist(f)))
| (FRTypeDist, IEvDistribution(f)) => Some(FRValueDist(f))
| (FRTypeNumeric, IEvNumber(f)) => Some(FRValueNumber(f)) | (FRTypeNumeric, IEvNumber(f)) => Some(FRValueNumber(f))
| (FRTypeNumeric, IEvDistribution(Symbolic(#Float(f)))) => Some(FRValueNumber(f)) | (FRTypeNumeric, IEvDistribution(Symbolic(#Float(f)))) => Some(FRValueNumber(f))
| (FRTypeLambda, IEvLambda(f)) => Some(FRValueLambda(f)) | (FRTypeLambda, IEvLambda(f)) => Some(FRValueLambda(f))
@ -319,7 +322,7 @@ module FnDefinition = {
t.name ++ `(${inputs})` t.name ++ `(${inputs})`
} }
let run = (t: t, args: array<internalExpressionValue>, env: DistributionOperation.env) => { let run = (t: t, args: array<internalExpressionValue>, env: GenericDist.env) => {
let argValues = FRType.matchWithExpressionValueArray(t.inputs, args) let argValues = FRType.matchWithExpressionValueArray(t.inputs, args)
switch argValues { switch argValues {
| Some(values) => t.run(values, env) | Some(values) => t.run(values, env)
@ -374,7 +377,7 @@ module Registry = {
~registry: registry, ~registry: registry,
~fnName: string, ~fnName: string,
~args: array<internalExpressionValue>, ~args: array<internalExpressionValue>,
~env: DistributionOperation.env, ~env: GenericDist.env,
) => { ) => {
let matchToDef = m => Matcher.Registry.matchToDef(registry, m) let matchToDef = m => Matcher.Registry.matchToDef(registry, m)
//Js.log(toSimple(registry)) //Js.log(toSimple(registry))

View File

@ -27,6 +27,12 @@ module Prepare = {
| _ => Error(impossibleError) | _ => Error(impossibleError)
} }
let threeArgs = (inputs: ts): result<ts, err> =>
switch inputs {
| [FRValueRecord([(_, n1), (_, n2), (_, n3)])] => Ok([n1, n2, n3])
| _ => Error(impossibleError)
}
let toArgs = (inputs: ts): result<ts, err> => let toArgs = (inputs: ts): result<ts, err> =>
switch inputs { switch inputs {
| [FRValueRecord(args)] => args->E.A2.fmap(((_, b)) => b)->Ok | [FRValueRecord(args)] => args->E.A2.fmap(((_, b)) => b)->Ok
@ -57,6 +63,16 @@ module Prepare = {
} }
} }
let twoDist = (values: ts): result<
(DistributionTypes.genericDist, DistributionTypes.genericDist),
err,
> => {
switch values {
| [FRValueDist(a1), FRValueDist(a2)] => Ok(a1, a2)
| _ => Error(impossibleError)
}
}
let twoNumbers = (values: ts): result<(float, float), err> => { let twoNumbers = (values: ts): result<(float, float), err> => {
switch values { switch values {
| [FRValueNumber(a1), FRValueNumber(a2)] => Ok(a1, a2) | [FRValueNumber(a1), FRValueNumber(a2)] => Ok(a1, a2)
@ -81,6 +97,11 @@ module Prepare = {
module Record = { module Record = {
let twoDistOrNumber = (values: ts): result<(frValueDistOrNumber, frValueDistOrNumber), err> => let twoDistOrNumber = (values: ts): result<(frValueDistOrNumber, frValueDistOrNumber), err> =>
values->ToValueArray.Record.twoArgs->E.R.bind(twoDistOrNumber) values->ToValueArray.Record.twoArgs->E.R.bind(twoDistOrNumber)
let twoDist = (values: ts): result<
(DistributionTypes.genericDist, DistributionTypes.genericDist),
err,
> => values->ToValueArray.Record.twoArgs->E.R.bind(twoDist)
} }
} }
@ -128,8 +149,7 @@ module Prepare = {
module Process = { module Process = {
module DistOrNumberToDist = { module DistOrNumberToDist = {
module Helpers = { module Helpers = {
let toSampleSet = (r, env: DistributionOperation.env) => let toSampleSet = (r, env: GenericDist.env) => GenericDist.toSampleSetDist(r, env.sampleCount)
GenericDist.toSampleSetDist(r, env.sampleCount)
let mapFnResult = r => let mapFnResult = r =>
switch r { switch r {
@ -166,7 +186,7 @@ module Process = {
let oneValue = ( let oneValue = (
~fn: float => result<DistributionTypes.genericDist, string>, ~fn: float => result<DistributionTypes.genericDist, string>,
~value: frValueDistOrNumber, ~value: frValueDistOrNumber,
~env: DistributionOperation.env, ~env: GenericDist.env,
): result<DistributionTypes.genericDist, string> => { ): result<DistributionTypes.genericDist, string> => {
switch value { switch value {
| FRValueNumber(a1) => fn(a1) | FRValueNumber(a1) => fn(a1)
@ -179,7 +199,7 @@ module Process = {
let twoValues = ( let twoValues = (
~fn: ((float, float)) => result<DistributionTypes.genericDist, string>, ~fn: ((float, float)) => result<DistributionTypes.genericDist, string>,
~values: (frValueDistOrNumber, frValueDistOrNumber), ~values: (frValueDistOrNumber, frValueDistOrNumber),
~env: DistributionOperation.env, ~env: GenericDist.env,
): result<DistributionTypes.genericDist, string> => { ): result<DistributionTypes.genericDist, string> => {
switch values { switch values {
| (FRValueNumber(a1), FRValueNumber(a2)) => fn((a1, a2)) | (FRValueNumber(a1), FRValueNumber(a2)) => fn((a1, a2))

View File

@ -49,7 +49,7 @@ let inputsTodist = (inputs: array<FunctionRegistry_Core.frValue>, makeDist) => {
expressionValue expressionValue
} }
let registry = [ let registryStart = [
Function.make( Function.make(
~name="toContinuousPointSet", ~name="toContinuousPointSet",
~definitions=[ ~definitions=[
@ -510,3 +510,67 @@ to(5,10)
(), (),
), ),
] ]
let runScoring = (estimate, answer, prior, env) => {
GenericDist.Score.logScore(~estimate, ~answer, ~prior, ~env)
->E.R2.fmap(FunctionRegistry_Helpers.Wrappers.evNumber)
->E.R2.errMap(DistributionTypes.Error.toString)
}
let scoreFunctions = [
Function.make(
~name="Score",
~definitions=[
FnDefinition.make(
~name="logScore",
~inputs=[
FRTypeRecord([
("estimate", FRTypeDist),
("answer", FRTypeDistOrNumber),
("prior", FRTypeDist),
]),
],
~run=(inputs, env) => {
switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.threeArgs(inputs) {
| Ok([FRValueDist(estimate), FRValueDistOrNumber(FRValueDist(d)), FRValueDist(prior)]) =>
runScoring(estimate, Score_Dist(d), Some(prior), env)
| Ok([
FRValueDist(estimate),
FRValueDistOrNumber(FRValueNumber(d)),
FRValueDist(prior),
]) =>
runScoring(estimate, Score_Scalar(d), Some(prior), env)
| Error(e) => Error(e)
| _ => Error(FunctionRegistry_Helpers.impossibleError)
}
},
),
FnDefinition.make(
~name="logScore",
~inputs=[FRTypeRecord([("estimate", FRTypeDist), ("answer", FRTypeDistOrNumber)])],
~run=(inputs, env) => {
switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.twoArgs(inputs) {
| Ok([FRValueDist(estimate), FRValueDistOrNumber(FRValueDist(d))]) =>
runScoring(estimate, Score_Dist(d), None, env)
| Ok([FRValueDist(estimate), FRValueDistOrNumber(FRValueNumber(d))]) =>
runScoring(estimate, Score_Scalar(d), None, env)
| Error(e) => Error(e)
| _ => Error(FunctionRegistry_Helpers.impossibleError)
}
},
),
FnDefinition.make(~name="klDivergence", ~inputs=[FRTypeDist, FRTypeDist], ~run=(
inputs,
env,
) => {
switch inputs {
| [FRValueDist(estimate), FRValueDist(d)] => runScoring(estimate, Score_Dist(d), None, env)
| _ => Error(FunctionRegistry_Helpers.impossibleError)
}
}),
],
(),
),
]
let registry = E.A.append(registryStart, scoreFunctions)

View File

@ -1,7 +1,7 @@
module IEV = ReducerInterface_InternalExpressionValue module IEV = ReducerInterface_InternalExpressionValue
type internalExpressionValue = IEV.t type internalExpressionValue = IEV.t
let dispatch = (call: IEV.functionCall, _: DistributionOperation.env): option< let dispatch = (call: IEV.functionCall, _: GenericDist.env): option<
result<internalExpressionValue, QuriSquiggleLang.Reducer_ErrorValue.errorValue>, result<internalExpressionValue, QuriSquiggleLang.Reducer_ErrorValue.errorValue>,
> => { > => {
switch call { switch call {

View File

@ -1,7 +1,7 @@
module IEV = ReducerInterface_InternalExpressionValue module IEV = ReducerInterface_InternalExpressionValue
type internalExpressionValue = IEV.t type internalExpressionValue = IEV.t
let dispatch = (call: IEV.functionCall, _: DistributionOperation.env): option< let dispatch = (call: IEV.functionCall, _: GenericDist.env): option<
result<internalExpressionValue, QuriSquiggleLang.Reducer_ErrorValue.errorValue>, result<internalExpressionValue, QuriSquiggleLang.Reducer_ErrorValue.errorValue>,
> => { > => {
switch call { switch call {

View File

@ -86,7 +86,7 @@ let toStringResult = x =>
} }
@genType @genType
type environment = DistributionOperation.env type environment = GenericDist.env
@genType @genType
let defaultEnvironment: environment = DistributionOperation.defaultEnv let defaultEnvironment: environment = DistributionOperation.defaultEnv

View File

@ -32,50 +32,38 @@ module Helpers = {
let toFloatFn = ( let toFloatFn = (
fnCall: DistributionTypes.DistributionOperation.toFloat, fnCall: DistributionTypes.DistributionOperation.toFloat,
dist: DistributionTypes.genericDist, dist: DistributionTypes.genericDist,
~env: DistributionOperation.env, ~env: GenericDist.env,
) => { ) => {
FromDist(DistributionTypes.DistributionOperation.ToFloat(fnCall), dist) FromDist(#ToFloat(fnCall), dist)->DistributionOperation.run(~env)->Some
->DistributionOperation.run(~env)
->Some
} }
let toStringFn = ( let toStringFn = (
fnCall: DistributionTypes.DistributionOperation.toString, fnCall: DistributionTypes.DistributionOperation.toString,
dist: DistributionTypes.genericDist, dist: DistributionTypes.genericDist,
~env: DistributionOperation.env, ~env: GenericDist.env,
) => { ) => {
FromDist(DistributionTypes.DistributionOperation.ToString(fnCall), dist) FromDist(#ToString(fnCall), dist)->DistributionOperation.run(~env)->Some
->DistributionOperation.run(~env)
->Some
} }
let toBoolFn = ( let toBoolFn = (
fnCall: DistributionTypes.DistributionOperation.toBool, fnCall: DistributionTypes.DistributionOperation.toBool,
dist: DistributionTypes.genericDist, dist: DistributionTypes.genericDist,
~env: DistributionOperation.env, ~env: GenericDist.env,
) => { ) => {
FromDist(DistributionTypes.DistributionOperation.ToBool(fnCall), dist) FromDist(#ToBool(fnCall), dist)->DistributionOperation.run(~env)->Some
->DistributionOperation.run(~env)
->Some
} }
let toDistFn = ( let toDistFn = (
fnCall: DistributionTypes.DistributionOperation.toDist, fnCall: DistributionTypes.DistributionOperation.toDist,
dist, dist,
~env: DistributionOperation.env, ~env: GenericDist.env,
) => { ) => {
FromDist(DistributionTypes.DistributionOperation.ToDist(fnCall), dist) FromDist(#ToDist(fnCall), dist)->DistributionOperation.run(~env)->Some
->DistributionOperation.run(~env)
->Some
} }
let twoDiststoDistFn = (direction, arithmetic, dist1, dist2, ~env: DistributionOperation.env) => { let twoDiststoDistFn = (direction, arithmetic, dist1, dist2, ~env: GenericDist.env) => {
FromDist( FromDist(
DistributionTypes.DistributionOperation.ToDistCombination( #ToDistCombination(direction, arithmeticMap(arithmetic), #Dist(dist2)),
direction,
arithmeticMap(arithmetic),
#Dist(dist2),
),
dist1, dist1,
)->DistributionOperation.run(~env) )->DistributionOperation.run(~env)
} }
@ -109,7 +97,7 @@ module Helpers = {
let mixtureWithGivenWeights = ( let mixtureWithGivenWeights = (
distributions: array<DistributionTypes.genericDist>, distributions: array<DistributionTypes.genericDist>,
weights: array<float>, weights: array<float>,
~env: DistributionOperation.env, ~env: GenericDist.env,
): DistributionOperation.outputType => ): DistributionOperation.outputType =>
E.A.length(distributions) == E.A.length(weights) E.A.length(distributions) == E.A.length(weights)
? Mixture(Belt.Array.zip(distributions, weights))->DistributionOperation.run(~env) ? Mixture(Belt.Array.zip(distributions, weights))->DistributionOperation.run(~env)
@ -119,7 +107,7 @@ module Helpers = {
let mixtureWithDefaultWeights = ( let mixtureWithDefaultWeights = (
distributions: array<DistributionTypes.genericDist>, distributions: array<DistributionTypes.genericDist>,
~env: DistributionOperation.env, ~env: GenericDist.env,
): 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))
@ -128,7 +116,7 @@ module Helpers = {
let mixture = ( let mixture = (
args: array<internalExpressionValue>, args: array<internalExpressionValue>,
~env: DistributionOperation.env, ~env: GenericDist.env,
): DistributionOperation.outputType => { ): DistributionOperation.outputType => {
let error = (err: string): DistributionOperation.outputType => let error = (err: string): DistributionOperation.outputType =>
err->DistributionTypes.ArgumentError->GenDistError err->DistributionTypes.ArgumentError->GenDistError
@ -167,20 +155,6 @@ module Helpers = {
} }
} }
} }
let klDivergenceWithPrior = (
prediction: DistributionTypes.genericDist,
answer: DistributionTypes.genericDist,
prior: DistributionTypes.genericDist,
env: DistributionOperation.env,
) => {
let term1 = DistributionOperation.Constructors.klDivergence(~env, prediction, answer)
let term2 = DistributionOperation.Constructors.klDivergence(~env, prior, answer)
switch E.R.merge(term1, term2)->E.R2.fmap(((a, b)) => a -. b) {
| Ok(x) => x->DistributionOperation.Float->Some
| Error(_) => None
}
}
} }
module SymbolicConstructors = { module SymbolicConstructors = {
@ -199,7 +173,7 @@ module SymbolicConstructors = {
} }
} }
let dispatchToGenericOutput = (call: IEV.functionCall, env: DistributionOperation.env): option< let dispatchToGenericOutput = (call: IEV.functionCall, env: GenericDist.env): option<
DistributionOperation.outputType, DistributionOperation.outputType,
> => { > => {
let (fnName, args) = call let (fnName, args) = call
@ -239,35 +213,6 @@ let dispatchToGenericOutput = (call: IEV.functionCall, env: DistributionOperatio
~env, ~env,
)->Some )->Some
| ("normalize", [IEvDistribution(dist)]) => Helpers.toDistFn(Normalize, dist, ~env) | ("normalize", [IEvDistribution(dist)]) => Helpers.toDistFn(Normalize, dist, ~env)
| ("klDivergence", [IEvDistribution(prediction), IEvDistribution(answer)]) =>
Some(DistributionOperation.run(FromDist(ToScore(KLDivergence(answer)), prediction), ~env))
| (
"klDivergence",
[IEvDistribution(prediction), IEvDistribution(answer), IEvDistribution(prior)],
) =>
Helpers.klDivergenceWithPrior(prediction, answer, prior, env)
| (
"logScoreWithPointAnswer",
[IEvDistribution(prediction), IEvNumber(answer), IEvDistribution(prior)],
)
| (
"logScoreWithPointAnswer",
[
IEvDistribution(prediction),
IEvDistribution(Symbolic(#Float(answer))),
IEvDistribution(prior),
],
) =>
DistributionOperation.run(
FromDist(ToScore(LogScore(answer, prior->Some)), prediction),
~env,
)->Some
| ("logScoreWithPointAnswer", [IEvDistribution(prediction), IEvNumber(answer)])
| (
"logScoreWithPointAnswer",
[IEvDistribution(prediction), IEvDistribution(Symbolic(#Float(answer)))],
) =>
DistributionOperation.run(FromDist(ToScore(LogScore(answer, None)), prediction), ~env)->Some
| ("isNormalized", [IEvDistribution(dist)]) => Helpers.toBoolFn(IsNormalized, dist, ~env) | ("isNormalized", [IEvDistribution(dist)]) => Helpers.toBoolFn(IsNormalized, dist, ~env)
| ("toPointSet", [IEvDistribution(dist)]) => Helpers.toDistFn(ToPointSet, dist, ~env) | ("toPointSet", [IEvDistribution(dist)]) => Helpers.toDistFn(ToPointSet, dist, ~env)
| ("scaleLog", [IEvDistribution(dist)]) => | ("scaleLog", [IEvDistribution(dist)]) =>

View File

@ -24,7 +24,7 @@ module ScientificUnit = {
} }
} }
let dispatch = (call: IEV.functionCall, _: DistributionOperation.env): option< let dispatch = (call: IEV.functionCall, _: GenericDist.env): option<
result<internalExpressionValue, QuriSquiggleLang.Reducer_ErrorValue.errorValue>, result<internalExpressionValue, QuriSquiggleLang.Reducer_ErrorValue.errorValue>,
> => { > => {
switch call { switch call {

View File

@ -8,7 +8,7 @@ The below few seem to work fine. In the future there's definitely more work to d
*/ */
@genType @genType
type samplingParams = DistributionOperation.env type samplingParams = GenericDist.env
@genType @genType
type genericDist = DistributionTypes.genericDist type genericDist = DistributionTypes.genericDist

View File

@ -547,6 +547,7 @@ module A = {
let init = Array.init let init = Array.init
let reduce = Belt.Array.reduce let reduce = Belt.Array.reduce
let reducei = Belt.Array.reduceWithIndex let reducei = Belt.Array.reduceWithIndex
let some = Belt.Array.some
let isEmpty = r => length(r) < 1 let isEmpty = r => length(r) < 1
let stableSortBy = Belt.SortArray.stableSortBy let stableSortBy = Belt.SortArray.stableSortBy
let toNoneIfEmpty = r => isEmpty(r) ? None : Some(r) let toNoneIfEmpty = r => isEmpty(r) ? None : Some(r)

View File

@ -327,8 +327,8 @@ module Zipped = {
module PointwiseCombination = { module PointwiseCombination = {
// t1Interpolator and t2Interpolator are functions from XYShape.XtoY, e.g. linearBetweenPointsExtrapolateFlat. // t1Interpolator and t2Interpolator are functions from XYShape.XtoY, e.g. linearBetweenPointsExtrapolateFlat.
let combine: ( let combine: (
(float, float) => result<float, Operation.Error.t>,
interpolator, interpolator,
(float, float) => result<float, Operation.Error.t>,
T.t, T.t,
T.t, T.t,
) => result<T.t, Operation.Error.t> = %raw(` ) => result<T.t, Operation.Error.t> = %raw(`
@ -337,7 +337,7 @@ module PointwiseCombination = {
// and interpolates the value on the other side, thus accumulating xs and ys. // and interpolates the value on the other side, thus accumulating xs and ys.
// This is written in raw JS because this can still be a bottleneck, and using refs for the i and j indices is quite painful. // This is written in raw JS because this can still be a bottleneck, and using refs for the i and j indices is quite painful.
function(fn, interpolator, t1, t2) { function(interpolator, fn, t1, t2) {
let t1n = t1.xs.length; let t1n = t1.xs.length;
let t2n = t2.xs.length; let t2n = t2.xs.length;
let outX = []; let outX = [];
@ -399,11 +399,11 @@ module PointwiseCombination = {
This is from an approach to kl divergence that was ultimately rejected. Leaving it in for now because it may help us factor `combine` out of raw javascript soon. This is from an approach to kl divergence that was ultimately rejected. Leaving it in for now because it may help us factor `combine` out of raw javascript soon.
*/ */
let combineAlongSupportOfSecondArgument0: ( let combineAlongSupportOfSecondArgument0: (
(float, float) => result<float, Operation.Error.t>,
interpolator, interpolator,
(float, float) => result<float, Operation.Error.t>,
T.t, T.t,
T.t, T.t,
) => result<T.t, Operation.Error.t> = (fn, interpolator, t1, t2) => { ) => result<T.t, Operation.Error.t> = (interpolator, fn, t1, t2) => {
let newYs = [] let newYs = []
let newXs = [] let newXs = []
let (l1, l2) = (E.A.length(t1.xs), E.A.length(t2.xs)) let (l1, l2) = (E.A.length(t1.xs), E.A.length(t2.xs))
@ -496,29 +496,9 @@ module PointwiseCombination = {
let newYs = E.A.fmap(x => XtoY.linear(x, t), newXs) let newYs = E.A.fmap(x => XtoY.linear(x, t), newXs)
{xs: newXs, ys: newYs} {xs: newXs, ys: newYs}
} }
// This function is used for klDivergence
let combineAlongSupportOfSecondArgument: (
(float, float) => result<float, Operation.Error.t>,
T.t,
T.t,
) => result<T.t, Operation.Error.t> = (fn, prediction, answer) => {
let combineWithFn = (answerX: float, i: int) => {
let answerY = answer.ys[i]
let predictionY = XtoY.linear(answerX, prediction)
fn(predictionY, answerY)
}
let newYsWithError = Js.Array.mapi((x, i) => combineWithFn(x, i), answer.xs)
let newYsOrError = E.A.R.firstErrorOrOpen(newYsWithError)
let result = switch newYsOrError {
| Ok(a) => Ok({xs: answer.xs, ys: a})
| Error(b) => Error(b)
}
result
}
let addCombine = (interpolator: interpolator, t1: T.t, t2: T.t): T.t => let addCombine = (interpolator: interpolator, t1: T.t, t2: T.t): T.t =>
combine((a, b) => Ok(a +. b), interpolator, t1, t2)->E.R.toExn( combine(interpolator, (a, b) => Ok(a +. b), t1, t2)->E.R.toExn(
"Add operation should never fail", "Add operation should never fail",
_, _,
) )