squiggle/src/symbolic/MathJsParser.re

110 lines
3.2 KiB
ReasonML
Raw Normal View History

open Jstat;
type arg =
2020-03-24 00:04:48 +00:00
| Symbol(string)
| Value(float)
| Fn(fn)
2020-03-24 00:04:48 +00:00
| Array(array(arg))
and fn = {
name: string,
args: array(arg),
};
2020-03-24 00:04:48 +00:00
let rec parseMathjs = (j: Js.Json.t) =>
Json.Decode.(
switch (field("mathjs", string, j)) {
| "FunctionNode" =>
let args = j |> field("args", array(parseMathjs));
Some(
Fn({
name: j |> field("fn", field("name", string)),
args: args |> E.A.O.concatSomes,
}),
);
2020-03-24 00:04:48 +00:00
| "OperatorNode" =>
let args = j |> field("args", array(parseMathjs));
Some(
Fn({
name: j |> field("fn", string),
args: args |> E.A.O.concatSomes,
}),
);
| "ConstantNode" => Some(Value(field("value", Json.Decode.float, j)))
| "ArrayNode" =>
let items = field("items", array(parseMathjs), j);
Some(Array(items |> E.A.O.concatSomes));
| "SymbolNode" => Some(Symbol(field("name", string, j)))
| n =>
Js.log2("Couldn't parse mathjs node", j);
None;
}
);
2020-03-24 00:04:48 +00:00
// let logHigh = math.log(high);
// let logLow = math.log(low);
// let mean = (math.mean(logHigh, logLow)).toFixed(3);
// let stdev = ((logHigh-logLow) / (2*1.645)).toFixed(3);
let normal: array(arg) => result(bigDist, string) =
fun
| [|Value(mean), Value(stdev)|] => Ok(`Dist(`Normal({mean, stdev})))
| _ => Error("Wrong number of variables in normal distribution");
let lognormal: array(arg) => result(bigDist, string) =
fun
| [|Value(mu), Value(sigma)|] => Ok(`Dist(`Lognormal({mu, sigma})))
| _ => Error("Wrong number of variables in lognormal distribution");
let to_: array(arg) => result(bigDist, string) =
fun
| [|Value(low), Value(high)|] => {
let logLow = Js.Math.log(low);
let logHigh = Js.Math.log(high);
let mu = Functions.mean([|logLow, logHigh|]);
let sigma = (logHigh -. logLow) /. (2.0 *. 1.645);
Ok(`Dist(`Lognormal({mu, sigma})));
}
| _ => Error("Wrong number of variables in lognormal distribution");
let uniform: array(arg) => result(bigDist, string) =
fun
| [|Value(low), Value(high)|] => Ok(`Dist(`Uniform({low, high})))
| _ => Error("Wrong number of variables in lognormal distribution");
let rec toValue = (r): result(bigDist, string) =>
r
|> (
fun
| Value(_) => Error("Top level can't be value")
| Fn({name: "normal", args}) => normal(args)
2020-03-24 00:04:48 +00:00
| Fn({name: "lognormal", args}) => lognormal(args)
| Fn({name: "uniform", args}) => uniform(args)
| Fn({name: "to", args}) => to_(args)
| Fn({name: "mm", args}) => {
let dists: array(dist) =
args
|> E.A.fmap(toValue)
|> E.A.fmap(
fun
2020-03-24 00:04:48 +00:00
| Ok(`Dist(n)) => Some(n)
| _ => None,
)
|> E.A.O.concatSomes;
let inputs = dists |> E.A.fmap(r => (r, 1.0));
Ok(`PointwiseCombination(inputs));
}
| Fn({name}) => Error(name ++ ": name not found")
2020-03-24 00:04:48 +00:00
| Array(_) => Error("Array not valid as top level")
| Symbol(_) => Error("Symbol not valid as top level")
);
let fromString = str =>
Mathjs.parseMath(str)
|> E.R.bind(_, r =>
switch (parseMathjs(r)) {
| Some(r) => toValue(r)
| None => Error("Second parse failed")
}
);