Merge pull request #821 from quantified-uncertainty/namespace-integration
Adds module namespace support to Function Registry
This commit is contained in:
commit
12ac2f551b
|
@ -529,7 +529,6 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
|
||||||
|
|
||||||
const withoutEditor = <div className="mt-3">{tabs}</div>;
|
const withoutEditor = <div className="mt-3">{tabs}</div>;
|
||||||
|
|
||||||
console.log(vars);
|
|
||||||
return (
|
return (
|
||||||
<SquiggleContainer>
|
<SquiggleContainer>
|
||||||
<StyledTab.Group>
|
<StyledTab.Group>
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
open Jest
|
||||||
|
open Expect
|
||||||
|
open Reducer_TestHelpers
|
||||||
|
|
||||||
|
let expectEvalToBeOk = (expr: string) =>
|
||||||
|
Reducer.evaluate(expr)->Reducer_Helpers.rRemoveDefaultsExternal->E.R.isOk->expect->toBe(true)
|
||||||
|
|
||||||
|
let registry = FunctionRegistry_Library.registry
|
||||||
|
let examples = E.A.to_list(FunctionRegistry_Core.Registry.allExamples(registry))
|
||||||
|
|
||||||
|
describe("FunctionRegistry Library", () => {
|
||||||
|
describe("Regular tests", () => {
|
||||||
|
testEvalToBe("List.make(3, 'HI')", "Ok(['HI','HI','HI'])")
|
||||||
|
testEvalToBe("make(3, 'HI')", "Error(Function not found: make(Number,String))")
|
||||||
|
testEvalToBe("List.upTo(1,3)", "Ok([1,2,3])")
|
||||||
|
testEvalToBe("List.first([3,5,8])", "Ok(3)")
|
||||||
|
testEvalToBe("List.last([3,5,8])", "Ok(8)")
|
||||||
|
testEvalToBe("List.reverse([3,5,8])", "Ok([8,5,3])")
|
||||||
|
testEvalToBe("Dist.normal(5,2)", "Ok(Normal(5,2))")
|
||||||
|
testEvalToBe("normal(5,2)", "Ok(Normal(5,2))")
|
||||||
|
testEvalToBe("normal({mean:5,stdev:2})", "Ok(Normal(5,2))")
|
||||||
|
testEvalToBe("-2 to 4", "Ok(Normal(1,1.8238704957353074))")
|
||||||
|
testEvalToBe("pointMass(5)", "Ok(PointMass(5))")
|
||||||
|
testEvalToBe("Number.floor(5.5)", "Ok(5)")
|
||||||
|
testEvalToBe("Number.ceil(5.5)", "Ok(6)")
|
||||||
|
testEvalToBe("floor(5.5)", "Ok(5)")
|
||||||
|
testEvalToBe("ceil(5.5)", "Ok(6)")
|
||||||
|
testEvalToBe("Number.abs(5.5)", "Ok(5.5)")
|
||||||
|
testEvalToBe("abs(5.5)", "Ok(5.5)")
|
||||||
|
testEvalToBe("Number.exp(10)", "Ok(22026.465794806718)")
|
||||||
|
testEvalToBe("Number.log10(10)", "Ok(1)")
|
||||||
|
testEvalToBe("Number.log2(10)", "Ok(3.321928094887362)")
|
||||||
|
testEvalToBe("Number.sum([2,5,3])", "Ok(10)")
|
||||||
|
testEvalToBe("sum([2,5,3])", "Ok(10)")
|
||||||
|
testEvalToBe("Number.product([2,5,3])", "Ok(30)")
|
||||||
|
testEvalToBe("Number.min([2,5,3])", "Ok(2)")
|
||||||
|
testEvalToBe("Number.max([2,5,3])", "Ok(5)")
|
||||||
|
testEvalToBe("Number.mean([0,5,10])", "Ok(5)")
|
||||||
|
testEvalToBe("Number.geomean([1,5,18])", "Ok(4.481404746557164)")
|
||||||
|
testEvalToBe("Number.stdev([0,5,10,15])", "Ok(5.5901699437494745)")
|
||||||
|
testEvalToBe("Number.variance([0,5,10,15])", "Ok(31.25)")
|
||||||
|
testEvalToBe("Number.sort([10,0,15,5])", "Ok([0,5,10,15])")
|
||||||
|
testEvalToBe("Number.cumsum([1,5,3])", "Ok([1,6,9])")
|
||||||
|
testEvalToBe("Number.cumprod([1,5,3])", "Ok([1,5,15])")
|
||||||
|
testEvalToBe("Number.diff([1,5,3])", "Ok([4,-2])")
|
||||||
|
testEvalToBe(
|
||||||
|
"Dist.logScore({estimate: normal(5,2), answer: normal(5.2,1), prior: normal(5.5,3)})",
|
||||||
|
"Ok(-0.33591375663884876)",
|
||||||
|
)
|
||||||
|
testEvalToBe(
|
||||||
|
"Dist.logScore({estimate: normal(5,2), answer: normal(5.2,1)})",
|
||||||
|
"Ok(0.32244107041564646)",
|
||||||
|
)
|
||||||
|
testEvalToBe("Dist.logScore({estimate: normal(5,2), answer: 4.5})", "Ok(1.6433360626394853)")
|
||||||
|
testEvalToBe("Dist.klDivergence(normal(5,2), normal(5,1.5))", "Ok(0.06874342818671068)")
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("Fn auto-testing", () => {
|
||||||
|
testAll("tests of validity", examples, r => {
|
||||||
|
expectEvalToBeOk(r)
|
||||||
|
})
|
||||||
|
|
||||||
|
testAll(
|
||||||
|
"tests of type",
|
||||||
|
E.A.to_list(
|
||||||
|
FunctionRegistry_Core.Registry.allExamplesWithFns(registry)->E.A2.filter(((fn, _)) =>
|
||||||
|
E.O.isSome(fn.output)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
((fn, example)) => {
|
||||||
|
let responseType =
|
||||||
|
example
|
||||||
|
->Reducer.evaluate
|
||||||
|
->E.R2.fmap(ReducerInterface_InternalExpressionValue.externalValueToValueType)
|
||||||
|
let expectedOutputType = fn.output |> E.O.toExn("")
|
||||||
|
expect(responseType)->toEqual(Ok(expectedOutputType))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
|
@ -1,4 +1,5 @@
|
||||||
type internalExpressionValue = ReducerInterface_InternalExpressionValue.t
|
type internalExpressionValue = ReducerInterface_InternalExpressionValue.t
|
||||||
|
type internalExpressionValueType = ReducerInterface_InternalExpressionValue.internalExpressionValueType
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Function Registry "Type". A type, without any other information.
|
Function Registry "Type". A type, without any other information.
|
||||||
|
@ -42,18 +43,26 @@ and frValueDistOrNumber = FRValueNumber(float) | FRValueDist(DistributionTypes.g
|
||||||
type fnDefinition = {
|
type fnDefinition = {
|
||||||
name: string,
|
name: string,
|
||||||
inputs: array<frType>,
|
inputs: array<frType>,
|
||||||
run: (array<frValue>, GenericDist.env) => result<internalExpressionValue, string>,
|
run: (
|
||||||
|
array<internalExpressionValue>,
|
||||||
|
array<frValue>,
|
||||||
|
GenericDist.env,
|
||||||
|
) => result<internalExpressionValue, string>,
|
||||||
}
|
}
|
||||||
|
|
||||||
type function = {
|
type function = {
|
||||||
name: string,
|
name: string,
|
||||||
definitions: array<fnDefinition>,
|
definitions: array<fnDefinition>,
|
||||||
examples: option<string>,
|
requiresNamespace: bool,
|
||||||
|
nameSpace: string,
|
||||||
|
output: option<internalExpressionValueType>,
|
||||||
|
examples: array<string>,
|
||||||
description: option<string>,
|
description: option<string>,
|
||||||
isExperimental: bool,
|
isExperimental: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
type registry = array<function>
|
type fnNameDict = Js.Dict.t<array<function>>
|
||||||
|
type registry = {functions: array<function>, fnNameDict: fnNameDict}
|
||||||
|
|
||||||
module FRType = {
|
module FRType = {
|
||||||
type t = frType
|
type t = frType
|
||||||
|
@ -265,7 +274,7 @@ module Matcher = {
|
||||||
|
|
||||||
module Registry = {
|
module Registry = {
|
||||||
let _findExactMatches = (r: registry, fnName: string, args: array<internalExpressionValue>) => {
|
let _findExactMatches = (r: registry, fnName: string, args: array<internalExpressionValue>) => {
|
||||||
let functionMatchPairs = r->E.A2.fmap(l => (l, Function.match(l, fnName, args)))
|
let functionMatchPairs = r.functions->E.A2.fmap(l => (l, Function.match(l, fnName, args)))
|
||||||
let fullMatch = functionMatchPairs->E.A.getBy(((_, match)) => Match.isFullMatch(match))
|
let fullMatch = functionMatchPairs->E.A.getBy(((_, match)) => Match.isFullMatch(match))
|
||||||
fullMatch->E.O.bind(((fn, match)) =>
|
fullMatch->E.O.bind(((fn, match)) =>
|
||||||
switch match {
|
switch match {
|
||||||
|
@ -276,7 +285,7 @@ module Matcher = {
|
||||||
}
|
}
|
||||||
|
|
||||||
let _findNameMatches = (r: registry, fnName: string, args: array<internalExpressionValue>) => {
|
let _findNameMatches = (r: registry, fnName: string, args: array<internalExpressionValue>) => {
|
||||||
let functionMatchPairs = r->E.A2.fmap(l => (l, Function.match(l, fnName, args)))
|
let functionMatchPairs = r.functions->E.A2.fmap(l => (l, Function.match(l, fnName, args)))
|
||||||
let getNameMatches =
|
let getNameMatches =
|
||||||
functionMatchPairs
|
functionMatchPairs
|
||||||
->E.A2.fmap(((fn, match)) => Match.isNameMatchOnly(match) ? Some((fn, match)) : None)
|
->E.A2.fmap(((fn, match)) => Match.isNameMatchOnly(match) ? Some((fn, match)) : None)
|
||||||
|
@ -295,10 +304,13 @@ module Matcher = {
|
||||||
}
|
}
|
||||||
|
|
||||||
let findMatches = (r: registry, fnName: string, args: array<internalExpressionValue>) => {
|
let findMatches = (r: registry, fnName: string, args: array<internalExpressionValue>) => {
|
||||||
switch _findExactMatches(r, fnName, args) {
|
let fnNameInParts = Js.String.split(".", fnName)
|
||||||
|
let fnToSearch = E.A.get(fnNameInParts, 1) |> E.O.default(fnNameInParts[0])
|
||||||
|
|
||||||
|
switch _findExactMatches(r, fnToSearch, args) {
|
||||||
| Some(r) => Match.FullMatch(r)
|
| Some(r) => Match.FullMatch(r)
|
||||||
| None =>
|
| None =>
|
||||||
switch _findNameMatches(r, fnName, args) {
|
switch _findNameMatches(r, fnToSearch, args) {
|
||||||
| Some(r) => Match.SameNameDifferentArguments(r)
|
| Some(r) => Match.SameNameDifferentArguments(r)
|
||||||
| None => Match.DifferentName
|
| None => Match.DifferentName
|
||||||
}
|
}
|
||||||
|
@ -308,7 +320,7 @@ module Matcher = {
|
||||||
let matchToDef = (registry: registry, {fnName, inputIndex}: RegistryMatch.match): option<
|
let matchToDef = (registry: registry, {fnName, inputIndex}: RegistryMatch.match): option<
|
||||||
fnDefinition,
|
fnDefinition,
|
||||||
> =>
|
> =>
|
||||||
registry
|
registry.functions
|
||||||
->E.A.getBy(fn => fn.name === fnName)
|
->E.A.getBy(fn => fn.name === fnName)
|
||||||
->E.O.bind(fn => E.A.get(fn.definitions, inputIndex))
|
->E.O.bind(fn => E.A.get(fn.definitions, inputIndex))
|
||||||
}
|
}
|
||||||
|
@ -322,15 +334,23 @@ module FnDefinition = {
|
||||||
t.name ++ `(${inputs})`
|
t.name ++ `(${inputs})`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let isMatch = (t: t, args: array<internalExpressionValue>) => {
|
||||||
|
let argValues = FRType.matchWithExpressionValueArray(t.inputs, args)
|
||||||
|
switch argValues {
|
||||||
|
| Some(_) => true
|
||||||
|
| None => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let run = (t: t, args: array<internalExpressionValue>, env: GenericDist.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(args, values, env)
|
||||||
| None => Error("Incorrect Types")
|
| None => Error("Incorrect Types")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let make = (~name, ~inputs, ~run): t => {
|
let make = (~name, ~inputs, ~run, ()): t => {
|
||||||
name: name,
|
name: name,
|
||||||
inputs: inputs,
|
inputs: inputs,
|
||||||
run: run,
|
run: run,
|
||||||
|
@ -343,16 +363,29 @@ module Function = {
|
||||||
type functionJson = {
|
type functionJson = {
|
||||||
name: string,
|
name: string,
|
||||||
definitions: array<string>,
|
definitions: array<string>,
|
||||||
examples: option<string>,
|
examples: array<string>,
|
||||||
description: option<string>,
|
description: option<string>,
|
||||||
isExperimental: bool,
|
isExperimental: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
let make = (~name, ~definitions, ~examples=?, ~description=?, ~isExperimental=false, ()): t => {
|
let make = (
|
||||||
|
~name,
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~definitions,
|
||||||
|
~examples=?,
|
||||||
|
~output=?,
|
||||||
|
~description=?,
|
||||||
|
~isExperimental=false,
|
||||||
|
(),
|
||||||
|
): t => {
|
||||||
name: name,
|
name: name,
|
||||||
|
nameSpace: nameSpace,
|
||||||
definitions: definitions,
|
definitions: definitions,
|
||||||
examples: examples,
|
output: output,
|
||||||
|
examples: examples |> E.O.default([]),
|
||||||
isExperimental: isExperimental,
|
isExperimental: isExperimental,
|
||||||
|
requiresNamespace: requiresNamespace,
|
||||||
description: description,
|
description: description,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,22 +398,64 @@ module Function = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module NameSpace = {
|
||||||
|
type t = {name: string, functions: array<function>}
|
||||||
|
let definitions = (t: t) => t.functions->E.A2.fmap(f => f.definitions)->E.A.concatMany
|
||||||
|
let uniqueFnNames = (t: t) => definitions(t)->E.A2.fmap(r => r.name)->E.A.uniq
|
||||||
|
let nameToDefinitions = (t: t, name: string) => definitions(t)->E.A2.filter(r => r.name == name)
|
||||||
|
}
|
||||||
|
|
||||||
module Registry = {
|
module Registry = {
|
||||||
let toJson = (r: registry) => r->E.A2.fmap(Function.toJson)
|
let toJson = (r: registry) => r.functions->E.A2.fmap(Function.toJson)
|
||||||
|
let allExamples = (r: registry) => r.functions->E.A2.fmap(r => r.examples)->E.A.concatMany
|
||||||
|
let allExamplesWithFns = (r: registry) =>
|
||||||
|
r.functions->E.A2.fmap(fn => fn.examples->E.A2.fmap(example => (fn, example)))->E.A.concatMany
|
||||||
|
|
||||||
|
let _buildFnNameDict = (r: array<function>): fnNameDict => {
|
||||||
|
let allDefinitionsWithFns =
|
||||||
|
r
|
||||||
|
->E.A2.fmap(fn => fn.definitions->E.A2.fmap(definitions => (fn, definitions)))
|
||||||
|
->E.A.concatMany
|
||||||
|
let functionsWithFnNames =
|
||||||
|
allDefinitionsWithFns
|
||||||
|
->E.A2.fmap(((fn, def)) => {
|
||||||
|
let nameWithNamespace = `${fn.nameSpace}.${def.name}`
|
||||||
|
let nameWithoutNamespace = def.name
|
||||||
|
fn.requiresNamespace
|
||||||
|
? [(nameWithNamespace, fn)]
|
||||||
|
: [(nameWithNamespace, fn), (nameWithoutNamespace, fn)]
|
||||||
|
})
|
||||||
|
->E.A.concatMany
|
||||||
|
let uniqueNames = functionsWithFnNames->E.A2.fmap(((name, _)) => name)->E.A.uniq
|
||||||
|
let cacheAsArray: array<(string, array<function>)> = uniqueNames->E.A2.fmap(uniqueName => {
|
||||||
|
let relevantItems =
|
||||||
|
E.A2.filter(functionsWithFnNames, ((defName, _)) => defName == uniqueName)->E.A2.fmap(
|
||||||
|
E.Tuple2.second,
|
||||||
|
)
|
||||||
|
(uniqueName, relevantItems)
|
||||||
|
})
|
||||||
|
cacheAsArray->Js.Dict.fromArray
|
||||||
|
}
|
||||||
|
|
||||||
|
let make = (fns: array<function>): registry => {
|
||||||
|
let dict = _buildFnNameDict(fns)
|
||||||
|
{functions: fns, fnNameDict: dict}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
There's a (potential+minor) bug here: If a function definition is called outside of the calls
|
There's a (potential+minor) bug here: If a function definition is called outside of the calls
|
||||||
to the registry, then it's possible that there could be a match after the registry is
|
to the registry, then it's possible that there could be a match after the registry is
|
||||||
called. However, for now, we could just call the registry last.
|
called. However, for now, we could just call the registry last.
|
||||||
*/
|
*/
|
||||||
let matchAndRun = (
|
let _matchAndRun = (
|
||||||
~registry: registry,
|
~registry: registry,
|
||||||
~fnName: string,
|
~fnName: string,
|
||||||
~args: array<internalExpressionValue>,
|
~args: array<internalExpressionValue>,
|
||||||
~env: GenericDist.env,
|
~env: GenericDist.env,
|
||||||
) => {
|
) => {
|
||||||
|
let relevantFunctions = Js.Dict.get(registry.fnNameDict, fnName) |> E.O.default([])
|
||||||
|
let modified = {functions: relevantFunctions, fnNameDict: registry.fnNameDict}
|
||||||
let matchToDef = m => Matcher.Registry.matchToDef(registry, m)
|
let matchToDef = m => Matcher.Registry.matchToDef(registry, m)
|
||||||
//Js.log(toSimple(registry))
|
|
||||||
let showNameMatchDefinitions = matches => {
|
let showNameMatchDefinitions = matches => {
|
||||||
let defs =
|
let defs =
|
||||||
matches
|
matches
|
||||||
|
@ -391,10 +466,21 @@ module Registry = {
|
||||||
->E.A2.joinWith("; ")
|
->E.A2.joinWith("; ")
|
||||||
`There are function matches for ${fnName}(), but with different arguments: ${defs}`
|
`There are function matches for ${fnName}(), but with different arguments: ${defs}`
|
||||||
}
|
}
|
||||||
switch Matcher.Registry.findMatches(registry, fnName, args) {
|
|
||||||
|
switch Matcher.Registry.findMatches(modified, fnName, args) {
|
||||||
| Matcher.Match.FullMatch(match) => match->matchToDef->E.O2.fmap(FnDefinition.run(_, args, env))
|
| Matcher.Match.FullMatch(match) => match->matchToDef->E.O2.fmap(FnDefinition.run(_, args, env))
|
||||||
| SameNameDifferentArguments(m) => Some(Error(showNameMatchDefinitions(m)))
|
| SameNameDifferentArguments(m) => Some(Error(showNameMatchDefinitions(m)))
|
||||||
| _ => None
|
| _ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let dispatch = (
|
||||||
|
registry,
|
||||||
|
(fnName, args): ReducerInterface_InternalExpressionValue.functionCall,
|
||||||
|
env,
|
||||||
|
) => {
|
||||||
|
_matchAndRun(~registry, ~fnName, ~args, ~env)->E.O2.fmap(
|
||||||
|
E.R2.errMap(_, s => Reducer_ErrorValue.RETodo(s)),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -213,72 +213,3 @@ module Process = {
|
||||||
twoValues(~fn=Helpers.wrapSymbolic(fn), ~values)
|
twoValues(~fn=Helpers.wrapSymbolic(fn), ~values)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module TwoArgDist = {
|
|
||||||
let process = (~fn, ~env, r) =>
|
|
||||||
r
|
|
||||||
->E.R.bind(Process.DistOrNumberToDist.twoValuesUsingSymbolicDist(~fn, ~values=_, ~env))
|
|
||||||
->E.R2.fmap(Wrappers.evDistribution)
|
|
||||||
|
|
||||||
let make = (name, fn) => {
|
|
||||||
FnDefinition.make(~name, ~inputs=[FRTypeDistOrNumber, FRTypeDistOrNumber], ~run=(inputs, env) =>
|
|
||||||
inputs->Prepare.ToValueTuple.twoDistOrNumber->process(~fn, ~env)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
let makeRecordP5P95 = (name, fn) => {
|
|
||||||
FnDefinition.make(
|
|
||||||
~name,
|
|
||||||
~inputs=[FRTypeRecord([("p5", FRTypeDistOrNumber), ("p95", FRTypeDistOrNumber)])],
|
|
||||||
~run=(inputs, env) => inputs->Prepare.ToValueTuple.Record.twoDistOrNumber->process(~fn, ~env),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
let makeRecordMeanStdev = (name, fn) => {
|
|
||||||
FnDefinition.make(
|
|
||||||
~name,
|
|
||||||
~inputs=[FRTypeRecord([("mean", FRTypeDistOrNumber), ("stdev", FRTypeDistOrNumber)])],
|
|
||||||
~run=(inputs, env) => inputs->Prepare.ToValueTuple.Record.twoDistOrNumber->process(~fn, ~env),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module OneArgDist = {
|
|
||||||
let process = (~fn, ~env, r) =>
|
|
||||||
r
|
|
||||||
->E.R.bind(Process.DistOrNumberToDist.oneValueUsingSymbolicDist(~fn, ~value=_, ~env))
|
|
||||||
->E.R2.fmap(Wrappers.evDistribution)
|
|
||||||
|
|
||||||
let make = (name, fn) =>
|
|
||||||
FnDefinition.make(~name, ~inputs=[FRTypeDistOrNumber], ~run=(inputs, env) =>
|
|
||||||
inputs->Prepare.ToValueTuple.oneDistOrNumber->process(~fn, ~env)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
module ArrayNumberDist = {
|
|
||||||
let make = (name, fn) => {
|
|
||||||
FnDefinition.make(~name, ~inputs=[FRTypeArray(FRTypeNumber)], ~run=(inputs, _) =>
|
|
||||||
Prepare.ToTypedArray.numbers(inputs)
|
|
||||||
->E.R.bind(r => E.A.length(r) === 0 ? Error("List is empty") : Ok(r))
|
|
||||||
->E.R.bind(fn)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
let make2 = (name, fn) => {
|
|
||||||
FnDefinition.make(~name, ~inputs=[FRTypeArray(FRTypeAny)], ~run=(inputs, _) =>
|
|
||||||
Prepare.ToTypedArray.numbers(inputs)
|
|
||||||
->E.R.bind(r => E.A.length(r) === 0 ? Error("List is empty") : Ok(r))
|
|
||||||
->E.R.bind(fn)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module NumberToNumber = {
|
|
||||||
let make = (name, fn) =>
|
|
||||||
FnDefinition.make(~name, ~inputs=[FRTypeNumber], ~run=(inputs, _) => {
|
|
||||||
inputs
|
|
||||||
->getOrError(0)
|
|
||||||
->E.R.bind(Prepare.oneNumber)
|
|
||||||
->E.R2.fmap(fn)
|
|
||||||
->E.R2.fmap(Wrappers.evNumber)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,576 +1,12 @@
|
||||||
open FunctionRegistry_Core
|
let fnList = Belt.Array.concatMany([
|
||||||
open FunctionRegistry_Helpers
|
FR_Dict.library,
|
||||||
|
FR_Dist.library,
|
||||||
|
FR_Fn.library,
|
||||||
|
FR_List.library,
|
||||||
|
FR_Number.library,
|
||||||
|
FR_Pointset.library,
|
||||||
|
FR_Scoring.library,
|
||||||
|
])
|
||||||
|
|
||||||
let twoArgs = E.Tuple2.toFnCall
|
let registry = FunctionRegistry_Core.Registry.make(fnList)
|
||||||
|
let dispatch = FunctionRegistry_Core.Registry.dispatch(registry)
|
||||||
module Declaration = {
|
|
||||||
let frType = FRTypeRecord([
|
|
||||||
("fn", FRTypeLambda),
|
|
||||||
("inputs", FRTypeArray(FRTypeRecord([("min", FRTypeNumber), ("max", FRTypeNumber)]))),
|
|
||||||
])
|
|
||||||
|
|
||||||
let fromExpressionValue = (e: frValue): result<internalExpressionValue, string> => {
|
|
||||||
switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.twoArgs([e]) {
|
|
||||||
| Ok([FRValueLambda(lambda), FRValueArray(inputs)]) => {
|
|
||||||
open FunctionRegistry_Helpers.Prepare
|
|
||||||
let getMinMax = arg =>
|
|
||||||
ToValueArray.Record.toArgs([arg])
|
|
||||||
->E.R.bind(ToValueTuple.twoNumbers)
|
|
||||||
->E.R2.fmap(((min, max)) => Declaration.ContinuousFloatArg.make(min, max))
|
|
||||||
inputs
|
|
||||||
->E.A2.fmap(getMinMax)
|
|
||||||
->E.A.R.firstErrorOrOpen
|
|
||||||
->E.R2.fmap(args => ReducerInterface_InternalExpressionValue.IEvDeclaration(
|
|
||||||
Declaration.make(lambda, args),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
| Error(r) => Error(r)
|
|
||||||
| Ok(_) => Error(FunctionRegistry_Helpers.impossibleError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let inputsTodist = (inputs: array<FunctionRegistry_Core.frValue>, makeDist) => {
|
|
||||||
let array = inputs->getOrError(0)->E.R.bind(Prepare.ToValueArray.Array.openA)
|
|
||||||
let xyCoords =
|
|
||||||
array->E.R.bind(xyCoords =>
|
|
||||||
xyCoords
|
|
||||||
->E.A2.fmap(xyCoord =>
|
|
||||||
[xyCoord]->Prepare.ToValueArray.Record.twoArgs->E.R.bind(Prepare.ToValueTuple.twoNumbers)
|
|
||||||
)
|
|
||||||
->E.A.R.firstErrorOrOpen
|
|
||||||
)
|
|
||||||
let expressionValue =
|
|
||||||
xyCoords
|
|
||||||
->E.R.bind(r => r->XYShape.T.makeFromZipped->E.R2.errMap(XYShape.Error.toString))
|
|
||||||
->E.R2.fmap(r => ReducerInterface_InternalExpressionValue.IEvDistribution(
|
|
||||||
PointSet(makeDist(r)),
|
|
||||||
))
|
|
||||||
expressionValue
|
|
||||||
}
|
|
||||||
|
|
||||||
let registryStart = [
|
|
||||||
Function.make(
|
|
||||||
~name="toContinuousPointSet",
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(
|
|
||||||
~name="toContinuousPointSet",
|
|
||||||
~inputs=[FRTypeArray(FRTypeRecord([("x", FRTypeNumeric), ("y", FRTypeNumeric)]))],
|
|
||||||
~run=(inputs, _) => inputsTodist(inputs, r => Continuous(Continuous.make(r))),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="toDiscretePointSet",
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(
|
|
||||||
~name="toDiscretePointSet",
|
|
||||||
~inputs=[FRTypeArray(FRTypeRecord([("x", FRTypeNumeric), ("y", FRTypeNumeric)]))],
|
|
||||||
~run=(inputs, _) => inputsTodist(inputs, r => Discrete(Discrete.make(r))),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Declaration",
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(~name="declareFn", ~inputs=[Declaration.frType], ~run=(inputs, _) => {
|
|
||||||
inputs->getOrError(0)->E.R.bind(Declaration.fromExpressionValue)
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Normal",
|
|
||||||
~examples=`normal(5,1)
|
|
||||||
normal({p5: 4, p95: 10})
|
|
||||||
normal({mean: 5, stdev: 2})`,
|
|
||||||
~definitions=[
|
|
||||||
TwoArgDist.make("normal", twoArgs(SymbolicDist.Normal.make)),
|
|
||||||
TwoArgDist.makeRecordP5P95("normal", r =>
|
|
||||||
twoArgs(SymbolicDist.Normal.from90PercentCI, r)->Ok
|
|
||||||
),
|
|
||||||
TwoArgDist.makeRecordMeanStdev("normal", twoArgs(SymbolicDist.Normal.make)),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Lognormal",
|
|
||||||
~examples=`lognormal(0.5, 0.8)
|
|
||||||
lognormal({p5: 4, p95: 10})
|
|
||||||
lognormal({mean: 5, stdev: 2})`,
|
|
||||||
~definitions=[
|
|
||||||
TwoArgDist.make("lognormal", twoArgs(SymbolicDist.Lognormal.make)),
|
|
||||||
TwoArgDist.makeRecordP5P95("lognormal", r =>
|
|
||||||
twoArgs(SymbolicDist.Lognormal.from90PercentCI, r)->Ok
|
|
||||||
),
|
|
||||||
TwoArgDist.makeRecordMeanStdev("lognormal", twoArgs(SymbolicDist.Lognormal.fromMeanAndStdev)),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Uniform",
|
|
||||||
~examples=`uniform(10, 12)`,
|
|
||||||
~definitions=[TwoArgDist.make("uniform", twoArgs(SymbolicDist.Uniform.make))],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Beta",
|
|
||||||
~examples=`beta(20, 25)
|
|
||||||
beta({mean: 0.39, stdev: 0.1})`,
|
|
||||||
~definitions=[
|
|
||||||
TwoArgDist.make("beta", twoArgs(SymbolicDist.Beta.make)),
|
|
||||||
TwoArgDist.makeRecordMeanStdev("beta", twoArgs(SymbolicDist.Beta.fromMeanAndStdev)),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Cauchy",
|
|
||||||
~examples=`cauchy(5, 1)`,
|
|
||||||
~definitions=[TwoArgDist.make("cauchy", twoArgs(SymbolicDist.Cauchy.make))],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Gamma",
|
|
||||||
~examples=`gamma(5, 1)`,
|
|
||||||
~definitions=[TwoArgDist.make("gamma", twoArgs(SymbolicDist.Gamma.make))],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Logistic",
|
|
||||||
~examples=`gamma(5, 1)`,
|
|
||||||
~definitions=[TwoArgDist.make("logistic", twoArgs(SymbolicDist.Logistic.make))],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="To (Distribution)",
|
|
||||||
~examples=`5 to 10
|
|
||||||
to(5,10)
|
|
||||||
-5 to 5`,
|
|
||||||
~definitions=[
|
|
||||||
TwoArgDist.make("to", twoArgs(SymbolicDist.From90thPercentile.make)),
|
|
||||||
TwoArgDist.make(
|
|
||||||
"credibleIntervalToDistribution",
|
|
||||||
twoArgs(SymbolicDist.From90thPercentile.make),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Exponential",
|
|
||||||
~examples=`exponential(2)`,
|
|
||||||
~definitions=[OneArgDist.make("exponential", SymbolicDist.Exponential.make)],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Bernoulli",
|
|
||||||
~examples=`bernoulli(0.5)`,
|
|
||||||
~definitions=[OneArgDist.make("bernoulli", SymbolicDist.Bernoulli.make)],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="PointMass",
|
|
||||||
~examples=`pointMass(0.5)`,
|
|
||||||
~definitions=[OneArgDist.make("pointMass", SymbolicDist.Float.makeSafe)],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="toContinuousPointSet",
|
|
||||||
~description="Converts a set of points to a continuous distribution",
|
|
||||||
~examples=`toContinuousPointSet([
|
|
||||||
{x: 0, y: 0.1},
|
|
||||||
{x: 1, y: 0.2},
|
|
||||||
{x: 2, y: 0.15},
|
|
||||||
{x: 3, y: 0.1}
|
|
||||||
])`,
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(
|
|
||||||
~name="toContinuousPointSet",
|
|
||||||
~inputs=[FRTypeArray(FRTypeRecord([("x", FRTypeNumeric), ("y", FRTypeNumeric)]))],
|
|
||||||
~run=(inputs, _) => inputsTodist(inputs, r => Continuous(Continuous.make(r))),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="toDiscretePointSet",
|
|
||||||
~description="Converts a set of points to a discrete distribution",
|
|
||||||
~examples=`toDiscretePointSet([
|
|
||||||
{x: 0, y: 0.1},
|
|
||||||
{x: 1, y: 0.2},
|
|
||||||
{x: 2, y: 0.15},
|
|
||||||
{x: 3, y: 0.1}
|
|
||||||
])`,
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(
|
|
||||||
~name="toDiscretePointSet",
|
|
||||||
~inputs=[FRTypeArray(FRTypeRecord([("x", FRTypeNumeric), ("y", FRTypeNumeric)]))],
|
|
||||||
~run=(inputs, _) => inputsTodist(inputs, r => Discrete(Discrete.make(r))),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Declaration (Continuous Function)",
|
|
||||||
~description="Adds metadata to a function of the input ranges. Works now for numeric and date inputs. This is useful when making predictions. It allows you to limit the domain that your prediction will be used and scored within.",
|
|
||||||
~examples=`declareFn({
|
|
||||||
fn: {|a,b| a },
|
|
||||||
inputs: [
|
|
||||||
{min: 0, max: 100},
|
|
||||||
{min: 30, max: 50}
|
|
||||||
]
|
|
||||||
})`,
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(~name="declareFn", ~inputs=[Declaration.frType], ~run=(inputs, _) => {
|
|
||||||
inputs->E.A.unsafe_get(0)->Declaration.fromExpressionValue
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
~isExperimental=true,
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Floor",
|
|
||||||
~definitions=[NumberToNumber.make("floor", Js.Math.floor_float)],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Ceiling",
|
|
||||||
~definitions=[NumberToNumber.make("ceil", Js.Math.ceil_float)],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Absolute Value",
|
|
||||||
~definitions=[NumberToNumber.make("abs", Js.Math.abs_float)],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(~name="Exponent", ~definitions=[NumberToNumber.make("exp", Js.Math.exp)], ()),
|
|
||||||
Function.make(~name="Log", ~definitions=[NumberToNumber.make("log", Js.Math.log)], ()),
|
|
||||||
Function.make(
|
|
||||||
~name="Log Base 10",
|
|
||||||
~definitions=[NumberToNumber.make("log10", Js.Math.log10)],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(~name="Log Base 2", ~definitions=[NumberToNumber.make("log2", Js.Math.log2)], ()),
|
|
||||||
Function.make(~name="Round", ~definitions=[NumberToNumber.make("round", Js.Math.round)], ()),
|
|
||||||
Function.make(
|
|
||||||
~name="Sum",
|
|
||||||
~definitions=[ArrayNumberDist.make("sum", r => r->E.A.Floats.sum->Wrappers.evNumber->Ok)],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Product",
|
|
||||||
~definitions=[
|
|
||||||
ArrayNumberDist.make("product", r => r->E.A.Floats.product->Wrappers.evNumber->Ok),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Min",
|
|
||||||
~definitions=[ArrayNumberDist.make("min", r => r->E.A.Floats.min->Wrappers.evNumber->Ok)],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Max",
|
|
||||||
~definitions=[ArrayNumberDist.make("max", r => r->E.A.Floats.max->Wrappers.evNumber->Ok)],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Mean",
|
|
||||||
~definitions=[ArrayNumberDist.make("mean", r => r->E.A.Floats.mean->Wrappers.evNumber->Ok)],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Geometric Mean",
|
|
||||||
~definitions=[
|
|
||||||
ArrayNumberDist.make("geomean", r => r->E.A.Floats.geomean->Wrappers.evNumber->Ok),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Standard Deviation",
|
|
||||||
~definitions=[ArrayNumberDist.make("stdev", r => r->E.A.Floats.stdev->Wrappers.evNumber->Ok)],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Variance",
|
|
||||||
~definitions=[
|
|
||||||
ArrayNumberDist.make("variance", r => r->E.A.Floats.stdev->Wrappers.evNumber->Ok),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="First",
|
|
||||||
~definitions=[
|
|
||||||
ArrayNumberDist.make2("first", r =>
|
|
||||||
r->E.A.first |> E.O.toResult(impossibleError) |> E.R.fmap(Wrappers.evNumber)
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Last",
|
|
||||||
~definitions=[
|
|
||||||
ArrayNumberDist.make2("last", r =>
|
|
||||||
r->E.A.last |> E.O.toResult(impossibleError) |> E.R.fmap(Wrappers.evNumber)
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Sort",
|
|
||||||
~definitions=[
|
|
||||||
ArrayNumberDist.make("sort", r =>
|
|
||||||
r->E.A.Floats.sort->E.A2.fmap(Wrappers.evNumber)->Wrappers.evArray->Ok
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Reverse",
|
|
||||||
~definitions=[
|
|
||||||
ArrayNumberDist.make("reverse", r =>
|
|
||||||
r->Belt_Array.reverse->E.A2.fmap(Wrappers.evNumber)->Wrappers.evArray->Ok
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Cumulative Sum",
|
|
||||||
~definitions=[
|
|
||||||
ArrayNumberDist.make("cumsum", r =>
|
|
||||||
r->E.A.Floats.cumsum->E.A2.fmap(Wrappers.evNumber)->Wrappers.evArray->Ok
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Cumulative Prod",
|
|
||||||
~definitions=[
|
|
||||||
ArrayNumberDist.make("cumprod", r =>
|
|
||||||
r->E.A.Floats.cumsum->E.A2.fmap(Wrappers.evNumber)->Wrappers.evArray->Ok
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Diff",
|
|
||||||
~definitions=[
|
|
||||||
ArrayNumberDist.make("diff", r =>
|
|
||||||
r->E.A.Floats.diff->E.A2.fmap(Wrappers.evNumber)->Wrappers.evArray->Ok
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Dict.merge",
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(
|
|
||||||
~name="merge",
|
|
||||||
~inputs=[FRTypeDict(FRTypeAny), FRTypeDict(FRTypeAny)],
|
|
||||||
~run=(inputs, _) => {
|
|
||||||
switch inputs {
|
|
||||||
| [FRValueDict(d1), FRValueDict(d2)] => {
|
|
||||||
let newDict =
|
|
||||||
E.Dict.concat(d1, d2) |> Js.Dict.map((. r) =>
|
|
||||||
FunctionRegistry_Core.FRType.matchReverse(r)
|
|
||||||
)
|
|
||||||
newDict->Js.Dict.entries->Belt.Map.String.fromArray->Wrappers.evRecord->Ok
|
|
||||||
}
|
|
||||||
| _ => Error(impossibleError)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
//TODO: Make sure that two functions can't have the same name. This causes chaos elsewhere.
|
|
||||||
Function.make(
|
|
||||||
~name="Dict.mergeMany",
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(~name="mergeMany", ~inputs=[FRTypeArray(FRTypeDict(FRTypeAny))], ~run=(
|
|
||||||
inputs,
|
|
||||||
_,
|
|
||||||
) =>
|
|
||||||
inputs
|
|
||||||
->Prepare.ToTypedArray.dicts
|
|
||||||
->E.R2.fmap(E.Dict.concatMany)
|
|
||||||
->E.R2.fmap(Js.Dict.map((. r) => FunctionRegistry_Core.FRType.matchReverse(r)))
|
|
||||||
->E.R2.fmap(r => r->Js.Dict.entries->Belt.Map.String.fromArray)
|
|
||||||
->E.R2.fmap(Wrappers.evRecord)
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Dict.keys",
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(~name="keys", ~inputs=[FRTypeDict(FRTypeAny)], ~run=(inputs, _) =>
|
|
||||||
switch inputs {
|
|
||||||
| [FRValueDict(d1)] => Js.Dict.keys(d1)->E.A2.fmap(Wrappers.evString)->Wrappers.evArray->Ok
|
|
||||||
| _ => Error(impossibleError)
|
|
||||||
}
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Dict.values",
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(~name="values", ~inputs=[FRTypeDict(FRTypeAny)], ~run=(inputs, _) =>
|
|
||||||
switch inputs {
|
|
||||||
| [FRValueDict(d1)] =>
|
|
||||||
Js.Dict.values(d1)
|
|
||||||
->E.A2.fmap(FunctionRegistry_Core.FRType.matchReverse)
|
|
||||||
->Wrappers.evArray
|
|
||||||
->Ok
|
|
||||||
| _ => Error(impossibleError)
|
|
||||||
}
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Dict.toList",
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(~name="dictToList", ~inputs=[FRTypeDict(FRTypeAny)], ~run=(inputs, _) =>
|
|
||||||
switch inputs {
|
|
||||||
| [FRValueDict(dict)] =>
|
|
||||||
dict
|
|
||||||
->Js.Dict.entries
|
|
||||||
->E.A2.fmap(((key, value)) =>
|
|
||||||
Wrappers.evArray([
|
|
||||||
Wrappers.evString(key),
|
|
||||||
FunctionRegistry_Core.FRType.matchReverse(value),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
->Wrappers.evArray
|
|
||||||
->Ok
|
|
||||||
| _ => Error(impossibleError)
|
|
||||||
}
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Dict.fromList",
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(~name="dictFromList", ~inputs=[FRTypeArray(FRTypeArray(FRTypeAny))], ~run=(
|
|
||||||
inputs,
|
|
||||||
_,
|
|
||||||
) => {
|
|
||||||
let convertInternalItems = items =>
|
|
||||||
items
|
|
||||||
->E.A2.fmap(item => {
|
|
||||||
switch item {
|
|
||||||
| [FRValueString(string), value] =>
|
|
||||||
(string, FunctionRegistry_Core.FRType.matchReverse(value))->Ok
|
|
||||||
| _ => Error(impossibleError)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
->E.A.R.firstErrorOrOpen
|
|
||||||
->E.R2.fmap(Belt.Map.String.fromArray)
|
|
||||||
->E.R2.fmap(Wrappers.evRecord)
|
|
||||||
inputs->getOrError(0)->E.R.bind(Prepare.ToValueArray.Array.arrayOfArrays)
|
|
||||||
|> E.R2.bind(convertInternalItems)
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="List.make",
|
|
||||||
~definitions=[
|
|
||||||
//Todo: If the second item is a function with no args, it could be nice to run this function and return the result.
|
|
||||||
FnDefinition.make(~name="listMake", ~inputs=[FRTypeNumber, FRTypeAny], ~run=(inputs, _) => {
|
|
||||||
switch inputs {
|
|
||||||
| [FRValueNumber(number), value] =>
|
|
||||||
Belt.Array.make(E.Float.toInt(number), value)
|
|
||||||
->E.A2.fmap(FunctionRegistry_Core.FRType.matchReverse)
|
|
||||||
->Wrappers.evArray
|
|
||||||
->Ok
|
|
||||||
| _ => Error(impossibleError)
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="upTo",
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(~name="upTo", ~inputs=[FRTypeNumber, FRTypeNumber], ~run=(inputs, _) =>
|
|
||||||
inputs
|
|
||||||
->Prepare.ToValueTuple.twoNumbers
|
|
||||||
->E.R2.fmap(((low, high)) =>
|
|
||||||
E.A.Floats.range(low, high, (high -. low +. 1.0)->E.Float.toInt)
|
|
||||||
->E.A2.fmap(Wrappers.evNumber)
|
|
||||||
->Wrappers.evArray
|
|
||||||
)
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
|
@ -0,0 +1,169 @@
|
||||||
|
open FunctionRegistry_Core
|
||||||
|
open FunctionRegistry_Helpers
|
||||||
|
|
||||||
|
let nameSpace = "Dict"
|
||||||
|
|
||||||
|
module Internals = {
|
||||||
|
type t = ReducerInterface_InternalExpressionValue.map
|
||||||
|
|
||||||
|
let keys = (a: t): internalExpressionValue => IEvArray(
|
||||||
|
Belt.Map.String.keysToArray(a)->E.A2.fmap(Wrappers.evString),
|
||||||
|
)
|
||||||
|
|
||||||
|
let values = (a: t): internalExpressionValue => IEvArray(Belt.Map.String.valuesToArray(a))
|
||||||
|
|
||||||
|
let toList = (a: t): internalExpressionValue =>
|
||||||
|
Belt.Map.String.toArray(a)
|
||||||
|
->E.A2.fmap(((key, value)) => Wrappers.evArray([IEvString(key), value]))
|
||||||
|
->Wrappers.evArray
|
||||||
|
|
||||||
|
let fromList = (items: array<internalExpressionValue>): result<internalExpressionValue, string> =>
|
||||||
|
items
|
||||||
|
->E.A2.fmap(item => {
|
||||||
|
switch (item: internalExpressionValue) {
|
||||||
|
| IEvArray([IEvString(string), value]) => (string, value)->Ok
|
||||||
|
| _ => Error(impossibleError)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
->E.A.R.firstErrorOrOpen
|
||||||
|
->E.R2.fmap(Belt.Map.String.fromArray)
|
||||||
|
->E.R2.fmap(Wrappers.evRecord)
|
||||||
|
|
||||||
|
let merge = (a: t, b: t): internalExpressionValue => IEvRecord(
|
||||||
|
Belt.Map.String.merge(a, b, (_, _, c) => c),
|
||||||
|
)
|
||||||
|
|
||||||
|
//Belt.Map.String has a function for mergeMany, but I couldn't understand how to use it yet.
|
||||||
|
let mergeMany = (a: array<t>): internalExpressionValue => {
|
||||||
|
let mergedValues =
|
||||||
|
a->E.A2.fmap(Belt.Map.String.toArray)->Belt.Array.concatMany->Belt.Map.String.fromArray
|
||||||
|
IEvRecord(mergedValues)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let library = [
|
||||||
|
Function.make(
|
||||||
|
~name="merge",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace=true,
|
||||||
|
~output=EvtRecord,
|
||||||
|
~examples=[`Dict.merge({a: 1, b: 2}, {c: 3, d: 4})`],
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="merge",
|
||||||
|
~inputs=[FRTypeDict(FRTypeAny), FRTypeDict(FRTypeAny)],
|
||||||
|
~run=(inputs, _, _) => {
|
||||||
|
switch inputs {
|
||||||
|
| [IEvRecord(d1), IEvRecord(d2)] => Internals.merge(d1, d2)->Ok
|
||||||
|
| _ => Error(impossibleError)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
//TODO: Change to use new mergeMany() function.
|
||||||
|
Function.make(
|
||||||
|
~name="mergeMany",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace=true,
|
||||||
|
~output=EvtRecord,
|
||||||
|
~examples=[`Dict.mergeMany([{a: 1, b: 2}, {c: 3, d: 4}])`],
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="mergeMany",
|
||||||
|
~inputs=[FRTypeArray(FRTypeDict(FRTypeAny))],
|
||||||
|
~run=(_, inputs, _) =>
|
||||||
|
inputs
|
||||||
|
->Prepare.ToTypedArray.dicts
|
||||||
|
->E.R2.fmap(E.Dict.concatMany)
|
||||||
|
->E.R2.fmap(Js.Dict.map((. r) => FunctionRegistry_Core.FRType.matchReverse(r)))
|
||||||
|
->E.R2.fmap(r => r->Js.Dict.entries->Belt.Map.String.fromArray)
|
||||||
|
->E.R2.fmap(Wrappers.evRecord),
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="keys",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace=true,
|
||||||
|
~output=EvtArray,
|
||||||
|
~examples=[`Dict.keys({a: 1, b: 2})`],
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="keys",
|
||||||
|
~inputs=[FRTypeDict(FRTypeAny)],
|
||||||
|
~run=(inputs, _, _) =>
|
||||||
|
switch inputs {
|
||||||
|
| [IEvRecord(d1)] => Internals.keys(d1)->Ok
|
||||||
|
| _ => Error(impossibleError)
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="values",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace=true,
|
||||||
|
~output=EvtArray,
|
||||||
|
~examples=[`Dict.values({a: 1, b: 2})`],
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="values",
|
||||||
|
~inputs=[FRTypeDict(FRTypeAny)],
|
||||||
|
~run=(inputs, _, _) =>
|
||||||
|
switch inputs {
|
||||||
|
| [IEvRecord(d1)] => Internals.values(d1)->Ok
|
||||||
|
| _ => Error(impossibleError)
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="toList",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace=true,
|
||||||
|
~output=EvtArray,
|
||||||
|
~examples=[`Dict.toList({a: 1, b: 2})`],
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="toList",
|
||||||
|
~inputs=[FRTypeDict(FRTypeAny)],
|
||||||
|
~run=(inputs, _, _) =>
|
||||||
|
switch inputs {
|
||||||
|
| [IEvRecord(dict)] => dict->Internals.toList->Ok
|
||||||
|
| _ => Error(impossibleError)
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="fromList",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace=true,
|
||||||
|
~output=EvtRecord,
|
||||||
|
~examples=[`Dict.fromList([["a", 1], ["b", 2]])`],
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="fromList",
|
||||||
|
~inputs=[FRTypeArray(FRTypeArray(FRTypeAny))],
|
||||||
|
~run=(inputs, _, _) =>
|
||||||
|
switch inputs {
|
||||||
|
| [IEvArray(items)] => Internals.fromList(items)
|
||||||
|
| _ => Error(impossibleError)
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,152 @@
|
||||||
|
open FunctionRegistry_Core
|
||||||
|
open FunctionRegistry_Helpers
|
||||||
|
let twoArgs = E.Tuple2.toFnCall
|
||||||
|
|
||||||
|
module DistributionCreation = {
|
||||||
|
let nameSpace = "Dist"
|
||||||
|
let output = ReducerInterface_InternalExpressionValue.EvtDistribution
|
||||||
|
let requiresNamespace = false
|
||||||
|
|
||||||
|
let fnMake = (~name, ~examples, ~definitions) => {
|
||||||
|
Function.make(~name, ~nameSpace, ~output, ~examples, ~definitions, ~requiresNamespace, ())
|
||||||
|
}
|
||||||
|
|
||||||
|
module TwoArgDist = {
|
||||||
|
let process = (~fn, ~env, r) =>
|
||||||
|
r
|
||||||
|
->E.R.bind(Process.DistOrNumberToDist.twoValuesUsingSymbolicDist(~fn, ~values=_, ~env))
|
||||||
|
->E.R2.fmap(Wrappers.evDistribution)
|
||||||
|
|
||||||
|
let make = (name, fn) => {
|
||||||
|
FnDefinition.make(
|
||||||
|
~name,
|
||||||
|
~inputs=[FRTypeDistOrNumber, FRTypeDistOrNumber],
|
||||||
|
~run=(_, inputs, env) => inputs->Prepare.ToValueTuple.twoDistOrNumber->process(~fn, ~env),
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let makeRecordP5P95 = (name, fn) => {
|
||||||
|
FnDefinition.make(
|
||||||
|
~name,
|
||||||
|
~inputs=[FRTypeRecord([("p5", FRTypeDistOrNumber), ("p95", FRTypeDistOrNumber)])],
|
||||||
|
~run=(_, inputs, env) =>
|
||||||
|
inputs->Prepare.ToValueTuple.Record.twoDistOrNumber->process(~fn, ~env),
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let makeRecordMeanStdev = (name, fn) => {
|
||||||
|
FnDefinition.make(
|
||||||
|
~name,
|
||||||
|
~inputs=[FRTypeRecord([("mean", FRTypeDistOrNumber), ("stdev", FRTypeDistOrNumber)])],
|
||||||
|
~run=(_, inputs, env) =>
|
||||||
|
inputs->Prepare.ToValueTuple.Record.twoDistOrNumber->process(~fn, ~env),
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module OneArgDist = {
|
||||||
|
let process = (~fn, ~env, r) =>
|
||||||
|
r
|
||||||
|
->E.R.bind(Process.DistOrNumberToDist.oneValueUsingSymbolicDist(~fn, ~value=_, ~env))
|
||||||
|
->E.R2.fmap(Wrappers.evDistribution)
|
||||||
|
|
||||||
|
let make = (name, fn) =>
|
||||||
|
FnDefinition.make(
|
||||||
|
~name,
|
||||||
|
~inputs=[FRTypeDistOrNumber],
|
||||||
|
~run=(_, inputs, env) => inputs->Prepare.ToValueTuple.oneDistOrNumber->process(~fn, ~env),
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let library = [
|
||||||
|
fnMake(
|
||||||
|
~name="normal",
|
||||||
|
~examples=["normal(5,1)", "normal({p5: 4, p95: 10})", "normal({mean: 5, stdev: 2})"],
|
||||||
|
~definitions=[
|
||||||
|
TwoArgDist.make("normal", twoArgs(SymbolicDist.Normal.make)),
|
||||||
|
TwoArgDist.makeRecordP5P95("normal", r =>
|
||||||
|
twoArgs(SymbolicDist.Normal.from90PercentCI, r)->Ok
|
||||||
|
),
|
||||||
|
TwoArgDist.makeRecordMeanStdev("normal", twoArgs(SymbolicDist.Normal.make)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
fnMake(
|
||||||
|
~name="lognormal",
|
||||||
|
~examples=[
|
||||||
|
"lognormal(0.5, 0.8)",
|
||||||
|
"lognormal({p5: 4, p95: 10})",
|
||||||
|
"lognormal({mean: 5, stdev: 2})",
|
||||||
|
],
|
||||||
|
~definitions=[
|
||||||
|
TwoArgDist.make("lognormal", twoArgs(SymbolicDist.Lognormal.make)),
|
||||||
|
TwoArgDist.makeRecordP5P95("lognormal", r =>
|
||||||
|
twoArgs(SymbolicDist.Lognormal.from90PercentCI, r)->Ok
|
||||||
|
),
|
||||||
|
TwoArgDist.makeRecordMeanStdev(
|
||||||
|
"lognormal",
|
||||||
|
twoArgs(SymbolicDist.Lognormal.fromMeanAndStdev),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
fnMake(
|
||||||
|
~name="uniform",
|
||||||
|
~examples=[`uniform(10, 12)`],
|
||||||
|
~definitions=[TwoArgDist.make("uniform", twoArgs(SymbolicDist.Uniform.make))],
|
||||||
|
),
|
||||||
|
fnMake(
|
||||||
|
~name="beta",
|
||||||
|
~examples=[`beta(20, 25)`, `beta({mean: 0.39, stdev: 0.1})`],
|
||||||
|
~definitions=[
|
||||||
|
TwoArgDist.make("beta", twoArgs(SymbolicDist.Beta.make)),
|
||||||
|
TwoArgDist.makeRecordMeanStdev("beta", twoArgs(SymbolicDist.Beta.fromMeanAndStdev)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
fnMake(
|
||||||
|
~name="cauchy",
|
||||||
|
~examples=[`cauchy(5, 1)`],
|
||||||
|
~definitions=[TwoArgDist.make("cauchy", twoArgs(SymbolicDist.Cauchy.make))],
|
||||||
|
),
|
||||||
|
fnMake(
|
||||||
|
~name="gamma",
|
||||||
|
~examples=[`gamma(5, 1)`],
|
||||||
|
~definitions=[TwoArgDist.make("gamma", twoArgs(SymbolicDist.Gamma.make))],
|
||||||
|
),
|
||||||
|
fnMake(
|
||||||
|
~name="logistic",
|
||||||
|
~examples=[`logistic(5, 1)`],
|
||||||
|
~definitions=[TwoArgDist.make("logistic", twoArgs(SymbolicDist.Logistic.make))],
|
||||||
|
),
|
||||||
|
fnMake(
|
||||||
|
~name="to (distribution)",
|
||||||
|
~examples=[`5 to 10`, `to(5,10)`, `-5 to 5`],
|
||||||
|
~definitions=[
|
||||||
|
TwoArgDist.make("to", twoArgs(SymbolicDist.From90thPercentile.make)),
|
||||||
|
TwoArgDist.make(
|
||||||
|
"credibleIntervalToDistribution",
|
||||||
|
twoArgs(SymbolicDist.From90thPercentile.make),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
fnMake(
|
||||||
|
~name="exponential",
|
||||||
|
~examples=[`exponential(2)`],
|
||||||
|
~definitions=[OneArgDist.make("exponential", SymbolicDist.Exponential.make)],
|
||||||
|
),
|
||||||
|
fnMake(
|
||||||
|
~name="bernoulli",
|
||||||
|
~examples=[`bernoulli(0.5)`],
|
||||||
|
~definitions=[OneArgDist.make("bernoulli", SymbolicDist.Bernoulli.make)],
|
||||||
|
),
|
||||||
|
fnMake(
|
||||||
|
~name="pointMass",
|
||||||
|
~examples=[`pointMass(0.5)`],
|
||||||
|
~definitions=[OneArgDist.make("pointMass", SymbolicDist.Float.makeSafe)],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
let library = DistributionCreation.library
|
|
@ -0,0 +1,62 @@
|
||||||
|
open FunctionRegistry_Core
|
||||||
|
open FunctionRegistry_Helpers
|
||||||
|
|
||||||
|
module Declaration = {
|
||||||
|
let frType = FRTypeRecord([
|
||||||
|
("fn", FRTypeLambda),
|
||||||
|
("inputs", FRTypeArray(FRTypeRecord([("min", FRTypeNumber), ("max", FRTypeNumber)]))),
|
||||||
|
])
|
||||||
|
|
||||||
|
let fromExpressionValue = (e: frValue): result<internalExpressionValue, string> => {
|
||||||
|
switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.twoArgs([e]) {
|
||||||
|
| Ok([FRValueLambda(lambda), FRValueArray(inputs)]) => {
|
||||||
|
open FunctionRegistry_Helpers.Prepare
|
||||||
|
let getMinMax = arg =>
|
||||||
|
ToValueArray.Record.toArgs([arg])
|
||||||
|
->E.R.bind(ToValueTuple.twoNumbers)
|
||||||
|
->E.R2.fmap(((min, max)) => Declaration.ContinuousFloatArg.make(min, max))
|
||||||
|
inputs
|
||||||
|
->E.A2.fmap(getMinMax)
|
||||||
|
->E.A.R.firstErrorOrOpen
|
||||||
|
->E.R2.fmap(args => ReducerInterface_InternalExpressionValue.IEvDeclaration(
|
||||||
|
Declaration.make(lambda, args),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
| Error(r) => Error(r)
|
||||||
|
| Ok(_) => Error(FunctionRegistry_Helpers.impossibleError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let nameSpace = "Function"
|
||||||
|
|
||||||
|
let library = [
|
||||||
|
Function.make(
|
||||||
|
~name="declare",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace=true,
|
||||||
|
~output=EvtDeclaration,
|
||||||
|
~description="Adds metadata to a function of the input ranges. Works now for numeric and date inputs. This is useful when making predictions. It allows you to limit the domain that your prediction will be used and scored within.",
|
||||||
|
~examples=[
|
||||||
|
`Function.declare({
|
||||||
|
fn: {|a,b| a },
|
||||||
|
inputs: [
|
||||||
|
{min: 0, max: 100},
|
||||||
|
{min: 30, max: 50}
|
||||||
|
]
|
||||||
|
})`,
|
||||||
|
],
|
||||||
|
~isExperimental=true,
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="declare",
|
||||||
|
~inputs=[Declaration.frType],
|
||||||
|
~run=(_, inputs, _) => {
|
||||||
|
inputs->getOrError(0)->E.R.bind(Declaration.fromExpressionValue)
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,128 @@
|
||||||
|
open FunctionRegistry_Core
|
||||||
|
open FunctionRegistry_Helpers
|
||||||
|
|
||||||
|
let nameSpace = "List"
|
||||||
|
let requiresNamespace = true
|
||||||
|
|
||||||
|
module Internals = {
|
||||||
|
let makeFromNumber = (
|
||||||
|
n: float,
|
||||||
|
value: internalExpressionValue,
|
||||||
|
): internalExpressionValue => IEvArray(Belt.Array.make(E.Float.toInt(n), value))
|
||||||
|
|
||||||
|
let upTo = (low: float, high: float): internalExpressionValue => IEvArray(
|
||||||
|
E.A.Floats.range(low, high, (high -. low +. 1.0)->E.Float.toInt)->E.A2.fmap(Wrappers.evNumber),
|
||||||
|
)
|
||||||
|
|
||||||
|
let first = (v: array<internalExpressionValue>): result<internalExpressionValue, string> =>
|
||||||
|
v->E.A.first |> E.O.toResult("No first element")
|
||||||
|
|
||||||
|
let last = (v: array<internalExpressionValue>): result<internalExpressionValue, string> =>
|
||||||
|
v->E.A.last |> E.O.toResult("No last element")
|
||||||
|
|
||||||
|
let reverse = (array: array<internalExpressionValue>): internalExpressionValue => IEvArray(
|
||||||
|
Belt.Array.reverse(array),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let library = [
|
||||||
|
Function.make(
|
||||||
|
~name="make",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtArray,
|
||||||
|
~examples=[`List.make(2, "testValue")`],
|
||||||
|
~definitions=[
|
||||||
|
//Todo: If the second item is a function with no args, it could be nice to run this function and return the result.
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="make",
|
||||||
|
~inputs=[FRTypeNumber, FRTypeAny],
|
||||||
|
~run=(inputs, _, _) => {
|
||||||
|
switch inputs {
|
||||||
|
| [IEvNumber(number), value] => Internals.makeFromNumber(number, value)->Ok
|
||||||
|
| _ => Error(impossibleError)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="upTo",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtArray,
|
||||||
|
~examples=[`List.upTo(1,4)`],
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="upTo",
|
||||||
|
~inputs=[FRTypeNumber, FRTypeNumber],
|
||||||
|
~run=(_, inputs, _) =>
|
||||||
|
inputs
|
||||||
|
->Prepare.ToValueTuple.twoNumbers
|
||||||
|
->E.R2.fmap(((low, high)) => Internals.upTo(low, high)),
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="first",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~examples=[`List.first([1,4,5])`],
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="first",
|
||||||
|
~inputs=[FRTypeArray(FRTypeAny)],
|
||||||
|
~run=(inputs, _, _) =>
|
||||||
|
switch inputs {
|
||||||
|
| [IEvArray(array)] => Internals.first(array)
|
||||||
|
| _ => Error(impossibleError)
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="last",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~examples=[`List.last([1,4,5])`],
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="last",
|
||||||
|
~inputs=[FRTypeArray(FRTypeAny)],
|
||||||
|
~run=(inputs, _, _) =>
|
||||||
|
switch inputs {
|
||||||
|
| [IEvArray(array)] => Internals.last(array)
|
||||||
|
| _ => Error(impossibleError)
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="reverse",
|
||||||
|
~nameSpace,
|
||||||
|
~output=EvtArray,
|
||||||
|
~requiresNamespace=false,
|
||||||
|
~examples=[`List.reverse([1,4,5])`],
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="reverse",
|
||||||
|
~inputs=[FRTypeArray(FRTypeAny)],
|
||||||
|
~run=(inputs, _, _) =>
|
||||||
|
switch inputs {
|
||||||
|
| [IEvArray(array)] => Internals.reverse(array)->Ok
|
||||||
|
| _ => Error(impossibleError)
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,251 @@
|
||||||
|
open FunctionRegistry_Core
|
||||||
|
open FunctionRegistry_Helpers
|
||||||
|
|
||||||
|
let nameSpace = "Number"
|
||||||
|
let requiresNamespace = false
|
||||||
|
|
||||||
|
module NumberToNumber = {
|
||||||
|
let make = (name, fn) =>
|
||||||
|
FnDefinition.make(
|
||||||
|
~name,
|
||||||
|
~inputs=[FRTypeNumber],
|
||||||
|
~run=(_, inputs, _) => {
|
||||||
|
inputs
|
||||||
|
->getOrError(0)
|
||||||
|
->E.R.bind(Prepare.oneNumber)
|
||||||
|
->E.R2.fmap(fn)
|
||||||
|
->E.R2.fmap(Wrappers.evNumber)
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
module ArrayNumberDist = {
|
||||||
|
let make = (name, fn) => {
|
||||||
|
FnDefinition.make(
|
||||||
|
~name,
|
||||||
|
~inputs=[FRTypeArray(FRTypeNumber)],
|
||||||
|
~run=(_, inputs, _) =>
|
||||||
|
Prepare.ToTypedArray.numbers(inputs)
|
||||||
|
->E.R.bind(r => E.A.length(r) === 0 ? Error("List is empty") : Ok(r))
|
||||||
|
->E.R.bind(fn),
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
let make2 = (name, fn) => {
|
||||||
|
FnDefinition.make(
|
||||||
|
~name,
|
||||||
|
~inputs=[FRTypeArray(FRTypeAny)],
|
||||||
|
~run=(_, inputs, _) =>
|
||||||
|
Prepare.ToTypedArray.numbers(inputs)
|
||||||
|
->E.R.bind(r => E.A.length(r) === 0 ? Error("List is empty") : Ok(r))
|
||||||
|
->E.R.bind(fn),
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let library = [
|
||||||
|
Function.make(
|
||||||
|
~name="floor",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`floor(3.5)`],
|
||||||
|
~definitions=[NumberToNumber.make("floor", Js.Math.floor_float)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="ceiling",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`ceil(3.5)`],
|
||||||
|
~definitions=[NumberToNumber.make("ceil", Js.Math.ceil_float)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="absolute value",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`abs(3.5)`],
|
||||||
|
~definitions=[NumberToNumber.make("abs", Js.Math.abs_float)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="exponent",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`exp(3.5)`],
|
||||||
|
~definitions=[NumberToNumber.make("exp", Js.Math.exp)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="log",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`log(3.5)`],
|
||||||
|
~definitions=[NumberToNumber.make("log", Js.Math.log)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="log base 10",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`log10(3.5)`],
|
||||||
|
~definitions=[NumberToNumber.make("log10", Js.Math.log10)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="log base 2",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`log2(3.5)`],
|
||||||
|
~definitions=[NumberToNumber.make("log2", Js.Math.log2)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="round",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`round(3.5)`],
|
||||||
|
~definitions=[NumberToNumber.make("round", Js.Math.round)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="sum",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`sum([3,5,2])`],
|
||||||
|
~definitions=[ArrayNumberDist.make("sum", r => r->E.A.Floats.sum->Wrappers.evNumber->Ok)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="product",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`product([3,5,2])`],
|
||||||
|
~definitions=[
|
||||||
|
ArrayNumberDist.make("product", r => r->E.A.Floats.product->Wrappers.evNumber->Ok),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="min",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`min([3,5,2])`],
|
||||||
|
~definitions=[ArrayNumberDist.make("min", r => r->E.A.Floats.min->Wrappers.evNumber->Ok)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="max",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`max([3,5,2])`],
|
||||||
|
~definitions=[ArrayNumberDist.make("max", r => r->E.A.Floats.max->Wrappers.evNumber->Ok)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="mean",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`mean([3,5,2])`],
|
||||||
|
~definitions=[ArrayNumberDist.make("mean", r => r->E.A.Floats.mean->Wrappers.evNumber->Ok)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="geometric mean",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`geomean([3,5,2])`],
|
||||||
|
~definitions=[
|
||||||
|
ArrayNumberDist.make("geomean", r => r->E.A.Floats.geomean->Wrappers.evNumber->Ok),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="standard deviation",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`stdev([3,5,2,3,5])`],
|
||||||
|
~definitions=[ArrayNumberDist.make("stdev", r => r->E.A.Floats.stdev->Wrappers.evNumber->Ok)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="variance",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`variance([3,5,2,3,5])`],
|
||||||
|
~definitions=[
|
||||||
|
ArrayNumberDist.make("variance", r => r->E.A.Floats.variance->Wrappers.evNumber->Ok),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="sort",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtArray,
|
||||||
|
~examples=[`sort([3,5,2,3,5])`],
|
||||||
|
~definitions=[
|
||||||
|
ArrayNumberDist.make("sort", r =>
|
||||||
|
r->E.A.Floats.sort->E.A2.fmap(Wrappers.evNumber)->Wrappers.evArray->Ok
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="cumulative sum",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtArray,
|
||||||
|
~examples=[`cumsum([3,5,2,3,5])`],
|
||||||
|
~definitions=[
|
||||||
|
ArrayNumberDist.make("cumsum", r =>
|
||||||
|
r->E.A.Floats.cumSum->E.A2.fmap(Wrappers.evNumber)->Wrappers.evArray->Ok
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="cumulative prod",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtArray,
|
||||||
|
~examples=[`cumprod([3,5,2,3,5])`],
|
||||||
|
~definitions=[
|
||||||
|
ArrayNumberDist.make("cumprod", r =>
|
||||||
|
r->E.A.Floats.cumProd->E.A2.fmap(Wrappers.evNumber)->Wrappers.evArray->Ok
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="diff",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtArray,
|
||||||
|
~examples=[`diff([3,5,2,3,5])`],
|
||||||
|
~definitions=[
|
||||||
|
ArrayNumberDist.make("diff", r =>
|
||||||
|
r->E.A.Floats.diff->E.A2.fmap(Wrappers.evNumber)->Wrappers.evArray->Ok
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,73 @@
|
||||||
|
open FunctionRegistry_Core
|
||||||
|
open FunctionRegistry_Helpers
|
||||||
|
|
||||||
|
let nameSpace = "Pointset"
|
||||||
|
let requiresNamespace = true
|
||||||
|
|
||||||
|
let inputsTodist = (inputs: array<FunctionRegistry_Core.frValue>, makeDist) => {
|
||||||
|
let array = inputs->getOrError(0)->E.R.bind(Prepare.ToValueArray.Array.openA)
|
||||||
|
let xyCoords =
|
||||||
|
array->E.R.bind(xyCoords =>
|
||||||
|
xyCoords
|
||||||
|
->E.A2.fmap(xyCoord =>
|
||||||
|
[xyCoord]->Prepare.ToValueArray.Record.twoArgs->E.R.bind(Prepare.ToValueTuple.twoNumbers)
|
||||||
|
)
|
||||||
|
->E.A.R.firstErrorOrOpen
|
||||||
|
)
|
||||||
|
let expressionValue =
|
||||||
|
xyCoords
|
||||||
|
->E.R.bind(r => r->XYShape.T.makeFromZipped->E.R2.errMap(XYShape.Error.toString))
|
||||||
|
->E.R2.fmap(r => ReducerInterface_InternalExpressionValue.IEvDistribution(
|
||||||
|
PointSet(makeDist(r)),
|
||||||
|
))
|
||||||
|
expressionValue
|
||||||
|
}
|
||||||
|
|
||||||
|
let library = [
|
||||||
|
Function.make(
|
||||||
|
~name="makeContinuous",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~examples=[
|
||||||
|
`Pointset.makeContinuous([
|
||||||
|
{x: 0, y: 0.2},
|
||||||
|
{x: 1, y: 0.7},
|
||||||
|
{x: 2, y: 0.8},
|
||||||
|
{x: 3, y: 0.2}
|
||||||
|
])`,
|
||||||
|
],
|
||||||
|
~output=ReducerInterface_InternalExpressionValue.EvtDistribution,
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="makeContinuous",
|
||||||
|
~inputs=[FRTypeArray(FRTypeRecord([("x", FRTypeNumeric), ("y", FRTypeNumeric)]))],
|
||||||
|
~run=(_, inputs, _) => inputsTodist(inputs, r => Continuous(Continuous.make(r))),
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="makeDiscrete",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~examples=[
|
||||||
|
`Pointset.makeDiscrete([
|
||||||
|
{x: 0, y: 0.2},
|
||||||
|
{x: 1, y: 0.7},
|
||||||
|
{x: 2, y: 0.8},
|
||||||
|
{x: 3, y: 0.2}
|
||||||
|
])`,
|
||||||
|
],
|
||||||
|
~output=ReducerInterface_InternalExpressionValue.EvtDistribution,
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="makeDiscrete",
|
||||||
|
~inputs=[FRTypeArray(FRTypeRecord([("x", FRTypeNumeric), ("y", FRTypeNumeric)]))],
|
||||||
|
~run=(_, inputs, _) => inputsTodist(inputs, r => Discrete(Discrete.make(r))),
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,89 @@
|
||||||
|
open FunctionRegistry_Core
|
||||||
|
|
||||||
|
let nameSpace = "Dist"
|
||||||
|
let requiresNamespace = true
|
||||||
|
|
||||||
|
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 library = [
|
||||||
|
Function.make(
|
||||||
|
~name="logScore",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[
|
||||||
|
"Dist.logScore({estimate: normal(5,2), answer: normal(5.2,1), prior: normal(5.5,3)})",
|
||||||
|
"Dist.logScore({estimate: normal(5,2), answer: normal(5.2,1)})",
|
||||||
|
"Dist.logScore({estimate: normal(5,2), answer: 4.5})",
|
||||||
|
],
|
||||||
|
~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)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="klDivergence",
|
||||||
|
~nameSpace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~requiresNamespace,
|
||||||
|
~examples=["Dist.klDivergence(normal(5,2), normal(5,1.5))"],
|
||||||
|
~definitions=[
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
]
|
|
@ -1,28 +1,9 @@
|
||||||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||||
|
|
||||||
type internalExpressionValue = InternalExpressionValue.t
|
type internalExpressionValue = InternalExpressionValue.t
|
||||||
|
|
||||||
// module Sample = {
|
|
||||||
// // In real life real libraries should be somewhere else
|
|
||||||
// /*
|
|
||||||
// For an example of mapping polymorphic custom functions. To be deleted after real integration
|
|
||||||
// */
|
|
||||||
// let customAdd = (a: float, b: float): float => {a +. b}
|
|
||||||
// }
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Map external calls of Reducer
|
Map external calls of Reducer
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// I expect that it's important to build this first, so it doesn't get recalculated for each tryRegistry() call.
|
|
||||||
let registry = FunctionRegistry_Library.registry
|
|
||||||
|
|
||||||
let tryRegistry = ((fnName, args): InternalExpressionValue.functionCall, env) => {
|
|
||||||
FunctionRegistry_Core.Registry.matchAndRun(~registry, ~fnName, ~args, ~env)->E.O2.fmap(
|
|
||||||
E.R2.errMap(_, s => Reducer_ErrorValue.RETodo(s)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
let dispatch = (call: InternalExpressionValue.functionCall, environment, reducer, chain): result<
|
let dispatch = (call: InternalExpressionValue.functionCall, environment, reducer, chain): result<
|
||||||
internalExpressionValue,
|
internalExpressionValue,
|
||||||
'e,
|
'e,
|
||||||
|
@ -32,9 +13,10 @@ let dispatch = (call: InternalExpressionValue.functionCall, environment, reducer
|
||||||
() => ReducerInterface_Date.dispatch(call, environment),
|
() => ReducerInterface_Date.dispatch(call, environment),
|
||||||
() => ReducerInterface_Duration.dispatch(call, environment),
|
() => ReducerInterface_Duration.dispatch(call, environment),
|
||||||
() => ReducerInterface_Number.dispatch(call, environment),
|
() => ReducerInterface_Number.dispatch(call, environment),
|
||||||
() => tryRegistry(call, environment),
|
() => FunctionRegistry_Library.dispatch(call, environment),
|
||||||
])->E.O2.default(chain(call, environment, reducer))
|
])->E.O2.default(chain(call, environment, reducer))
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
If your dispatch is too big you can divide it into smaller dispatches and pass the call so that it gets called finally.
|
If your dispatch is too big you can divide it into smaller dispatches and pass the call so that it gets called finally.
|
||||||
|
|
||||||
|
|
|
@ -160,6 +160,26 @@ let valueToValueType = value =>
|
||||||
| IEvTypeIdentifier(_) => EvtTypeIdentifier
|
| IEvTypeIdentifier(_) => EvtTypeIdentifier
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let externalValueToValueType = (value: ExternalExpressionValue.t) =>
|
||||||
|
switch value {
|
||||||
|
| EvArray(_) => EvtArray
|
||||||
|
| EvArrayString(_) => EvtArrayString
|
||||||
|
| EvBool(_) => EvtBool
|
||||||
|
| EvCall(_) => EvtCall
|
||||||
|
| EvDate(_) => EvtDate
|
||||||
|
| EvDeclaration(_) => EvtDeclaration
|
||||||
|
| EvDistribution(_) => EvtDistribution
|
||||||
|
| EvLambda(_) => EvtLambda
|
||||||
|
| EvModule(_) => EvtModule
|
||||||
|
| EvNumber(_) => EvtNumber
|
||||||
|
| EvRecord(_) => EvtRecord
|
||||||
|
| EvString(_) => EvtString
|
||||||
|
| EvSymbol(_) => EvtSymbol
|
||||||
|
| EvTimeDuration(_) => EvtTimeDuration
|
||||||
|
| EvType(_) => EvtType
|
||||||
|
| EvTypeIdentifier(_) => EvtTypeIdentifier
|
||||||
|
}
|
||||||
|
|
||||||
let functionCallToCallSignature = (functionCall: functionCall): functionCallSignature => {
|
let functionCallToCallSignature = (functionCall: functionCall): functionCallSignature => {
|
||||||
let (fn, args) = functionCall
|
let (fn, args) = functionCall
|
||||||
CallSignature(fn, args->Js.Array2.map(valueToValueType))
|
CallSignature(fn, args->Js.Array2.map(valueToValueType))
|
||||||
|
|
|
@ -765,7 +765,7 @@ module A = {
|
||||||
let diff = (t: t): array<float> =>
|
let diff = (t: t): array<float> =>
|
||||||
Belt.Array.zipBy(t, Belt.Array.sliceToEnd(t, 1), (left, right) => right -. left)
|
Belt.Array.zipBy(t, Belt.Array.sliceToEnd(t, 1), (left, right) => right -. left)
|
||||||
|
|
||||||
let cumsum = (t: t): array<float> => accumulate((a, b) => a +. b, t)
|
let cumSum = (t: t): array<float> => accumulate((a, b) => a +. b, t)
|
||||||
let cumProd = (t: t): array<float> => accumulate((a, b) => a *. b, t)
|
let cumProd = (t: t): array<float> => accumulate((a, b) => a *. b, t)
|
||||||
|
|
||||||
exception RangeError(string)
|
exception RangeError(string)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user