It compiles

Value: [1e-3 to 4e-1]
This commit is contained in:
Quinn Dougherty 2022-05-25 08:17:45 -04:00
parent 3a56d6fca4
commit f2460a5e12
9 changed files with 258 additions and 128 deletions

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 =
@ -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
@ -193,7 +190,7 @@ describe("combineAlongSupportOfSecondArgument0", () => {
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))))) =>

View File

@ -145,18 +145,9 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => {
Dist(dist) Dist(dist)
} }
| ToDist(Normalize) => dist->GenericDist.normalize->Dist | ToDist(Normalize) => dist->GenericDist.normalize->Dist
| ToScore(KLDivergence(t2)) =>
GenericDist.Score.klDivergence(dist, t2, ~toPointSetFn)
->E.R2.fmap(r => Float(r))
->OutputLocal.fromResult
| ToScore(LogScore(answer, prior)) => | ToScore(LogScore(answer, prior)) =>
GenericDist.Score.logScoreWithPointResolution( GenericDist.Score.logScore(~estimate=Score_Dist(dist), ~answer, ~prior)
~prediction=dist, ->E.R2.fmap(s => Float(s))
~answer,
~prior,
~toPointSetFn,
)
->E.R2.fmap(r => Float(r))
->OutputLocal.fromResult ->OutputLocal.fromResult
| ToBool(IsNormalized) => dist->GenericDist.isNormalized->Bool | ToBool(IsNormalized) => dist->GenericDist.isNormalized->Bool
| ToDist(Truncate(leftCutoff, rightCutoff)) => | ToDist(Truncate(leftCutoff, rightCutoff)) =>
@ -271,13 +262,22 @@ 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 let logScore_DistEstimateDistAnswer = (~env, estimate, answer) =>
let logScoreWithPointResolution = ( C.logScore_DistEstimateDistAnswer(estimate, answer)->run(~env)->toFloatR
~env, let logScore_DistEstimateDistAnswerWithPrior = (~env, estimate, answer, prior) =>
~prediction: DistributionTypes.genericDist, C.logScore_DistEstimateDistAnswerWithPrior(estimate, answer, prior)->run(~env)->toFloatR
~answer: float, let logScore_DistEstimateScalarAnswer = (~env, estimate, answer) =>
~prior: option<DistributionTypes.genericDist>, C.logScore_DistEstimateScalarAnswer(estimate, answer)->run(~env)->toFloatR
) => C.logScoreWithPointResolution(~prediction, ~answer, ~prior)->run(~env)->toFloatR let logScore_DistEstimateScalarAnswerWithPrior = (~env, estimate, answer, prior) =>
C.logScore_DistEstimateScalarAnswerWithPrior(estimate, answer, prior)->run(~env)->toFloatR
let logScore_ScalarEstimateDistAnswer = (~env, estimate, answer) =>
C.logScore_ScalarEstimateDistAnswer(estimate, answer)->run(~env)->toFloatR
let logScore_ScalarEstimateDistAnswerWithPrior = (~env, estimate, answer, prior) =>
C.logScore_ScalarEstimateDistAnswerWithPrior(estimate, answer, prior)->run(~env)->toFloatR
let logScore_ScalarEstimateScalarAnswer = (~env, estimate, answer) =>
C.logScore_ScalarEstimateScalarAnswer(estimate, answer)->run(~env)->toFloatR
let logScore_ScalarEstimateScalarAnswerWithPrior = (~env, estimate, answer, prior) =>
C.logScore_ScalarEstimateScalarAnswerWithPrior(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

@ -61,13 +61,40 @@ module Constructors: {
@genType @genType
let isNormalized: (~env: env, genericDist) => result<bool, error> let isNormalized: (~env: env, genericDist) => result<bool, error>
@genType @genType
let klDivergence: (~env: env, genericDist, genericDist) => result<float, error> let logScore_DistEstimateDistAnswer: (~env: env, genericDist, genericDist) => result<float, error>
@genType @genType
let logScoreWithPointResolution: ( let logScore_DistEstimateDistAnswerWithPrior: (
~env: env, ~env: env,
~prediction: genericDist, genericDist,
~answer: float, genericDist,
~prior: option<genericDist>, DistributionTypes.DistributionOperation.scoreDistOrScalar,
) => result<float, error>
@genType
let logScore_DistEstimateScalarAnswer: (~env: env, genericDist, float) => result<float, error>
@genType
let logScore_DistEstimateScalarAnswerWithPrior: (
~env: env,
genericDist,
float,
DistributionTypes.DistributionOperation.scoreDistOrScalar,
) => result<float, error>
@genType
let logScore_ScalarEstimateDistAnswer: (~env: env, float, genericDist) => result<float, error>
@genType
let logScore_ScalarEstimateDistAnswerWithPrior: (
~env: env,
float,
genericDist,
DistributionTypes.DistributionOperation.scoreDistOrScalar,
) => result<float, error>
@genType
let logScore_ScalarEstimateScalarAnswer: (~env: env, float, float) => result<float, error>
@genType
let logScore_ScalarEstimateScalarAnswerWithPrior: (
~env: env,
float,
float,
DistributionTypes.DistributionOperation.scoreDistOrScalar,
) => result<float, error> ) => result<float, error>
@genType @genType
let toPointSet: (~env: env, genericDist) => result<genericDist, error> let toPointSet: (~env: env, genericDist) => result<genericDist, error>

View File

@ -92,7 +92,9 @@ module DistributionOperation = {
| ToString | ToString
| ToSparkline(int) | ToSparkline(int)
type toScore = KLDivergence(genericDist) | LogScore(float, option<genericDist>) type scoreDistOrScalar = Score_Dist(genericDist) | Score_Scalar(float)
type toScore = LogScore(scoreDistOrScalar, option<scoreDistOrScalar>)
type fromDist = type fromDist =
| ToFloat(toFloat) | ToFloat(toFloat)
@ -120,8 +122,7 @@ module DistributionOperation = {
| 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` | ToScore(_) => `logScore`
| ToScore(LogScore(x, _)) => `logScore against ${E.Float.toFixed(x)}`
| ToDist(Normalize) => `normalize` | ToDist(Normalize) => `normalize`
| ToDist(ToPointSet) => `toPointSet` | ToDist(ToPointSet) => `toPointSet`
| ToDist(ToSampleSet(r)) => `toSampleSet(${E.I.toString(r)})` | ToDist(ToSampleSet(r)) => `toSampleSet(${E.I.toString(r)})`
@ -162,10 +163,37 @@ module Constructors = {
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) let logScore_DistEstimateDistAnswer = (estimate, answer): t => FromDist(
let logScoreWithPointResolution = (~prediction, ~answer, ~prior): t => FromDist( ToScore(LogScore(Score_Dist(answer), None)),
ToScore(LogScore(answer, prior)), estimate,
prediction, )
let logScore_DistEstimateDistAnswerWithPrior = (estimate, answer, prior): t => FromDist(
ToScore(LogScore(Score_Dist(answer), Some(prior))),
estimate,
)
let logScore_DistEstimateScalarAnswer = (estimate, answer): t => FromDist(
ToScore(LogScore(Score_Scalar(answer), None)),
estimate,
)
let logScore_DistEstimateScalarAnswerWithPrior = (estimate, answer, prior): t => FromDist(
ToScore(LogScore(Score_Scalar(answer), Some(prior))),
estimate,
)
let logScore_ScalarEstimateDistAnswer = (estimate, answer): t => FromFloat(
ToScore(LogScore(Score_Dist(answer), None)),
estimate,
)
let logScore_ScalarEstimateDistAnswerWithPrior = (estimate, answer, prior): t => FromFloat(
ToScore(LogScore(Score_Dist(answer), Some(prior))),
estimate,
)
let logScore_ScalarEstimateScalarAnswer = (estimate, answer): t => FromFloat(
ToScore(LogScore(Score_Scalar(answer), None)),
estimate,
)
let logScore_ScalarEstimateScalarAnswerWithPrior = (estimate, answer, prior): t => FromFloat(
ToScore(LogScore(Score_Scalar(answer), Some(prior))),
estimate,
) )
let scalePower = (dist, n): t => FromDist(ToDist(Scale(#Power, 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 scaleLogarithm = (dist, n): t => FromDist(ToDist(Scale(#Logarithm, n)), dist)

View File

@ -61,46 +61,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,
@ -159,6 +119,125 @@ let toPointSet = (
} }
} }
module Score = {
type scoreDistOrScalar = DistributionTypes.DistributionOperation.scoreDistOrScalar
type pointSet_ScoreDistOrScalar = D(PointSetTypes.pointSetDist) | S(float)
let argsMake = (
~esti: scoreDistOrScalar,
~answ: scoreDistOrScalar,
~prior: option<scoreDistOrScalar>,
): result<PointSetDist_Scoring.scoreArgs, error> => {
let toPointSetFn = toPointSet(
~xyPointLength=MagicNumbers.Environment.defaultXYPointLength,
~sampleCount=MagicNumbers.Environment.defaultSampleCount,
~xSelection=#ByWeight,
)
let prior': option<result<pointSet_ScoreDistOrScalar, error>> = switch prior {
| None => None
| Some(Score_Dist(d)) => toPointSetFn(d, ())->E.R.bind(x => x->D->Ok)->Some
| Some(Score_Scalar(s)) => s->S->Ok->Some
}
let twoDists = (esti': t, answ': t): result<
(PointSetTypes.pointSetDist, PointSetTypes.pointSetDist),
error,
> => E.R.merge(toPointSetFn(esti', ()), toPointSetFn(answ', ()))
switch (esti, answ, prior') {
| (Score_Dist(esti'), Score_Dist(answ'), None) =>
twoDists(esti', answ')->E.R.bind(((esti'', answ'')) =>
{estimate: esti'', answer: answ'', prior: None}
->PointSetDist_Scoring.DistEstimateDistAnswer
->Ok
)
| (Score_Dist(esti'), Score_Dist(answ'), Some(Ok(D(prior'')))) =>
twoDists(esti', answ')->E.R.bind(((esti'', answ'')) =>
{estimate: esti'', answer: answ'', prior: Some(prior'')}
->PointSetDist_Scoring.DistEstimateDistAnswer
->Ok
)
| (Score_Dist(esti'), Score_Scalar(answ'), None) =>
toPointSetFn(esti', ())->E.R.bind(esti'' =>
{estimate: esti'', answer: answ', prior: None}
->PointSetDist_Scoring.DistEstimateScalarAnswer
->Ok
)
| (Score_Dist(esti'), Score_Scalar(answ'), Some(Ok(D(prior'')))) =>
toPointSetFn(esti', ())->E.R.bind(esti'' =>
{estimate: esti'', answer: answ', prior: Some(prior'')}
->PointSetDist_Scoring.DistEstimateScalarAnswer
->Ok
)
| (Score_Scalar(esti'), Score_Dist(answ'), None) =>
toPointSetFn(answ', ())->E.R.bind(answ'' =>
{estimate: esti', answer: answ'', prior: None}
->PointSetDist_Scoring.ScalarEstimateDistAnswer
->Ok
)
| (Score_Scalar(esti'), Score_Dist(answ'), Some(Ok(S(prior'')))) =>
toPointSetFn(answ', ())->E.R.bind(answ'' =>
{estimate: esti', answer: answ'', prior: Some(prior'')}
->PointSetDist_Scoring.ScalarEstimateDistAnswer
->Ok
)
| (Score_Scalar(esti'), Score_Scalar(answ'), None) =>
{estimate: esti', answer: answ', prior: None}
->PointSetDist_Scoring.ScalarEstimateScalarAnswer
->Ok
| (Score_Scalar(esti'), Score_Scalar(answ'), Some(Ok(S(prior'')))) =>
{estimate: esti', answer: answ', prior: prior''->Some}
->PointSetDist_Scoring.ScalarEstimateScalarAnswer
->Ok
| (_, _, Some(Error(err))) => err->Error
}
}
let logScore = (
~estimate: scoreDistOrScalar,
~answer: scoreDistOrScalar,
~prior: option<scoreDistOrScalar>,
): result<float, error> =>
argsMake(~esti=estimate, ~answ=answer, ~prior)->E.R.bind(x =>
x->PointSetDist.logScore->E.R2.errMap(y => DistributionTypes.OperationError(y))
)
// 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))
// )
// }
// }
}
/* /*
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

@ -25,12 +25,10 @@ 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: DistributionTypes.DistributionOperation.scoreDistOrScalar,
~prediction: t, ~answer: DistributionTypes.DistributionOperation.scoreDistOrScalar,
~answer: float, ~prior: option<DistributionTypes.DistributionOperation.scoreDistOrScalar>,
~prior: option<t>,
~toPointSetFn: toPointSetFn,
) => result<float, error> ) => result<float, error>
} }

View File

@ -215,7 +215,7 @@ module T = Dist({
}) })
let logScore = (args: PointSetDist_Scoring.scoreArgs): result<float, Operation.Error.t> => let logScore = (args: PointSetDist_Scoring.scoreArgs): result<float, Operation.Error.t> =>
PointSetDist_Scoring.logScore(args) PointSetDist_Scoring.logScore(args, ~combineFn=combinePointwise, ~integrateFn=T.integralEndY)
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)

View File

@ -2,6 +2,7 @@ type t = PointSetTypes.pointSetDist
type continuousShape = PointSetTypes.continuousShape type continuousShape = PointSetTypes.continuousShape
type discreteShape = PointSetTypes.discreteShape type discreteShape = PointSetTypes.discreteShape
type mixedShape = PointSetTypes.mixedShape type mixedShape = PointSetTypes.mixedShape
type scalar = float type scalar = float
type abstractScoreArgs<'a, 'b> = {estimate: 'a, answer: 'b, prior: option<'a>} type abstractScoreArgs<'a, 'b> = {estimate: 'a, answer: 'b, prior: option<'a>}
type scoreArgs = type scoreArgs =
@ -9,6 +10,7 @@ type scoreArgs =
| DistEstimateScalarAnswer(abstractScoreArgs<t, scalar>) | DistEstimateScalarAnswer(abstractScoreArgs<t, scalar>)
| ScalarEstimateDistAnswer(abstractScoreArgs<scalar, t>) | ScalarEstimateDistAnswer(abstractScoreArgs<scalar, t>)
| ScalarEstimateScalarAnswer(abstractScoreArgs<scalar, scalar>) | ScalarEstimateScalarAnswer(abstractScoreArgs<scalar, scalar>)
let logFn = Js.Math.log // base e let logFn = Js.Math.log // base e
let minusScaledLogOfQuot = (~esti, ~answ): result<float, Operation.Error.t> => { let minusScaledLogOfQuot = (~esti, ~answ): result<float, Operation.Error.t> => {
let quot = esti /. answ let quot = esti /. answ
@ -56,7 +58,7 @@ module WithScalarAnswer = {
} }
let scoreWithPrior' = ( let scoreWithPrior' = (
~estimatePdf: float => float, ~estimatePdf: float => float,
~answer: float, ~answer: scalar,
~priorPdf: float => float, ~priorPdf: float => float,
): result<float, Operation.Error.t> => { ): result<float, Operation.Error.t> => {
let numerator = answer->estimatePdf let numerator = answer->estimatePdf
@ -69,23 +71,44 @@ module WithScalarAnswer = {
minusScaledLogOfQuot(~esti=numerator, ~answ=priorDensityOfAnswer) minusScaledLogOfQuot(~esti=numerator, ~answ=priorDensityOfAnswer)
} }
} }
let score = (~estimate: t, ~answer: t, ~mapper): result<float, Operation.Error.t> => { let score = (~estimate: t, ~answer: scalar): result<float, Operation.Error.t> => {
let pdf = (shape, ~x) => XYShape.XtoY.linear(x, shape.xyShape) let estimatePdf = x =>
let estimatePdf = mapper((x => pdf(~x), x => pdf(~x), x => pdf(~x))) switch estimate {
| Continuous(esti) => XYShape.XtoY.linear(x, esti.xyShape)
| Discrete(esti) => XYShape.XtoY.linear(x, esti.xyShape)
| Mixed(esti) =>
XYShape.XtoY.linear(x, esti.continuous.xyShape) +.
XYShape.XtoY.linear(x, esti.discrete.xyShape)
}
score'(~estimatePdf, ~answer) score'(~estimatePdf, ~answer)
} }
let scoreWithPrior = (~estimate: t, ~answer: t, ~prior: t, ~mapper): result< let scoreWithPrior = (~estimate: t, ~answer: scalar, ~prior: t): result<
float, float,
Operation.Error.t, Operation.Error.t,
> => { > => {
let estimatePdf = x => XYShape.XtoY.linear(x, estimate.xyShape) let estimatePdf = x =>
let priorPdf = x => XYShape.XtoY.linear(x, prior.xyShape) switch estimate {
| Continuous(esti) => XYShape.XtoY.linear(x, esti.xyShape)
| Discrete(esti) => XYShape.XtoY.linear(x, esti.xyShape)
| Mixed(esti) =>
XYShape.XtoY.linear(x, esti.continuous.xyShape) +.
XYShape.XtoY.linear(x, esti.discrete.xyShape)
}
let priorPdf = x =>
switch prior {
| Continuous(prio) => XYShape.XtoY.linear(x, prio.xyShape)
| Discrete(prio) => XYShape.XtoY.linear(x, prio.xyShape)
| Mixed(prio) =>
XYShape.XtoY.linear(x, prio.continuous.xyShape) +.
XYShape.XtoY.linear(x, prio.discrete.xyShape)
}
scoreWithPrior'(~estimatePdf, ~answer, ~priorPdf) scoreWithPrior'(~estimatePdf, ~answer, ~priorPdf)
} }
} }
module TwoScalars = { module TwoScalars = {
let score = (~estimate: float, ~answer: float) => let score = (~estimate: scalar, ~answer: scalar) =>
if answer == 0.0 { if answer == 0.0 {
0.0->Ok 0.0->Ok
} else if estimate == 0.0 { } else if estimate == 0.0 {
@ -107,9 +130,9 @@ module TwoScalars = {
let logScore = (args: scoreArgs, ~combineFn, ~integrateFn): result<float, Operation.Error.t> => let logScore = (args: scoreArgs, ~combineFn, ~integrateFn): result<float, Operation.Error.t> =>
switch args { switch args {
| DistEstimateDistAnswer({estimate, answer, prior: None}) => | DistEstimateDistAnswer({estimate, answer, prior: None}) =>
WithDistAnswer.sum(~estimate, ~answer, ~integrateFn) WithDistAnswer.sum(~estimate, ~answer, ~integrateFn, ~combineFn)
| DistEstimateDistAnswer({estimate, answer, prior: Some(prior)}) => | DistEstimateDistAnswer({estimate, answer, prior: Some(prior)}) =>
WithDistAnswer.sumWithPrior(~estimate, ~answer, ~prior, ~integrateFn) WithDistAnswer.sumWithPrior(~estimate, ~answer, ~prior, ~integrateFn, ~combineFn)
| DistEstimateScalarAnswer({estimate, answer, prior: None}) => | DistEstimateScalarAnswer({estimate, answer, prior: None}) =>
WithScalarAnswer.score(~estimate, ~answer) WithScalarAnswer.score(~estimate, ~answer)
| DistEstimateScalarAnswer({estimate, answer, prior: Some(prior)}) => | DistEstimateScalarAnswer({estimate, answer, prior: Some(prior)}) =>

View File

@ -162,20 +162,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 = {
@ -226,28 +212,20 @@ let dispatchToGenericOutput = (
~env, ~env,
)->Some )->Some
| ("normalize", [EvDistribution(dist)]) => Helpers.toDistFn(Normalize, dist, ~env) | ("normalize", [EvDistribution(dist)]) => Helpers.toDistFn(Normalize, dist, ~env)
| ("klDivergence", [EvDistribution(prediction), EvDistribution(answer)]) => | ("klDivergence", [EvDistribution(estimate), EvDistribution(answer)]) =>
Some(DistributionOperation.run(FromDist(ToScore(KLDivergence(answer)), prediction), ~env)) Some(
| ("klDivergence", [EvDistribution(prediction), EvDistribution(answer), EvDistribution(prior)]) => DistributionOperation.run(
Helpers.klDivergenceWithPrior(prediction, answer, prior, env) FromDist(ToScore(LogScore(Score_Dist(answer), None)), estimate),
| ( ~env,
"logScoreWithPointAnswer", ),
[EvDistribution(prediction), EvNumber(answer), EvDistribution(prior)], )
) | ("klDivergence", [EvDistribution(estimate), EvDistribution(answer), EvDistribution(prior)]) =>
| ( Some(
"logScoreWithPointAnswer", DistributionOperation.run(
[EvDistribution(prediction), EvDistribution(Symbolic(#Float(answer))), EvDistribution(prior)], FromDist(ToScore(LogScore(Score_Dist(answer), Some(Score_Dist(prior)))), estimate),
) => ~env,
DistributionOperation.run( ),
FromDist(ToScore(LogScore(answer, prior->Some)), prediction), )
~env,
)->Some
| ("logScoreWithPointAnswer", [EvDistribution(prediction), EvNumber(answer)])
| (
"logScoreWithPointAnswer",
[EvDistribution(prediction), EvDistribution(Symbolic(#Float(answer)))],
) =>
DistributionOperation.run(FromDist(ToScore(LogScore(answer, None)), prediction), ~env)->Some
| ("isNormalized", [EvDistribution(dist)]) => Helpers.toBoolFn(IsNormalized, dist, ~env) | ("isNormalized", [EvDistribution(dist)]) => Helpers.toBoolFn(IsNormalized, dist, ~env)
| ("toPointSet", [EvDistribution(dist)]) => Helpers.toDistFn(ToPointSet, dist, ~env) | ("toPointSet", [EvDistribution(dist)]) => Helpers.toDistFn(ToPointSet, dist, ~env)
| ("scaleLog", [EvDistribution(dist)]) => | ("scaleLog", [EvDistribution(dist)]) =>