Converting final files to rescript
This commit is contained in:
parent
3019a58962
commit
9463914204
|
@ -1,6 +1,6 @@
|
|||
open Jest;
|
||||
open Expect;
|
||||
/*
|
||||
open Jest
|
||||
open Expect
|
||||
/*
|
||||
let makeTest = (~only=false, str, item1, item2) =>
|
||||
only
|
||||
? Only.test(str, () =>
|
|
@ -1,25 +0,0 @@
|
|||
open Jest;
|
||||
open Expect;
|
||||
|
||||
let makeTest = (~only=false, str, item1, item2) =>
|
||||
only
|
||||
? Only.test(str, () =>
|
||||
expect(item1) |> toEqual(item2)
|
||||
)
|
||||
: test(str, () =>
|
||||
expect(item1) |> toEqual(item2)
|
||||
);
|
||||
|
||||
describe("Lodash", () => {
|
||||
describe("Lodash", () => {
|
||||
makeTest(~only=true, "Foo", Jstat.Normal.mean(5.0,2.0), 5.0);
|
||||
makeTest("min", Lodash.min([|1, 3, 4|]), 1);
|
||||
makeTest("max", Lodash.max([|1, 3, 4|]), 4);
|
||||
makeTest("uniq", Lodash.uniq([|1, 3, 4, 4|]), [|1, 3, 4|]);
|
||||
makeTest(
|
||||
"countBy",
|
||||
Lodash.countBy([|1, 3, 4, 4|], r => r),
|
||||
Js.Dict.fromArray([|("1", 1), ("3", 1), ("4", 2)|]),
|
||||
);
|
||||
})
|
||||
});
|
20
packages/squiggle-lang/__tests__/Lodash__test.res
Normal file
20
packages/squiggle-lang/__tests__/Lodash__test.res
Normal file
|
@ -0,0 +1,20 @@
|
|||
open Jest
|
||||
open Expect
|
||||
|
||||
let makeTest = (~only=false, str, item1, item2) =>
|
||||
only
|
||||
? Only.test(str, () => expect(item1) |> toEqual(item2))
|
||||
: test(str, () => expect(item1) |> toEqual(item2))
|
||||
|
||||
describe("Lodash", () =>
|
||||
describe("Lodash", () => {
|
||||
makeTest("min", Lodash.min([1, 3, 4]), 1)
|
||||
makeTest("max", Lodash.max([1, 3, 4]), 4)
|
||||
makeTest("uniq", Lodash.uniq([1, 3, 4, 4]), [1, 3, 4])
|
||||
makeTest(
|
||||
"countBy",
|
||||
Lodash.countBy([1, 3, 4, 4], r => r),
|
||||
Js.Dict.fromArray([("1", 1), ("3", 1), ("4", 2)]),
|
||||
)
|
||||
})
|
||||
)
|
|
@ -1,258 +0,0 @@
|
|||
open TypeSystem;
|
||||
|
||||
let wrongInputsError = (r: array(typedValue)) => {
|
||||
let inputs = r |> E.A.fmap(TypedValue.toString) |>Js.String.concatMany(_, ",");
|
||||
Js.log3("Inputs were", inputs, r);
|
||||
Error("Wrong inputs. The inputs were:" ++ inputs);
|
||||
};
|
||||
|
||||
let to_: (float, float) => result(node, string) =
|
||||
(low, high) =>
|
||||
switch (low, high) {
|
||||
| (low, high) when low <= 0.0 && low < high =>
|
||||
Ok(`SymbolicDist(SymbolicDist.Normal.from90PercentCI(low, high)))
|
||||
| (low, high) when low < high =>
|
||||
Ok(`SymbolicDist(SymbolicDist.Lognormal.from90PercentCI(low, high)))
|
||||
| (_, _) => Error("Low value must be less than high value.")
|
||||
};
|
||||
|
||||
let makeSymbolicFromTwoFloats = (name, fn) =>
|
||||
Function.T.make(
|
||||
~name,
|
||||
~outputType=`SamplingDistribution,
|
||||
~inputTypes=[|`Float, `Float|],
|
||||
~run=
|
||||
fun
|
||||
| [|`Float(a), `Float(b)|] => Ok(`SymbolicDist(fn(a, b)))
|
||||
| e => wrongInputsError(e),
|
||||
(),
|
||||
);
|
||||
|
||||
let makeSymbolicFromOneFloat = (name, fn) =>
|
||||
Function.T.make(
|
||||
~name,
|
||||
~outputType=`SamplingDistribution,
|
||||
~inputTypes=[|`Float|],
|
||||
~run=
|
||||
fun
|
||||
| [|`Float(a)|] => Ok(`SymbolicDist(fn(a)))
|
||||
| e => wrongInputsError(e),
|
||||
(),
|
||||
);
|
||||
|
||||
let makeDistFloat = (name, fn) =>
|
||||
Function.T.make(
|
||||
~name,
|
||||
~outputType=`SamplingDistribution,
|
||||
~inputTypes=[|`SamplingDistribution, `Float|],
|
||||
~run=
|
||||
fun
|
||||
| [|`SamplingDist(a), `Float(b)|] => fn(a, b)
|
||||
| [|`RenderedDist(a), `Float(b)|] => fn(`RenderedDist(a), b)
|
||||
| e => wrongInputsError(e),
|
||||
(),
|
||||
);
|
||||
|
||||
let makeRenderedDistFloat = (name, fn) =>
|
||||
Function.T.make(
|
||||
~name,
|
||||
~outputType=`RenderedDistribution,
|
||||
~inputTypes=[|`RenderedDistribution, `Float|],
|
||||
~shouldCoerceTypes=true,
|
||||
~run=
|
||||
fun
|
||||
| [|`RenderedDist(a), `Float(b)|] => fn(a, b)
|
||||
| e => wrongInputsError(e),
|
||||
(),
|
||||
);
|
||||
|
||||
let makeDist = (name, fn) =>
|
||||
Function.T.make(
|
||||
~name,
|
||||
~outputType=`SamplingDistribution,
|
||||
~inputTypes=[|`SamplingDistribution|],
|
||||
~run=
|
||||
fun
|
||||
| [|`SamplingDist(a)|] => fn(a)
|
||||
| [|`RenderedDist(a)|] => fn(`RenderedDist(a))
|
||||
| e => wrongInputsError(e),
|
||||
(),
|
||||
);
|
||||
|
||||
let floatFromDist =
|
||||
(
|
||||
distToFloatOp: ExpressionTypes.distToFloatOperation,
|
||||
t: TypeSystem.samplingDist,
|
||||
)
|
||||
: result(node, string) => {
|
||||
switch (t) {
|
||||
| `SymbolicDist(s) =>
|
||||
SymbolicDist.T.operate(distToFloatOp, s)
|
||||
|> E.R.bind(_, v => Ok(`SymbolicDist(`Float(v))))
|
||||
| `RenderedDist(rs) =>
|
||||
Shape.operate(distToFloatOp, rs) |> (v => Ok(`SymbolicDist(`Float(v))))
|
||||
};
|
||||
};
|
||||
|
||||
let verticalScaling = (scaleOp, rs, scaleBy) => {
|
||||
// scaleBy has to be a single float, otherwise we'll return an error.
|
||||
let fn = (secondary, main) =>
|
||||
Operation.Scale.toFn(scaleOp, main, secondary);
|
||||
let integralSumCacheFn = Operation.Scale.toIntegralSumCacheFn(scaleOp);
|
||||
let integralCacheFn = Operation.Scale.toIntegralCacheFn(scaleOp);
|
||||
Ok(
|
||||
`RenderedDist(
|
||||
Shape.T.mapY(
|
||||
~integralSumCacheFn=integralSumCacheFn(scaleBy),
|
||||
~integralCacheFn=integralCacheFn(scaleBy),
|
||||
~fn=fn(scaleBy),
|
||||
rs,
|
||||
),
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
module Multimodal = {
|
||||
let getByNameResult = ExpressionTypes.ExpressionTree.Hash.getByNameResult;
|
||||
|
||||
let _paramsToDistsAndWeights = (r: array(typedValue)) =>
|
||||
switch (r) {
|
||||
| [|`Hash(r)|] =>
|
||||
let dists =
|
||||
getByNameResult(r, "dists")
|
||||
->E.R.bind(TypeSystem.TypedValue.toArray)
|
||||
->E.R.bind(r =>
|
||||
r
|
||||
|> E.A.fmap(TypeSystem.TypedValue.toDist)
|
||||
|> E.A.R.firstErrorOrOpen
|
||||
);
|
||||
let weights =
|
||||
getByNameResult(r, "weights")
|
||||
->E.R.bind(TypeSystem.TypedValue.toArray)
|
||||
->E.R.bind(r =>
|
||||
r
|
||||
|> E.A.fmap(TypeSystem.TypedValue.toFloat)
|
||||
|> E.A.R.firstErrorOrOpen
|
||||
);
|
||||
|
||||
E.R.merge(dists, weights)
|
||||
|> E.R.fmap(((a, b)) =>
|
||||
E.A.zipMaxLength(a, b)
|
||||
|> E.A.fmap(((a, b)) =>
|
||||
(a |> E.O.toExn(""), b |> E.O.default(1.0))
|
||||
)
|
||||
);
|
||||
| _ => Error("Needs items")
|
||||
};
|
||||
let _runner: array(typedValue) => result(node, string) =
|
||||
r => {
|
||||
let paramsToDistsAndWeights =
|
||||
_paramsToDistsAndWeights(r)
|
||||
|> E.R.fmap(
|
||||
E.A.fmap(((dist, weight)) =>
|
||||
`FunctionCall((
|
||||
"scaleMultiply",
|
||||
[|dist, `SymbolicDist(`Float(weight))|],
|
||||
))
|
||||
),
|
||||
);
|
||||
let pointwiseSum: result(node, string) =
|
||||
paramsToDistsAndWeights->E.R.bind(
|
||||
E.R.errorIfCondition(E.A.isEmpty, "Needs one input"),
|
||||
)
|
||||
|> E.R.fmap(r =>
|
||||
r
|
||||
|> Js.Array.sliceFrom(1)
|
||||
|> E.A.fold_left(
|
||||
(acc, x) => {`PointwiseCombination((`Add, acc, x))},
|
||||
E.A.unsafe_get(r, 0),
|
||||
)
|
||||
);
|
||||
pointwiseSum;
|
||||
};
|
||||
|
||||
let _function =
|
||||
Function.T.make(
|
||||
~name="multimodal",
|
||||
~outputType=`SamplingDistribution,
|
||||
~inputTypes=[|
|
||||
`Hash([|
|
||||
("dists", `Array(`SamplingDistribution)),
|
||||
("weights", `Array(`Float)),
|
||||
|]),
|
||||
|],
|
||||
~run=_runner,
|
||||
(),
|
||||
);
|
||||
};
|
||||
|
||||
let all = [|
|
||||
makeSymbolicFromTwoFloats("normal", SymbolicDist.Normal.make),
|
||||
makeSymbolicFromTwoFloats("uniform", SymbolicDist.Uniform.make),
|
||||
makeSymbolicFromTwoFloats("beta", SymbolicDist.Beta.make),
|
||||
makeSymbolicFromTwoFloats("lognormal", SymbolicDist.Lognormal.make),
|
||||
makeSymbolicFromTwoFloats(
|
||||
"lognormalFromMeanAndStdDev",
|
||||
SymbolicDist.Lognormal.fromMeanAndStdev,
|
||||
),
|
||||
makeSymbolicFromOneFloat("exponential", SymbolicDist.Exponential.make),
|
||||
Function.T.make(
|
||||
~name="to",
|
||||
~outputType=`SamplingDistribution,
|
||||
~inputTypes=[|`Float, `Float|],
|
||||
~run=
|
||||
fun
|
||||
| [|`Float(a), `Float(b)|] => to_(a, b)
|
||||
| e => wrongInputsError(e),
|
||||
(),
|
||||
),
|
||||
Function.T.make(
|
||||
~name="triangular",
|
||||
~outputType=`SamplingDistribution,
|
||||
~inputTypes=[|`Float, `Float, `Float|],
|
||||
~run=
|
||||
fun
|
||||
| [|`Float(a), `Float(b), `Float(c)|] =>
|
||||
SymbolicDist.Triangular.make(a, b, c)
|
||||
|> E.R.fmap(r => `SymbolicDist(r))
|
||||
| e => wrongInputsError(e),
|
||||
(),
|
||||
),
|
||||
makeDistFloat("pdf", (dist, float) => floatFromDist(`Pdf(float), dist)),
|
||||
makeDistFloat("inv", (dist, float) => floatFromDist(`Inv(float), dist)),
|
||||
makeDistFloat("cdf", (dist, float) => floatFromDist(`Cdf(float), dist)),
|
||||
makeDist("mean", dist => floatFromDist(`Mean, dist)),
|
||||
makeDist("sample", dist => floatFromDist(`Sample, dist)),
|
||||
Function.T.make(
|
||||
~name="render",
|
||||
~outputType=`RenderedDistribution,
|
||||
~inputTypes=[|`RenderedDistribution|],
|
||||
~run=
|
||||
fun
|
||||
| [|`RenderedDist(c)|] => Ok(`RenderedDist(c))
|
||||
| e => wrongInputsError(e),
|
||||
(),
|
||||
),
|
||||
Function.T.make(
|
||||
~name="normalize",
|
||||
~outputType=`SamplingDistribution,
|
||||
~inputTypes=[|`SamplingDistribution|],
|
||||
~run=
|
||||
fun
|
||||
| [|`SamplingDist(`SymbolicDist(c))|] => Ok(`SymbolicDist(c))
|
||||
| [|`SamplingDist(`RenderedDist(c))|] =>
|
||||
Ok(`RenderedDist(Shape.T.normalize(c)))
|
||||
| e => wrongInputsError(e),
|
||||
(),
|
||||
),
|
||||
makeRenderedDistFloat("scaleExp", (dist, float) =>
|
||||
verticalScaling(`Exponentiate, dist, float)
|
||||
),
|
||||
makeRenderedDistFloat("scaleMultiply", (dist, float) =>
|
||||
verticalScaling(`Multiply, dist, float)
|
||||
),
|
||||
makeRenderedDistFloat("scaleLog", (dist, float) =>
|
||||
verticalScaling(`Log, dist, float)
|
||||
),
|
||||
Multimodal._function
|
||||
|];
|
|
@ -0,0 +1,221 @@
|
|||
open TypeSystem
|
||||
|
||||
let wrongInputsError = (r: array<typedValue>) => {
|
||||
let inputs = r |> E.A.fmap(TypedValue.toString) |> Js.String.concatMany(_, ",")
|
||||
Js.log3("Inputs were", inputs, r)
|
||||
Error("Wrong inputs. The inputs were:" ++ inputs)
|
||||
}
|
||||
|
||||
let to_: (float, float) => result<node, string> = (low, high) =>
|
||||
switch (low, high) {
|
||||
| (low, high) if low <= 0.0 && low < high =>
|
||||
Ok(#SymbolicDist(SymbolicDist.Normal.from90PercentCI(low, high)))
|
||||
| (low, high) if low < high =>
|
||||
Ok(#SymbolicDist(SymbolicDist.Lognormal.from90PercentCI(low, high)))
|
||||
| (_, _) => Error("Low value must be less than high value.")
|
||||
}
|
||||
|
||||
let makeSymbolicFromTwoFloats = (name, fn) =>
|
||||
Function.T.make(
|
||||
~name,
|
||||
~outputType=#SamplingDistribution,
|
||||
~inputTypes=[#Float, #Float],
|
||||
~run=x =>
|
||||
switch x {
|
||||
| [#Float(a), #Float(b)] => Ok(#SymbolicDist(fn(a, b)))
|
||||
| e => wrongInputsError(e)
|
||||
},
|
||||
(),
|
||||
)
|
||||
|
||||
let makeSymbolicFromOneFloat = (name, fn) =>
|
||||
Function.T.make(
|
||||
~name,
|
||||
~outputType=#SamplingDistribution,
|
||||
~inputTypes=[#Float],
|
||||
~run=x =>
|
||||
switch x {
|
||||
| [#Float(a)] => Ok(#SymbolicDist(fn(a)))
|
||||
| e => wrongInputsError(e)
|
||||
},
|
||||
(),
|
||||
)
|
||||
|
||||
let makeDistFloat = (name, fn) =>
|
||||
Function.T.make(
|
||||
~name,
|
||||
~outputType=#SamplingDistribution,
|
||||
~inputTypes=[#SamplingDistribution, #Float],
|
||||
~run=x =>
|
||||
switch x {
|
||||
| [#SamplingDist(a), #Float(b)] => fn(a, b)
|
||||
| [#RenderedDist(a), #Float(b)] => fn(#RenderedDist(a), b)
|
||||
| e => wrongInputsError(e)
|
||||
},
|
||||
(),
|
||||
)
|
||||
|
||||
let makeRenderedDistFloat = (name, fn) =>
|
||||
Function.T.make(
|
||||
~name,
|
||||
~outputType=#RenderedDistribution,
|
||||
~inputTypes=[#RenderedDistribution, #Float],
|
||||
~shouldCoerceTypes=true,
|
||||
~run=x =>
|
||||
switch x {
|
||||
| [#RenderedDist(a), #Float(b)] => fn(a, b)
|
||||
| e => wrongInputsError(e)
|
||||
},
|
||||
(),
|
||||
)
|
||||
|
||||
let makeDist = (name, fn) =>
|
||||
Function.T.make(
|
||||
~name,
|
||||
~outputType=#SamplingDistribution,
|
||||
~inputTypes=[#SamplingDistribution],
|
||||
~run=x =>
|
||||
switch x {
|
||||
| [#SamplingDist(a)] => fn(a)
|
||||
| [#RenderedDist(a)] => fn(#RenderedDist(a))
|
||||
| e => wrongInputsError(e)
|
||||
},
|
||||
(),
|
||||
)
|
||||
|
||||
let floatFromDist = (
|
||||
distToFloatOp: ExpressionTypes.distToFloatOperation,
|
||||
t: TypeSystem.samplingDist,
|
||||
): result<node, string> =>
|
||||
switch t {
|
||||
| #SymbolicDist(s) =>
|
||||
SymbolicDist.T.operate(distToFloatOp, s) |> E.R.bind(_, v => Ok(#SymbolicDist(#Float(v))))
|
||||
| #RenderedDist(rs) => Shape.operate(distToFloatOp, rs) |> (v => Ok(#SymbolicDist(#Float(v))))
|
||||
}
|
||||
|
||||
let verticalScaling = (scaleOp, rs, scaleBy) => {
|
||||
// scaleBy has to be a single float, otherwise we'll return an error.
|
||||
let fn = (secondary, main) => Operation.Scale.toFn(scaleOp, main, secondary)
|
||||
let integralSumCacheFn = Operation.Scale.toIntegralSumCacheFn(scaleOp)
|
||||
let integralCacheFn = Operation.Scale.toIntegralCacheFn(scaleOp)
|
||||
Ok(
|
||||
#RenderedDist(
|
||||
Shape.T.mapY(
|
||||
~integralSumCacheFn=integralSumCacheFn(scaleBy),
|
||||
~integralCacheFn=integralCacheFn(scaleBy),
|
||||
~fn=fn(scaleBy),
|
||||
rs,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
module Multimodal = {
|
||||
let getByNameResult = ExpressionTypes.ExpressionTree.Hash.getByNameResult
|
||||
|
||||
let _paramsToDistsAndWeights = (r: array<typedValue>) =>
|
||||
switch r {
|
||||
| [#Hash(r)] =>
|
||||
let dists =
|
||||
getByNameResult(r, "dists")
|
||||
->E.R.bind(TypeSystem.TypedValue.toArray)
|
||||
->E.R.bind(r => r |> E.A.fmap(TypeSystem.TypedValue.toDist) |> E.A.R.firstErrorOrOpen)
|
||||
let weights =
|
||||
getByNameResult(r, "weights")
|
||||
->E.R.bind(TypeSystem.TypedValue.toArray)
|
||||
->E.R.bind(r => r |> E.A.fmap(TypeSystem.TypedValue.toFloat) |> E.A.R.firstErrorOrOpen)
|
||||
|
||||
E.R.merge(dists, weights) |> E.R.fmap(((a, b)) =>
|
||||
E.A.zipMaxLength(a, b) |> E.A.fmap(((a, b)) => (a |> E.O.toExn(""), b |> E.O.default(1.0)))
|
||||
)
|
||||
| _ => Error("Needs items")
|
||||
}
|
||||
let _runner: array<typedValue> => result<node, string> = r => {
|
||||
let paramsToDistsAndWeights =
|
||||
_paramsToDistsAndWeights(r) |> E.R.fmap(
|
||||
E.A.fmap(((dist, weight)) =>
|
||||
#FunctionCall("scaleMultiply", [dist, #SymbolicDist(#Float(weight))])
|
||||
),
|
||||
)
|
||||
let pointwiseSum: result<node, string> =
|
||||
paramsToDistsAndWeights->E.R.bind(E.R.errorIfCondition(E.A.isEmpty, "Needs one input"))
|
||||
|> E.R.fmap(r =>
|
||||
r
|
||||
|> Js.Array.sliceFrom(1)
|
||||
|> E.A.fold_left((acc, x) => #PointwiseCombination(#Add, acc, x), E.A.unsafe_get(r, 0))
|
||||
)
|
||||
pointwiseSum
|
||||
}
|
||||
|
||||
let _function = Function.T.make(
|
||||
~name="multimodal",
|
||||
~outputType=#SamplingDistribution,
|
||||
~inputTypes=[#Hash([("dists", #Array(#SamplingDistribution)), ("weights", #Array(#Float))])],
|
||||
~run=_runner,
|
||||
(),
|
||||
)
|
||||
}
|
||||
|
||||
let all = [
|
||||
makeSymbolicFromTwoFloats("normal", SymbolicDist.Normal.make),
|
||||
makeSymbolicFromTwoFloats("uniform", SymbolicDist.Uniform.make),
|
||||
makeSymbolicFromTwoFloats("beta", SymbolicDist.Beta.make),
|
||||
makeSymbolicFromTwoFloats("lognormal", SymbolicDist.Lognormal.make),
|
||||
makeSymbolicFromTwoFloats("lognormalFromMeanAndStdDev", SymbolicDist.Lognormal.fromMeanAndStdev),
|
||||
makeSymbolicFromOneFloat("exponential", SymbolicDist.Exponential.make),
|
||||
Function.T.make(
|
||||
~name="to",
|
||||
~outputType=#SamplingDistribution,
|
||||
~inputTypes=[#Float, #Float],
|
||||
~run=x =>
|
||||
switch x {
|
||||
| [#Float(a), #Float(b)] => to_(a, b)
|
||||
| e => wrongInputsError(e)
|
||||
},
|
||||
(),
|
||||
),
|
||||
Function.T.make(
|
||||
~name="triangular",
|
||||
~outputType=#SamplingDistribution,
|
||||
~inputTypes=[#Float, #Float, #Float],
|
||||
~run=x =>
|
||||
switch x {
|
||||
| [#Float(a), #Float(b), #Float(c)] =>
|
||||
SymbolicDist.Triangular.make(a, b, c) |> E.R.fmap(r => #SymbolicDist(r))
|
||||
| e => wrongInputsError(e)
|
||||
},
|
||||
(),
|
||||
),
|
||||
makeDistFloat("pdf", (dist, float) => floatFromDist(#Pdf(float), dist)),
|
||||
makeDistFloat("inv", (dist, float) => floatFromDist(#Inv(float), dist)),
|
||||
makeDistFloat("cdf", (dist, float) => floatFromDist(#Cdf(float), dist)),
|
||||
makeDist("mean", dist => floatFromDist(#Mean, dist)),
|
||||
makeDist("sample", dist => floatFromDist(#Sample, dist)),
|
||||
Function.T.make(
|
||||
~name="render",
|
||||
~outputType=#RenderedDistribution,
|
||||
~inputTypes=[#RenderedDistribution],
|
||||
~run=x =>
|
||||
switch x {
|
||||
| [#RenderedDist(c)] => Ok(#RenderedDist(c))
|
||||
| e => wrongInputsError(e)
|
||||
},
|
||||
(),
|
||||
),
|
||||
Function.T.make(
|
||||
~name="normalize",
|
||||
~outputType=#SamplingDistribution,
|
||||
~inputTypes=[#SamplingDistribution],
|
||||
~run=x =>
|
||||
switch x {
|
||||
| [#SamplingDist(#SymbolicDist(c))] => Ok(#SymbolicDist(c))
|
||||
| [#SamplingDist(#RenderedDist(c))] => Ok(#RenderedDist(Shape.T.normalize(c)))
|
||||
| e => wrongInputsError(e)
|
||||
},
|
||||
(),
|
||||
),
|
||||
makeRenderedDistFloat("scaleExp", (dist, float) => verticalScaling(#Exponentiate, dist, float)),
|
||||
makeRenderedDistFloat("scaleMultiply", (dist, float) => verticalScaling(#Multiply, dist, float)),
|
||||
makeRenderedDistFloat("scaleLog", (dist, float) => verticalScaling(#Log, dist, float)),
|
||||
Multimodal._function,
|
||||
]
|
|
@ -1,228 +0,0 @@
|
|||
type node = ExpressionTypes.ExpressionTree.node;
|
||||
let getFloat = ExpressionTypes.ExpressionTree.getFloat;
|
||||
|
||||
type samplingDist = [
|
||||
| `SymbolicDist(SymbolicTypes.symbolicDist)
|
||||
| `RenderedDist(DistTypes.shape)
|
||||
];
|
||||
|
||||
type hashType = array((string, _type))
|
||||
and _type = [
|
||||
| `Float
|
||||
| `SamplingDistribution
|
||||
| `RenderedDistribution
|
||||
| `Array(_type)
|
||||
| `Hash(hashType)
|
||||
];
|
||||
|
||||
type hashTypedValue = array((string, typedValue))
|
||||
and typedValue = [
|
||||
| `Float(float)
|
||||
| `RenderedDist(DistTypes.shape)
|
||||
| `SamplingDist(samplingDist)
|
||||
| `Array(array(typedValue))
|
||||
| `Hash(hashTypedValue)
|
||||
];
|
||||
|
||||
type _function = {
|
||||
name: string,
|
||||
inputTypes: array(_type),
|
||||
outputType: _type,
|
||||
run: array(typedValue) => result(node, string),
|
||||
shouldCoerceTypes: bool,
|
||||
};
|
||||
|
||||
type functions = array(_function);
|
||||
type inputNodes = array(node);
|
||||
|
||||
module TypedValue = {
|
||||
let rec toString: typedValue => string =
|
||||
fun
|
||||
| `SamplingDist(_) => "[sampling dist]"
|
||||
| `RenderedDist(_) => "[rendered Shape]"
|
||||
| `Float(f) => "Float: " ++ Js.Float.toString(f)
|
||||
| `Array(a) =>
|
||||
"[" ++ (a |> E.A.fmap(toString) |> Js.String.concatMany(_, ",")) ++ "]"
|
||||
| `Hash(v) =>
|
||||
"{"
|
||||
++ (
|
||||
v
|
||||
|> E.A.fmap(((name, value)) => name ++ ":" ++ toString(value))
|
||||
|> Js.String.concatMany(_, ",")
|
||||
)
|
||||
++ "}";
|
||||
|
||||
let rec fromNode = (node: node): result(typedValue, string) =>
|
||||
switch (node) {
|
||||
| `SymbolicDist(`Float(r)) => Ok(`Float(r))
|
||||
| `SymbolicDist(s) => Ok(`SamplingDist(`SymbolicDist(s)))
|
||||
| `RenderedDist(s) => Ok(`RenderedDist(s))
|
||||
| `Array(r) =>
|
||||
r
|
||||
|> E.A.fmap(fromNode)
|
||||
|> E.A.R.firstErrorOrOpen
|
||||
|> E.R.fmap(r => `Array(r))
|
||||
| `Hash(hash) =>
|
||||
hash
|
||||
|> E.A.fmap(((name, t)) => fromNode(t) |> E.R.fmap(r => (name, r)))
|
||||
|> E.A.R.firstErrorOrOpen
|
||||
|> E.R.fmap(r => `Hash(r))
|
||||
| e => Error("Wrong type: " ++ ExpressionTreeBasic.toString(e))
|
||||
};
|
||||
|
||||
// todo: Arrays and hashes
|
||||
let rec fromNodeWithTypeCoercion = (evaluationParams, _type: _type, node) => {
|
||||
switch (_type, node) {
|
||||
| (`Float, _) =>
|
||||
switch (getFloat(node)) {
|
||||
| Some(a) => Ok(`Float(a))
|
||||
| _ => Error("Type Error: Expected float.")
|
||||
}
|
||||
| (`SamplingDistribution, _) =>
|
||||
PTypes.SamplingDistribution.renderIfIsNotSamplingDistribution(
|
||||
evaluationParams,
|
||||
node,
|
||||
)
|
||||
|> E.R.bind(_, fromNode)
|
||||
| (`RenderedDistribution, _) =>{
|
||||
ExpressionTypes.ExpressionTree.Render.render(evaluationParams, node)
|
||||
|> E.R.bind(_, fromNode);
|
||||
}
|
||||
| (`Array(_type), `Array(b)) =>
|
||||
b
|
||||
|> E.A.fmap(fromNodeWithTypeCoercion(evaluationParams, _type))
|
||||
|> E.A.R.firstErrorOrOpen
|
||||
|> E.R.fmap(r => `Array(r))
|
||||
| (`Hash(named), `Hash(r)) =>
|
||||
let keyValues =
|
||||
named
|
||||
|> E.A.fmap(((name, intendedType)) =>
|
||||
(
|
||||
name,
|
||||
intendedType,
|
||||
ExpressionTypes.ExpressionTree.Hash.getByName(r, name),
|
||||
)
|
||||
);
|
||||
let typedHash =
|
||||
keyValues
|
||||
|> E.A.fmap(((name, intendedType, optionNode)) =>
|
||||
switch (optionNode) {
|
||||
| Some(node) =>
|
||||
fromNodeWithTypeCoercion(evaluationParams, intendedType, node)
|
||||
|> E.R.fmap(node => (name, node))
|
||||
| None => Error("Hash parameter not present in hash.")
|
||||
}
|
||||
)
|
||||
|> E.A.R.firstErrorOrOpen
|
||||
|> E.R.fmap(r => `Hash(r));
|
||||
typedHash;
|
||||
| _ => Error("fromNodeWithTypeCoercion error, sorry.")
|
||||
};
|
||||
};
|
||||
|
||||
let toFloat: typedValue => result(float, string) =
|
||||
fun
|
||||
| `Float(x) => Ok(x)
|
||||
| _ => Error("Not a float");
|
||||
|
||||
let toArray: typedValue => result(array('a), string) =
|
||||
fun
|
||||
| `Array(x) => Ok(x)
|
||||
| _ => Error("Not an array");
|
||||
|
||||
let toNamed: typedValue => result(hashTypedValue, string) =
|
||||
fun
|
||||
| `Hash(x) => Ok(x)
|
||||
| _ => Error("Not a named item");
|
||||
|
||||
let toDist: typedValue => result(node,string) =
|
||||
fun
|
||||
| `SamplingDist(`SymbolicDist(c)) => Ok(`SymbolicDist(c))
|
||||
| `SamplingDist(`RenderedDist(c)) => Ok(`RenderedDist(c))
|
||||
| `RenderedDist(c) => Ok(`RenderedDist(c))
|
||||
| `Float(x) => Ok(`SymbolicDist(`Float(x)))
|
||||
| x => Error("Cannot be converted into a distribution: " ++ toString(x));
|
||||
};
|
||||
|
||||
module Function = {
|
||||
type t = _function;
|
||||
type ts = functions;
|
||||
|
||||
module T = {
|
||||
let make =
|
||||
(~name, ~inputTypes, ~outputType, ~run, ~shouldCoerceTypes=true, _): t => {
|
||||
name,
|
||||
inputTypes,
|
||||
outputType,
|
||||
run,
|
||||
shouldCoerceTypes,
|
||||
};
|
||||
|
||||
let _inputLengthCheck = (inputNodes: inputNodes, t: t) => {
|
||||
let expectedLength = E.A.length(t.inputTypes);
|
||||
let actualLength = E.A.length(inputNodes);
|
||||
expectedLength == actualLength
|
||||
? Ok(inputNodes)
|
||||
: Error(
|
||||
"Wrong number of inputs. Expected"
|
||||
++ (expectedLength |> E.I.toString)
|
||||
++ ". Got:"
|
||||
++ (actualLength |> E.I.toString),
|
||||
);
|
||||
};
|
||||
|
||||
let _coerceInputNodes =
|
||||
(evaluationParams, inputTypes, shouldCoerce, inputNodes) =>
|
||||
Belt.Array.zip(inputTypes, inputNodes)
|
||||
|> E.A.fmap(((def, input)) =>
|
||||
shouldCoerce
|
||||
? TypedValue.fromNodeWithTypeCoercion(
|
||||
evaluationParams,
|
||||
def,
|
||||
input,
|
||||
)
|
||||
: TypedValue.fromNode(input)
|
||||
)
|
||||
|> E.A.R.firstErrorOrOpen;
|
||||
|
||||
let inputsToTypedValues =
|
||||
(
|
||||
evaluationParams: ExpressionTypes.ExpressionTree.evaluationParams,
|
||||
inputNodes: inputNodes,
|
||||
t: t,
|
||||
) => {
|
||||
_inputLengthCheck(inputNodes, t)
|
||||
->E.R.bind(
|
||||
_coerceInputNodes(
|
||||
evaluationParams,
|
||||
t.inputTypes,
|
||||
t.shouldCoerceTypes,
|
||||
),
|
||||
)
|
||||
};
|
||||
|
||||
let run =
|
||||
(
|
||||
evaluationParams: ExpressionTypes.ExpressionTree.evaluationParams,
|
||||
inputNodes: inputNodes,
|
||||
t: t,
|
||||
) => {
|
||||
inputsToTypedValues(evaluationParams, inputNodes, t)->E.R.bind(t.run)
|
||||
|> (
|
||||
fun
|
||||
| Ok(i) => Ok(i)
|
||||
| Error(r) => {
|
||||
Error("Function " ++ t.name ++ " error: " ++ r);
|
||||
}
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
module Ts = {
|
||||
let findByName = (ts: ts, n: string) =>
|
||||
ts |> Belt.Array.getBy(_, ({name}) => name == n);
|
||||
|
||||
let findByNameAndRun = (ts: ts, n: string, evaluationParams, inputTypes) =>
|
||||
findByName(ts, n) |> E.O.fmap(T.run(evaluationParams, inputTypes));
|
||||
};
|
||||
};
|
204
packages/squiggle-lang/src/distPlus/typeSystem/TypeSystem.res
Normal file
204
packages/squiggle-lang/src/distPlus/typeSystem/TypeSystem.res
Normal file
|
@ -0,0 +1,204 @@
|
|||
type node = ExpressionTypes.ExpressionTree.node
|
||||
let getFloat = ExpressionTypes.ExpressionTree.getFloat
|
||||
|
||||
type samplingDist = [
|
||||
| #SymbolicDist(SymbolicTypes.symbolicDist)
|
||||
| #RenderedDist(DistTypes.shape)
|
||||
]
|
||||
|
||||
type rec hashType = array<(string, _type)>
|
||||
and _type = [
|
||||
| #Float
|
||||
| #SamplingDistribution
|
||||
| #RenderedDistribution
|
||||
| #Array(_type)
|
||||
| #Hash(hashType)
|
||||
]
|
||||
|
||||
type rec hashTypedValue = array<(string, typedValue)>
|
||||
and typedValue = [
|
||||
| #Float(float)
|
||||
| #RenderedDist(DistTypes.shape)
|
||||
| #SamplingDist(samplingDist)
|
||||
| #Array(array<typedValue>)
|
||||
| #Hash(hashTypedValue)
|
||||
]
|
||||
|
||||
type _function = {
|
||||
name: string,
|
||||
inputTypes: array<_type>,
|
||||
outputType: _type,
|
||||
run: array<typedValue> => result<node, string>,
|
||||
shouldCoerceTypes: bool,
|
||||
}
|
||||
|
||||
type functions = array<_function>
|
||||
type inputNodes = array<node>
|
||||
|
||||
module TypedValue = {
|
||||
let rec toString: typedValue => string = x =>
|
||||
switch x {
|
||||
| #SamplingDist(_) => "[sampling dist]"
|
||||
| #RenderedDist(_) => "[rendered Shape]"
|
||||
| #Float(f) => "Float: " ++ Js.Float.toString(f)
|
||||
| #Array(a) => "[" ++ ((a |> E.A.fmap(toString) |> Js.String.concatMany(_, ",")) ++ "]")
|
||||
| #Hash(v) =>
|
||||
"{" ++
|
||||
((v
|
||||
|> E.A.fmap(((name, value)) => name ++ (":" ++ toString(value)))
|
||||
|> Js.String.concatMany(_, ",")) ++
|
||||
"}")
|
||||
}
|
||||
|
||||
let rec fromNode = (node: node): result<typedValue, string> =>
|
||||
switch node {
|
||||
| #SymbolicDist(#Float(r)) => Ok(#Float(r))
|
||||
| #SymbolicDist(s) => Ok(#SamplingDist(#SymbolicDist(s)))
|
||||
| #RenderedDist(s) => Ok(#RenderedDist(s))
|
||||
| #Array(r) => r |> E.A.fmap(fromNode) |> E.A.R.firstErrorOrOpen |> E.R.fmap(r => #Array(r))
|
||||
| #Hash(hash) =>
|
||||
hash
|
||||
|> E.A.fmap(((name, t)) => fromNode(t) |> E.R.fmap(r => (name, r)))
|
||||
|> E.A.R.firstErrorOrOpen
|
||||
|> E.R.fmap(r => #Hash(r))
|
||||
| e => Error("Wrong type: " ++ ExpressionTreeBasic.toString(e))
|
||||
}
|
||||
|
||||
// todo: Arrays and hashes
|
||||
let rec fromNodeWithTypeCoercion = (evaluationParams, _type: _type, node) =>
|
||||
switch (_type, node) {
|
||||
| (#Float, _) =>
|
||||
switch getFloat(node) {
|
||||
| Some(a) => Ok(#Float(a))
|
||||
| _ => Error("Type Error: Expected float.")
|
||||
}
|
||||
| (#SamplingDistribution, _) =>
|
||||
PTypes.SamplingDistribution.renderIfIsNotSamplingDistribution(
|
||||
evaluationParams,
|
||||
node,
|
||||
) |> E.R.bind(_, fromNode)
|
||||
| (#RenderedDistribution, _) =>
|
||||
ExpressionTypes.ExpressionTree.Render.render(evaluationParams, node) |> E.R.bind(_, fromNode)
|
||||
| (#Array(_type), #Array(b)) =>
|
||||
b
|
||||
|> E.A.fmap(fromNodeWithTypeCoercion(evaluationParams, _type))
|
||||
|> E.A.R.firstErrorOrOpen
|
||||
|> E.R.fmap(r => #Array(r))
|
||||
| (#Hash(named), #Hash(r)) =>
|
||||
let keyValues =
|
||||
named |> E.A.fmap(((name, intendedType)) => (
|
||||
name,
|
||||
intendedType,
|
||||
ExpressionTypes.ExpressionTree.Hash.getByName(r, name),
|
||||
))
|
||||
let typedHash =
|
||||
keyValues
|
||||
|> E.A.fmap(((name, intendedType, optionNode)) =>
|
||||
switch optionNode {
|
||||
| Some(node) =>
|
||||
fromNodeWithTypeCoercion(evaluationParams, intendedType, node) |> E.R.fmap(node => (
|
||||
name,
|
||||
node,
|
||||
))
|
||||
| None => Error("Hash parameter not present in hash.")
|
||||
}
|
||||
)
|
||||
|> E.A.R.firstErrorOrOpen
|
||||
|> E.R.fmap(r => #Hash(r))
|
||||
typedHash
|
||||
| _ => Error("fromNodeWithTypeCoercion error, sorry.")
|
||||
}
|
||||
|
||||
let toFloat: typedValue => result<float, string> = x =>
|
||||
switch x {
|
||||
| #Float(x) => Ok(x)
|
||||
| _ => Error("Not a float")
|
||||
}
|
||||
|
||||
let toArray: typedValue => result<array<'a>, string> = x =>
|
||||
switch x {
|
||||
| #Array(x) => Ok(x)
|
||||
| _ => Error("Not an array")
|
||||
}
|
||||
|
||||
let toNamed: typedValue => result<hashTypedValue, string> = x =>
|
||||
switch x {
|
||||
| #Hash(x) => Ok(x)
|
||||
| _ => Error("Not a named item")
|
||||
}
|
||||
|
||||
let toDist: typedValue => result<node, string> = x =>
|
||||
switch x {
|
||||
| #SamplingDist(#SymbolicDist(c)) => Ok(#SymbolicDist(c))
|
||||
| #SamplingDist(#RenderedDist(c)) => Ok(#RenderedDist(c))
|
||||
| #RenderedDist(c) => Ok(#RenderedDist(c))
|
||||
| #Float(x) => Ok(#SymbolicDist(#Float(x)))
|
||||
| x => Error("Cannot be converted into a distribution: " ++ toString(x))
|
||||
}
|
||||
}
|
||||
|
||||
module Function = {
|
||||
type t = _function
|
||||
type ts = functions
|
||||
|
||||
module T = {
|
||||
let make = (~name, ~inputTypes, ~outputType, ~run, ~shouldCoerceTypes=true, _): t => {
|
||||
name: name,
|
||||
inputTypes: inputTypes,
|
||||
outputType: outputType,
|
||||
run: run,
|
||||
shouldCoerceTypes: shouldCoerceTypes,
|
||||
}
|
||||
|
||||
let _inputLengthCheck = (inputNodes: inputNodes, t: t) => {
|
||||
let expectedLength = E.A.length(t.inputTypes)
|
||||
let actualLength = E.A.length(inputNodes)
|
||||
expectedLength == actualLength
|
||||
? Ok(inputNodes)
|
||||
: Error(
|
||||
"Wrong number of inputs. Expected" ++
|
||||
((expectedLength |> E.I.toString) ++
|
||||
(". Got:" ++ (actualLength |> E.I.toString))),
|
||||
)
|
||||
}
|
||||
|
||||
let _coerceInputNodes = (evaluationParams, inputTypes, shouldCoerce, inputNodes) =>
|
||||
Belt.Array.zip(inputTypes, inputNodes)
|
||||
|> E.A.fmap(((def, input)) =>
|
||||
shouldCoerce
|
||||
? TypedValue.fromNodeWithTypeCoercion(evaluationParams, def, input)
|
||||
: TypedValue.fromNode(input)
|
||||
)
|
||||
|> E.A.R.firstErrorOrOpen
|
||||
|
||||
let inputsToTypedValues = (
|
||||
evaluationParams: ExpressionTypes.ExpressionTree.evaluationParams,
|
||||
inputNodes: inputNodes,
|
||||
t: t,
|
||||
) =>
|
||||
_inputLengthCheck(inputNodes, t)->E.R.bind(
|
||||
_coerceInputNodes(evaluationParams, t.inputTypes, t.shouldCoerceTypes),
|
||||
)
|
||||
|
||||
let run = (
|
||||
evaluationParams: ExpressionTypes.ExpressionTree.evaluationParams,
|
||||
inputNodes: inputNodes,
|
||||
t: t,
|
||||
) =>
|
||||
inputsToTypedValues(evaluationParams, inputNodes, t)->E.R.bind(t.run)
|
||||
|> (
|
||||
x =>
|
||||
switch x {
|
||||
| Ok(i) => Ok(i)
|
||||
| Error(r) => Error("Function " ++ (t.name ++ (" error: " ++ r)))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
module Ts = {
|
||||
let findByName = (ts: ts, n: string) => ts |> Belt.Array.getBy(_, ({name}) => name == n)
|
||||
|
||||
let findByNameAndRun = (ts: ts, n: string, evaluationParams, inputTypes) =>
|
||||
findByName(ts, n) |> E.O.fmap(T.run(evaluationParams, inputTypes))
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
@module("jStat") @scope("normal") external mean: (float, float) => float = "mean"
|
||||
|
||||
let foo = mean;
|
Loading…
Reference in New Issue
Block a user