feat: Cleanup of Danger namespace

This commit is contained in:
NunoSempere 2022-09-05 12:38:15 +02:00
parent 6d75af61c3
commit a45e38d75d

View File

@ -4,56 +4,46 @@ open FunctionRegistry_Helpers
let nameSpace = "Danger" let nameSpace = "Danger"
let requiresNamespace = true let requiresNamespace = true
module NumberToNumber = { module NNumbersToNumber = {
let make = (name, fn) => module One = {
FnDefinition.make( let make = (name, fn) =>
~name, FnDefinition.make(
~inputs=[FRTypeNumber], ~name,
~run=(_, inputs, _, _) => { ~inputs=[FRTypeNumber],
inputs ~run=(_, inputs, _, _) => {
->getOrError(0) inputs
->E.R.bind(Prepare.oneNumber) ->getOrError(0)
->E.R2.fmap(fn) ->E.R.bind(Prepare.oneNumber)
->E.R2.fmap(Wrappers.evNumber) ->E.R2.fmap(fn)
}, ->E.R2.fmap(Wrappers.evNumber)
(), },
) (),
} )
}
module TwoNumbersToNumber = { module Two = {
let make = (name, fn) => let make = (name, fn) =>
FnDefinition.make( FnDefinition.make(
~name, ~name,
~inputs=[FRTypeNumber, FRTypeNumber], ~inputs=[FRTypeNumber, FRTypeNumber],
~run=(_, inputs, _, _) => { ~run=(_, inputs, _, _) => {
inputs->Prepare.ToValueTuple.twoNumbers->E.R2.fmap(fn)->E.R2.fmap(Wrappers.evNumber) inputs->Prepare.ToValueTuple.twoNumbers->E.R2.fmap(fn)->E.R2.fmap(Wrappers.evNumber)
}, },
(), (),
) )
} }
module ThreeNumbersToNumber = { module Three = {
let make = (name, fn) => let make = (name, fn) =>
FnDefinition.make( FnDefinition.make(
~name, ~name,
~inputs=[FRTypeNumber, FRTypeNumber, FRTypeNumber], ~inputs=[FRTypeNumber, FRTypeNumber, FRTypeNumber],
~run=(_, inputs, _, _) => { ~run=(_, inputs, _, _) => {
inputs->Prepare.ToValueTuple.threeNumbers->E.R2.fmap(fn)->E.R2.fmap(Wrappers.evNumber) inputs->Prepare.ToValueTuple.threeNumbers->E.R2.fmap(fn)->E.R2.fmap(Wrappers.evNumber)
}, },
(), (),
) )
} }
module FunctionToNumberZero = {
let make = (name, _) =>
FnDefinition.make(
~name,
~inputs=[FRTypeLambda],
~run=(_, _, _, _) => {
Ok(0.0)->E.R2.fmap(Wrappers.evNumber)
},
(),
)
} }
module Internals = { module Internals = {
@ -62,6 +52,7 @@ module Internals = {
let choose = ((n, k)) => factorial(n) /. (factorial(n -. k) *. factorial(k)) let choose = ((n, k)) => factorial(n) /. (factorial(n -. k) *. factorial(k))
let pow = (base, exp) => Js.Math.pow_float(~base, ~exp) let pow = (base, exp) => Js.Math.pow_float(~base, ~exp)
let binomial = ((n, k, p)) => choose((n, k)) *. pow(p, k) *. pow(1.0 -. p, n -. k) let binomial = ((n, k, p)) => choose((n, k)) *. pow(p, k) *. pow(1.0 -. p, n -. k)
// Integral helper functions // Integral helper functions
let applyFunctionAtPoint = ( let applyFunctionAtPoint = (
aLambda, aLambda,
@ -69,72 +60,30 @@ module Internals = {
environment, environment,
reducer, reducer,
): result<ReducerInterface_InternalExpressionValue.t, Reducer_ErrorValue.errorValue> => { ): result<ReducerInterface_InternalExpressionValue.t, Reducer_ErrorValue.errorValue> => {
let x = internalNumber let result = Reducer_Expression_Lambda.doLambdaCall(
let result = Reducer_Expression_Lambda.doLambdaCall(aLambda, list{x}, environment, reducer)
result
}
let internalZero = ReducerInterface_InternalExpressionValue.IEvNumber(0.0)
let applyFunctionAtZero = (aLambda, environment, reducer) =>
applyFunctionAtPoint(aLambda, internalZero, environment, reducer)
@dead
let applyFunctionAtFloat = (aLambda, point, environment, reducer) =>
applyFunctionAtPoint(
aLambda, aLambda,
ReducerInterface_InternalExpressionValue.IEvNumber(point), list{internalNumber},
environment, environment,
reducer, reducer,
) )
// simplest integral function
let integrateFunctionBetweenWithIncrement = (
aLambda,
min: float,
max: float,
increment: float,
environment,
reducer,
) => {
let applyFunctionAtFloatToFloatOption = (point: float) => {
let pointAsInternalExpression = ReducerInterface_InternalExpressionValue.IEvNumber(point)
let resultAsInternalExpression = Reducer_Expression_Lambda.doLambdaCall(
aLambda,
list{pointAsInternalExpression},
environment,
reducer,
)
let result = switch resultAsInternalExpression {
| Ok(IEvNumber(x)) => Ok(x)
| Error(_) => Error("Integration error in Danger.integrate")
| _ => Error("Integration error in Danger.integrate")
}
result
}
let xsLength = Js.Math.floor((max -. min) /. increment) // Note that we are loosing a bit of the tail
let xs = Belt.Array.makeBy(xsLength, i => min +. (Belt_Float.fromInt(i) +. 0.5) *. increment)
// makeBy goes from 0 to (n-1): <https://rescript-lang.org/docs/manual/latest/api/belt/array#makeby>
let ysOptions = Belt.Array.map(xs, x => applyFunctionAtFloatToFloatOption(x))
let okYs = E.A.R.filterOk(ysOptions)
let result = switch E.A.length(ysOptions) == E.A.length(okYs) {
| true => {
let numericIntermediate = okYs->E.A.reduce(0.0, (a, b) => a +. b)
let numericIntermediate2 = numericIntermediate *. increment
let resultWrapped =
numericIntermediate2->ReducerInterface_InternalExpressionValue.IEvNumber->Ok
resultWrapped
}
| false => Error("Integration error in Danger.integrate")
}
result result
} }
// slightly better integrate function let castFloatToInternalNumber = x => ReducerInterface_InternalExpressionValue.IEvNumber(x)
let integrateFunctionBetweenWithNumIntervals = ( @dead
let applyFunctionAtFloat = (aLambda, point, environment, reducer) =>
// reason for existence: might be an useful template to have for calculating diminishing marginal returns later on
applyFunctionAtPoint(aLambda, castFloatToInternalNumber(point), environment, reducer)
// integrate function itself
let integrateFunctionBetweenWithNumIntegrationPoints = (
aLambda, aLambda,
min: float, min: float,
max: float, max: float,
numIntervals: float, // cast as int? numIntegrationPoints: float, // cast as int?
environment, environment,
reducer, reducer,
) => { ) => {
let applyFunctionAtFloatToFloatOption = (point: float) => { let applyFunctionAtFloatToFloatOption = (point: float) => {
// Defined here so that it has access to environment, reducer
let pointAsInternalExpression = ReducerInterface_InternalExpressionValue.IEvNumber(point) let pointAsInternalExpression = ReducerInterface_InternalExpressionValue.IEvNumber(point)
let resultAsInternalExpression = Reducer_Expression_Lambda.doLambdaCall( let resultAsInternalExpression = Reducer_Expression_Lambda.doLambdaCall(
aLambda, aLambda,
@ -144,13 +93,16 @@ module Internals = {
) )
let result = switch resultAsInternalExpression { let result = switch resultAsInternalExpression {
| Ok(IEvNumber(x)) => Ok(x) | Ok(IEvNumber(x)) => Ok(x)
| Error(_) => Error("Integration error in Danger.integrate") | Error(_) =>
Error(
"Integration error in Danger.integrate. It's possible that your function doesn't return a number, try definining auxiliaryFunction(x) = mean(yourFunction(x)) and integrate auxiliaryFunction instead",
)
| _ => Error("Integration error in Danger.integrate") | _ => Error("Integration error in Danger.integrate")
} }
result result
} }
// worked example in comments below, assuming min=0, max = 10 // worked example in comments below, assuming min=0, max = 10
let numTotalPoints = Belt.Float.toInt(numIntervals) let numTotalPoints = Belt.Float.toInt(numIntegrationPoints) // superflous declaration, but useful to keep track that we are interpreting "numIntegrationPoints" as the total number on which we evaluate the function, not e.g., as the inner integration points.
let numInnerPoints = numTotalPoints - 2 let numInnerPoints = numTotalPoints - 2
let numOuterPoints = 2 let numOuterPoints = 2
let totalWeight = max -. min let totalWeight = max -. min
@ -160,32 +112,36 @@ module Internals = {
let innerXs = Belt.Array.makeBy(numInnerPoints, i => let innerXs = Belt.Array.makeBy(numInnerPoints, i =>
min +. Belt_Float.fromInt(i + 1) *. innerPointIncrement min +. Belt_Float.fromInt(i + 1) *. innerPointIncrement
) )
// Note that makeBy goes from 0 to (n-1): <https://rescript-lang.org/docs/manual/latest/api/belt/array#makeby> // Gotcha: makeBy goes from 0 to (n-1): <https://rescript-lang.org/docs/manual/latest/api/belt/array#makeby>
let ysOptions = Belt.Array.map(innerXs, x => applyFunctionAtFloatToFloatOption(x)) let ysOptions = Belt.Array.map(innerXs, x => applyFunctionAtFloatToFloatOption(x))
let okYs = E.A.R.filterOk(ysOptions) let okYs = E.A.R.filterOk(ysOptions)
// Logging /* Logging, with a worked example. */
// assuming min = 0, max = 10, results below: // Useful for understanding what is happening.
Js.Console.log2("numTotalPoints", numTotalPoints) // 5 // assuming min = 0, max = 10, numTotalPoints=10, results below:
Js.Console.log2("numInnerPoints", numInnerPoints) // 3 let verbose = false
Js.Console.log2("numOuterPoints", numOuterPoints) // always 2 if verbose {
Js.Console.log2("totalWeight", totalWeight) // 10 - 0 = 10 Js.Console.log2("numTotalPoints", numTotalPoints) // 5
Js.Console.log2("weightForAnInnerPoint", weightForAnInnerPoint) // 10/4 = 2.5 Js.Console.log2("numInnerPoints", numInnerPoints) // 3
Js.Console.log2("weightForAnOuterPoint", weightForAnOuterPoint) // 10/4/2 = 1.25 Js.Console.log2("numOuterPoints", numOuterPoints) // always 2
Js.Console.log2( Js.Console.log2("totalWeight", totalWeight) // 10 - 0 = 10
"weightForAnInnerPoint * numInnerPoints + weightForAnOuterPoint * numOuterPoints", Js.Console.log2("weightForAnInnerPoint", weightForAnInnerPoint) // 10/4 = 2.5
weightForAnInnerPoint *. E.I.toFloat(numInnerPoints) +. Js.Console.log2("weightForAnOuterPoint", weightForAnOuterPoint) // 10/4/2 = 1.25
weightForAnOuterPoint *. E.I.toFloat(numOuterPoints), Js.Console.log2(
) // should be 10 "weightForAnInnerPoint * numInnerPoints + weightForAnOuterPoint * numOuterPoints",
Js.Console.log2( weightForAnInnerPoint *. E.I.toFloat(numInnerPoints) +.
"sum of weights == totalWeight", weightForAnOuterPoint *. E.I.toFloat(numOuterPoints),
weightForAnInnerPoint *. E.I.toFloat(numInnerPoints) +. ) // should be 10
weightForAnOuterPoint *. E.I.toFloat(numOuterPoints) == totalWeight, Js.Console.log2(
) // true "sum of weights == totalWeight",
Js.Console.log2("innerPointIncrement", innerPointIncrement) // (10-0)/4 = 2.5 weightForAnInnerPoint *. E.I.toFloat(numInnerPoints) +.
Js.Console.log2("innerXs", innerXs) // 2.5, 5, 7.5 weightForAnOuterPoint *. E.I.toFloat(numOuterPoints) == totalWeight,
Js.Console.log2("ysOptions", ysOptions) ) // true
Js.Console.log2("okYs", okYs) Js.Console.log2("innerPointIncrement", innerPointIncrement) // (10-0)/4 = 2.5
Js.Console.log2("innerXs", innerXs) // 2.5, 5, 7.5
Js.Console.log2("ysOptions", ysOptions)
Js.Console.log2("okYs", okYs)
}
let result = switch E.A.length(ysOptions) == E.A.length(okYs) { let result = switch E.A.length(ysOptions) == E.A.length(okYs) {
| true => { | true => {
@ -220,7 +176,7 @@ let library = [
~output=EvtNumber, ~output=EvtNumber,
~examples=[`Danger.laplace(1, 20)`], ~examples=[`Danger.laplace(1, 20)`],
~definitions=[ ~definitions=[
TwoNumbersToNumber.make("laplace", ((successes, trials)) => NNumbersToNumber.Two.make("laplace", ((successes, trials)) =>
(successes +. 1.0) /. (trials +. 2.0) (successes +. 1.0) /. (trials +. 2.0)
), ),
], ],
@ -232,7 +188,7 @@ let library = [
~requiresNamespace, ~requiresNamespace,
~output=EvtNumber, ~output=EvtNumber,
~examples=[`Danger.factorial(20)`], ~examples=[`Danger.factorial(20)`],
~definitions=[NumberToNumber.make("factorial", Internals.factorial)], ~definitions=[NNumbersToNumber.One.make("factorial", Internals.factorial)],
(), (),
), ),
Function.make( Function.make(
@ -241,7 +197,7 @@ let library = [
~requiresNamespace, ~requiresNamespace,
~output=EvtNumber, ~output=EvtNumber,
~examples=[`Danger.choose(1, 20)`], ~examples=[`Danger.choose(1, 20)`],
~definitions=[TwoNumbersToNumber.make("choose", Internals.choose)], ~definitions=[NNumbersToNumber.Two.make("choose", Internals.choose)],
(), (),
), ),
Function.make( Function.make(
@ -250,19 +206,10 @@ let library = [
~requiresNamespace, ~requiresNamespace,
~output=EvtNumber, ~output=EvtNumber,
~examples=[`Danger.binomial(1, 20, 0.5)`], ~examples=[`Danger.binomial(1, 20, 0.5)`],
~definitions=[ThreeNumbersToNumber.make("binomial", Internals.binomial)], ~definitions=[NNumbersToNumber.Three.make("binomial", Internals.binomial)],
(), (),
), ),
// Helper functions building up to the integral // Helper functions building up to the integral
Function.make(
~name="functionToZero",
~nameSpace,
~requiresNamespace,
~output=EvtNumber,
~examples=[`Danger.functionToZero({|x| x})`],
~definitions=[FunctionToNumberZero.make("functionToZero", x => x)],
(),
),
Function.make( Function.make(
~name="applyFunctionAtZero", ~name="applyFunctionAtZero",
~nameSpace, ~nameSpace,
@ -273,10 +220,15 @@ let library = [
FnDefinition.make( FnDefinition.make(
~name="applyFunctionAtZero", ~name="applyFunctionAtZero",
~inputs=[FRTypeLambda], ~inputs=[FRTypeLambda],
~run=(inputs, _, env, reducer) => { ~run=(inputs, _, environment, reducer) => {
let result = switch inputs { let result = switch inputs {
| [IEvLambda(aLambda)] => | [IEvLambda(aLambda)] =>
Internals.applyFunctionAtZero(aLambda, env, reducer)->E.R2.errMap(_ => "Error!") Internals.applyFunctionAtPoint(
aLambda,
Internals.castFloatToInternalNumber(0.0),
environment,
reducer,
)->E.R2.errMap(_ => "Error!")
| _ => Error(impossibleError) | _ => Error(impossibleError)
} }
result result
@ -307,29 +259,31 @@ let library = [
], ],
(), (),
), ),
// simplest integral // Integral in terms of function, min, max, num points
// Note that execution time will be more predictable, because it
// will only depend on num points and the complexity of the function
Function.make( Function.make(
~name="integrateFunctionBetweenWithIncrement", ~name="integrateFunctionBetweenWithNumIntegrationPoints",
~nameSpace, ~nameSpace,
~output=EvtNumber, ~output=EvtNumber,
~requiresNamespace=false, ~requiresNamespace=false,
~examples=[`Danger.integrateFunctionBetweenWithIncrement({|x| x+1}, 1, 10, 1)`], ~examples=[`Danger.integrateFunctionBetweenWithNumIntegrationPoints({|x| x+1}, 1, 10, 10)`],
// should be [x^2/2 + x]1_10 = (100/2 + 10) - (1/2 + 1) = 60 - 1.5 = 58.5 // should be [x^2/2 + x]1_10 = (100/2 + 10) - (1/2 + 1) = 60 - 1.5 = 58.5
// https://www.wolframalpha.com/input?i=integrate+x%2B1+from+1+to+10 // https://www.wolframalpha.com/input?i=integrate+x%2B1+from+1+to+10
~definitions=[ ~definitions=[
FnDefinition.make( FnDefinition.make(
~name="integrateFunctionBetweenWithIncrement", ~name="integrateFunctionBetweenWithNumIntegrationPoints",
~inputs=[FRTypeLambda, FRTypeNumber, FRTypeNumber, FRTypeNumber], ~inputs=[FRTypeLambda, FRTypeNumber, FRTypeNumber, FRTypeNumber],
~run=(inputs, _, env, reducer) => { ~run=(inputs, _, env, reducer) => {
let result = switch inputs { let result = switch inputs {
| [_, _, _, IEvNumber(0.0)] => | [_, _, _, IEvNumber(0.0)] =>
Error("Integration error in Danger.integrate: Increment can't be 0.") Error("Integration error in Danger.integrate: Increment can't be 0.")
| [IEvLambda(aLambda), IEvNumber(min), IEvNumber(max), IEvNumber(increment)] => | [IEvLambda(aLambda), IEvNumber(min), IEvNumber(max), IEvNumber(numIntegrationPoints)] =>
Internals.integrateFunctionBetweenWithIncrement( Internals.integrateFunctionBetweenWithNumIntegrationPoints(
aLambda, aLambda,
min, min,
max, max,
increment, numIntegrationPoints,
env, env,
reducer, reducer,
)->E.R2.errMap(_ => )->E.R2.errMap(_ =>
@ -347,29 +301,30 @@ let library = [
], ],
(), (),
), ),
// Integral which is a bit more thoughtful // Integral in terms of function, min, max, epsilon (distance between points)
// Note that execution time will be less predictable, because it
// will depend on min, max and epsilon together,
// as well and the complexity of the function
Function.make( Function.make(
~name="integrateFunctionBetweenWithNumIntervals", ~name="integrateFunctionBetweenWithEpsilon",
~nameSpace, ~nameSpace,
~output=EvtNumber, ~output=EvtNumber,
~requiresNamespace=false, ~requiresNamespace=false,
~examples=[`Danger.integrateFunctionBetweenWithNumIntervals({|x| x+1}, 1, 10, 10)`], ~examples=[`Danger.integrateFunctionBetweenWithEpsilon({|x| x+1}, 1, 10, 1)`],
// should be [x^2/2 + x]1_10 = (100/2 + 10) - (1/2 + 1) = 60 - 1.5 = 58.5
// https://www.wolframalpha.com/input?i=integrate+x%2B1+from+1+to+10
~definitions=[ ~definitions=[
FnDefinition.make( FnDefinition.make(
~name="integrateFunctionBetweenWithNumIntervals", ~name="integrateFunctionBetweenWithEpsilon",
~inputs=[FRTypeLambda, FRTypeNumber, FRTypeNumber, FRTypeNumber], ~inputs=[FRTypeLambda, FRTypeNumber, FRTypeNumber, FRTypeNumber],
~run=(inputs, _, env, reducer) => { ~run=(inputs, _, env, reducer) => {
let result = switch inputs { let result = switch inputs {
| [_, _, _, IEvNumber(0.0)] => | [_, _, _, IEvNumber(0.0)] =>
Error("Integration error in Danger.integrate: Increment can't be 0.") Error("Integration error in Danger.integrate: Increment can't be 0.")
| [IEvLambda(aLambda), IEvNumber(min), IEvNumber(max), IEvNumber(numIntervals)] => | [IEvLambda(aLambda), IEvNumber(min), IEvNumber(max), IEvNumber(epsilon)] =>
Internals.integrateFunctionBetweenWithNumIntervals( Internals.integrateFunctionBetweenWithNumIntegrationPoints(
aLambda, aLambda,
min, min,
max, max,
numIntervals, (max -. min) /. epsilon,
env, env,
reducer, reducer,
)->E.R2.errMap(_ => )->E.R2.errMap(_ =>