Add project serialization and run project
This commit is contained in:
parent
d3bc08ab9d
commit
f489d0bcfe
|
@ -180,3 +180,42 @@ describe("project with independent sources", () => {
|
|||
expect(Project.getRunOrderFor(project, "second")) == ["second"]
|
||||
})
|
||||
})
|
||||
|
||||
describe("project serialization", () => {
|
||||
let project = Project.createProject()
|
||||
Project.setSource(project, "first", "x = 1")
|
||||
Project.setSource(project, "second", "y = 1")
|
||||
Project.setSource(project, "main", "x + y")
|
||||
Project.setContinues(project, "main", ["first", "second"])
|
||||
let expectedJson: Project.reducerProjectJson = {
|
||||
environment: {
|
||||
sampleCount: 10000,
|
||||
xyPointLength: 1000,
|
||||
},
|
||||
items: [
|
||||
{
|
||||
continues: [],
|
||||
id: "first",
|
||||
source: "x = 1",
|
||||
},
|
||||
{
|
||||
continues: ["first", "second"],
|
||||
id: "main",
|
||||
source: "x + y",
|
||||
},
|
||||
{
|
||||
continues: [],
|
||||
id: "second",
|
||||
source: "y = 1",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
test("serializes correctly", () => {
|
||||
expect(Project.toJson(project)) == expectedJson
|
||||
})
|
||||
|
||||
test("serializes reflexive", () => {
|
||||
expect(Project.toJson(Project.fromJson(Project.toJson(project)))) == Project.toJson(project)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -82,7 +82,10 @@ describe("FunctionRegistry Library", () => {
|
|||
)
|
||||
|
||||
testEvalToBe("Dict.merge({a: 1, b: 2}, {b: 3, c: 4, d: 5})", "Ok({a: 1,b: 3,c: 4,d: 5})")
|
||||
testEvalToBe("Dict.mergeMany([{a: 1, b: 2}, {c: 3, d: 4}, {c: 5, e: 6}])", "Ok({a: 1,b: 2,c: 5,d: 4,e: 6})")
|
||||
testEvalToBe(
|
||||
"Dict.mergeMany([{a: 1, b: 2}, {c: 3, d: 4}, {c: 5, e: 6}])",
|
||||
"Ok({a: 1,b: 2,c: 5,d: 4,e: 6})",
|
||||
)
|
||||
testEvalToBe("Dict.keys({a: 1, b: 2})", "Ok(['a','b'])")
|
||||
testEvalToBe("Dict.values({a: 1, b: 2})", "Ok([1,2])")
|
||||
testEvalToBe("Dict.toList({a: 1, b: 2})", "Ok([['a',1],['b',2]])")
|
||||
|
|
|
@ -39,3 +39,26 @@ export const run = (src, { output, sampleCount } = {}) => {
|
|||
result.tag === "Error" ? result.value.toString() : ""
|
||||
);
|
||||
};
|
||||
|
||||
export const runProject = (project, output) => {
|
||||
const time = measure(() => project.runAll());
|
||||
console.log("Time: ", time);
|
||||
|
||||
const ids = project.getSourceIds();
|
||||
|
||||
ids.forEach((id) => {
|
||||
const result = project.getResult(id);
|
||||
const bindings = project.getBindings("main");
|
||||
|
||||
console.log(id + ":");
|
||||
if (output) {
|
||||
console.log("Result:", result.tag, result.value.toString());
|
||||
console.log("Bindings:", bindings.toString());
|
||||
}
|
||||
|
||||
console.log(
|
||||
result.tag === "Error" ? red(result.tag) : green(result.tag),
|
||||
result.tag === "Error" ? result.value.toString() : ""
|
||||
);
|
||||
});
|
||||
};
|
||||
|
|
28
packages/squiggle-lang/scripts/run-project.mjs
Executable file
28
packages/squiggle-lang/scripts/run-project.mjs
Executable file
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env node
|
||||
import fs from "fs";
|
||||
|
||||
import { Command } from "commander";
|
||||
|
||||
import { SqProject } from "@quri/squiggle-lang";
|
||||
import { runProject } from "./lib.mjs";
|
||||
|
||||
const program = new Command();
|
||||
|
||||
program.option("-o, --output");
|
||||
program.arguments("<string>");
|
||||
|
||||
const options = program.parse(process.argv);
|
||||
|
||||
const sampleCount = process.env.SAMPLE_COUNT ?? 10000;
|
||||
console.log(sampleCount);
|
||||
|
||||
const src = fs.readFileSync(program.args[0], "utf-8");
|
||||
if (!src) {
|
||||
throw new Error("Expected src");
|
||||
}
|
||||
|
||||
const projectJson = JSON.parse(src);
|
||||
|
||||
const project = SqProject.fromJson(projectJson);
|
||||
|
||||
runProject(project, options.output);
|
|
@ -115,4 +115,14 @@ export class SqProject {
|
|||
getEnvironment(): environment {
|
||||
return RSProject.getEnvironment(this._value);
|
||||
}
|
||||
|
||||
toJSON(): RSProject.reducerProjectJson {
|
||||
return RSProject.toJson(this._value);
|
||||
}
|
||||
|
||||
static fromJson(
|
||||
projectJson: RSProject.reducerProjectJson
|
||||
): RSProject.reducerProject {
|
||||
return RSProject.fromJson(projectJson);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,7 +59,6 @@ let library = [
|
|||
],
|
||||
(),
|
||||
),
|
||||
|
||||
Function.make(
|
||||
~name="mergeMany",
|
||||
~nameSpace,
|
||||
|
@ -72,13 +71,16 @@ let library = [
|
|||
~inputs=[FRTypeArray(FRTypeDict(FRTypeAny))],
|
||||
~run=(inputs, _, _) => {
|
||||
switch inputs {
|
||||
| [IEvArray(dicts)] => {
|
||||
dicts->Belt.Array.map(dictValue => switch dictValue {
|
||||
| IEvRecord(dict) => dict
|
||||
| _ => impossibleError->Reducer_ErrorValue.toException
|
||||
})->Internals.mergeMany->Ok
|
||||
}
|
||||
| _ => impossibleError->Error
|
||||
| [IEvArray(dicts)] => dicts
|
||||
->Belt.Array.map(dictValue =>
|
||||
switch dictValue {
|
||||
| IEvRecord(dict) => dict
|
||||
| _ => impossibleError->Reducer_ErrorValue.toException
|
||||
}
|
||||
)
|
||||
->Internals.mergeMany
|
||||
->Ok
|
||||
| _ => impossibleError->Error
|
||||
}
|
||||
},
|
||||
(),
|
||||
|
|
|
@ -22,8 +22,7 @@ module DistributionCreation = {
|
|||
FnDefinition.make(
|
||||
~name,
|
||||
~inputs=[FRTypeDistOrNumber, FRTypeDistOrNumber],
|
||||
~run=(inputs, env, _) =>
|
||||
inputs->Prepare.ToValueTuple.twoDistOrNumber->process(~fn, ~env),
|
||||
~run=(inputs, env, _) => inputs->Prepare.ToValueTuple.twoDistOrNumber->process(~fn, ~env),
|
||||
(),
|
||||
)
|
||||
}
|
||||
|
@ -43,7 +42,9 @@ module DistributionCreation = {
|
|||
~name,
|
||||
~inputs=[FRTypeRecord([("mean", FRTypeDistOrNumber), ("stdev", FRTypeDistOrNumber)])],
|
||||
~run=(inputs, env, _) =>
|
||||
inputs->Prepare.ToValueTuple.Record.twoDistOrNumber(("mean", "stdev"))->process(~fn, ~env),
|
||||
inputs
|
||||
->Prepare.ToValueTuple.Record.twoDistOrNumber(("mean", "stdev"))
|
||||
->process(~fn, ~env),
|
||||
(),
|
||||
)
|
||||
}
|
||||
|
@ -60,8 +61,7 @@ module DistributionCreation = {
|
|||
FnDefinition.make(
|
||||
~name,
|
||||
~inputs=[FRTypeDistOrNumber],
|
||||
~run=(inputs, env, _) =>
|
||||
inputs->Prepare.ToValueTuple.oneDistOrNumber->process(~fn, ~env),
|
||||
~run=(inputs, env, _) => inputs->Prepare.ToValueTuple.oneDistOrNumber->process(~fn, ~env),
|
||||
(),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -109,8 +109,8 @@ let library = [
|
|||
~inputs=[FRTypeNumber, FRTypeNumber],
|
||||
~run=(inputs, _, _) =>
|
||||
switch inputs {
|
||||
| [IEvNumber(low), IEvNumber(high)] => Internals.upTo(low, high)->Ok
|
||||
| _ => impossibleError->Error
|
||||
| [IEvNumber(low), IEvNumber(high)] => Internals.upTo(low, high)->Ok
|
||||
| _ => impossibleError->Error
|
||||
},
|
||||
(),
|
||||
),
|
||||
|
|
|
@ -7,24 +7,24 @@ let requiresNamespace = true
|
|||
let inputsToDist = (inputs: array<Reducer_T.value>, xyShapeToPointSetDist) => {
|
||||
// TODO - rewrite in more functional/functor-based style
|
||||
switch inputs {
|
||||
| [IEvArray(items)] => {
|
||||
items->Belt.Array.map(
|
||||
item =>
|
||||
switch item {
|
||||
| IEvRecord(map) => {
|
||||
let xValue = map->Belt.Map.String.get("x")
|
||||
let yValue = map->Belt.Map.String.get("y")
|
||||
switch (xValue, yValue) {
|
||||
| (Some(IEvNumber(x)), Some(IEvNumber(y))) => (x, y)
|
||||
| _ => impossibleError->Reducer_ErrorValue.toException
|
||||
}
|
||||
}
|
||||
| [IEvArray(items)] => items
|
||||
->Belt.Array.map(item =>
|
||||
switch item {
|
||||
| IEvRecord(map) => {
|
||||
let xValue = map->Belt.Map.String.get("x")
|
||||
let yValue = map->Belt.Map.String.get("y")
|
||||
switch (xValue, yValue) {
|
||||
| (Some(IEvNumber(x)), Some(IEvNumber(y))) => (x, y)
|
||||
| _ => impossibleError->Reducer_ErrorValue.toException
|
||||
}
|
||||
}
|
||||
)->Ok->E.R.bind(r => r->XYShape.T.makeFromZipped->E.R2.errMap(XYShape.Error.toString))
|
||||
->E.R2.fmap(r => Reducer_T.IEvDistribution(PointSet(r->xyShapeToPointSetDist)))
|
||||
}
|
||||
| _ => impossibleError->Reducer_ErrorValue.toException
|
||||
| _ => impossibleError->Reducer_ErrorValue.toException
|
||||
}
|
||||
)
|
||||
->Ok
|
||||
->E.R.bind(r => r->XYShape.T.makeFromZipped->E.R2.errMap(XYShape.Error.toString))
|
||||
->E.R2.fmap(r => Reducer_T.IEvDistribution(PointSet(r->xyShapeToPointSetDist)))
|
||||
| _ => impossibleError->Reducer_ErrorValue.toException
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -115,9 +115,8 @@ let libaryBase = [
|
|||
~inputs=[FRTypeArray(FRTypeNumber)],
|
||||
~run=(inputs, _, _) => {
|
||||
let sampleSet =
|
||||
inputs->Prepare.ToTypedArray.numbers |> E.R2.bind(r =>
|
||||
SampleSetDist.make(r)->E.R2.errMap(_ => "AM I HERE? WHYERE AMI??")
|
||||
)
|
||||
inputs->Prepare.ToTypedArray.numbers
|
||||
|> E.R2.bind(r => SampleSetDist.make(r)->E.R2.errMap(_ => "AM I HERE? WHYERE AMI??"))
|
||||
sampleSet
|
||||
->E.R2.fmap(Wrappers.sampleSet)
|
||||
->E.R2.fmap(Wrappers.evDistribution)
|
||||
|
|
|
@ -31,14 +31,13 @@ let library = [
|
|||
]),
|
||||
],
|
||||
~run=(inputs, environment, _) => {
|
||||
switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.threeArgs(inputs, ("estimate", "answer", "prior")) {
|
||||
switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.threeArgs(
|
||||
inputs,
|
||||
("estimate", "answer", "prior"),
|
||||
) {
|
||||
| Ok([IEvDistribution(estimate), IEvDistribution(d), IEvDistribution(prior)]) =>
|
||||
runScoring(estimate, Score_Dist(d), Some(prior), environment)
|
||||
| Ok([
|
||||
IEvDistribution(estimate),
|
||||
IEvNumber(d),
|
||||
IEvDistribution(prior),
|
||||
]) =>
|
||||
| Ok([IEvDistribution(estimate), IEvNumber(d), IEvDistribution(prior)]) =>
|
||||
runScoring(estimate, Score_Scalar(d), Some(prior), environment)
|
||||
| Error(e) => Error(e->FunctionRegistry_Helpers.wrapError)
|
||||
| _ => Error(FunctionRegistry_Helpers.impossibleError)
|
||||
|
@ -50,7 +49,10 @@ let library = [
|
|||
~name="logScore",
|
||||
~inputs=[FRTypeRecord([("estimate", FRTypeDist), ("answer", FRTypeDistOrNumber)])],
|
||||
~run=(inputs, environment, _) => {
|
||||
switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.twoArgs(inputs, ("estimate", "answer")) {
|
||||
switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.twoArgs(
|
||||
inputs,
|
||||
("estimate", "answer"),
|
||||
) {
|
||||
| Ok([IEvDistribution(estimate), IEvDistribution(d)]) =>
|
||||
runScoring(estimate, Score_Dist(d), None, environment)
|
||||
| Ok([IEvDistribution(estimate), IEvNumber(d)]) =>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
@genType type reducerProject = ReducerProject_T.project //re-export
|
||||
@genType type reducerProjectJson = ReducerProject_T.projectJson //re-export
|
||||
|
||||
type reducerErrorValue = ForTS_Reducer_ErrorValue.reducerErrorValue //use
|
||||
|
||||
|
@ -214,6 +215,12 @@ let setEnvironment = (project: reducerProject, environment: environment): unit =
|
|||
@genType
|
||||
let getEnvironment = (project: reducerProject): environment => project->Private.getEnvironment
|
||||
|
||||
@genType
|
||||
let toJson = (project: reducerProject): reducerProjectJson => project->Private.toJson
|
||||
|
||||
@genType
|
||||
let fromJson = (project: reducerProjectJson): reducerProject => project->Private.fromJson
|
||||
|
||||
/*
|
||||
Foreign function interface is intentionally demolished.
|
||||
There is another way to do that: Umur.
|
||||
|
|
|
@ -83,19 +83,20 @@ module FRType = {
|
|||
| (FRTypeNumeric, IEvNumber(_)) => true
|
||||
| (FRTypeNumeric, IEvDistribution(Symbolic(#Float(_)))) => true
|
||||
| (FRTypeLambda, IEvLambda(_)) => true
|
||||
| (FRTypeArray(intendedType), IEvArray(elements)) => {
|
||||
elements->Belt.Array.every(v => matchWithValue(intendedType, v))
|
||||
}
|
||||
| (FRTypeArray(intendedType), IEvArray(elements)) => elements->Belt.Array.every(v =>
|
||||
matchWithValue(intendedType, v)
|
||||
)
|
||||
| (FRTypeDict(r), IEvRecord(map)) =>
|
||||
map->Belt.Map.String.valuesToArray->Belt.Array.every(v => matchWithValue(r, v))
|
||||
| (FRTypeRecord(recordParams), IEvRecord(map)) => {
|
||||
recordParams->Belt.Array.every(((name, input)) => {
|
||||
| (FRTypeRecord(recordParams), IEvRecord(map)) => recordParams->Belt.Array.every(((
|
||||
name,
|
||||
input,
|
||||
)) => {
|
||||
switch map->Belt.Map.String.get(name) {
|
||||
| Some(v) => matchWithValue(input, v)
|
||||
| None => false
|
||||
| Some(v) => matchWithValue(input, v)
|
||||
| None => false
|
||||
}
|
||||
})
|
||||
}
|
||||
| _ => false
|
||||
}
|
||||
|
||||
|
@ -104,8 +105,7 @@ module FRType = {
|
|||
if !isSameLength {
|
||||
false
|
||||
} else {
|
||||
E.A.zip(inputs, args)
|
||||
->Belt.Array.every(((input, arg)) => matchWithValue(input, arg))
|
||||
E.A.zip(inputs, args)->Belt.Array.every(((input, arg)) => matchWithValue(input, arg))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,21 +30,21 @@ module Prepare = {
|
|||
let twoArgs = (inputs: ts, (arg1: string, arg2: string)): result<ts, err> =>
|
||||
switch inputs {
|
||||
| [IEvRecord(map)] => {
|
||||
let n1 = map->Belt.Map.String.getExn(arg1)
|
||||
let n2 = map->Belt.Map.String.getExn(arg2)
|
||||
Ok([n1, n2])
|
||||
}
|
||||
let n1 = map->Belt.Map.String.getExn(arg1)
|
||||
let n2 = map->Belt.Map.String.getExn(arg2)
|
||||
Ok([n1, n2])
|
||||
}
|
||||
| _ => Error(impossibleErrorString)
|
||||
}
|
||||
|
||||
let threeArgs = (inputs: ts, (arg1: string, arg2: string, arg3: string)): result<ts, err> =>
|
||||
switch inputs {
|
||||
| [IEvRecord(map)] => {
|
||||
let n1 = map->Belt.Map.String.getExn(arg1)
|
||||
let n2 = map->Belt.Map.String.getExn(arg2)
|
||||
let n3 = map->Belt.Map.String.getExn(arg3)
|
||||
Ok([n1, n2, n3])
|
||||
}
|
||||
let n1 = map->Belt.Map.String.getExn(arg1)
|
||||
let n2 = map->Belt.Map.String.getExn(arg2)
|
||||
let n3 = map->Belt.Map.String.getExn(arg3)
|
||||
Ok([n1, n2, n3])
|
||||
}
|
||||
| _ => Error(impossibleErrorString)
|
||||
}
|
||||
}
|
||||
|
@ -108,8 +108,10 @@ module Prepare = {
|
|||
}
|
||||
|
||||
module Record = {
|
||||
let twoDistOrNumber = (values: ts, labels: (string, string)): result<(frValueDistOrNumber, frValueDistOrNumber), err> =>
|
||||
values->ToValueArray.Record.twoArgs(labels)->E.R.bind(twoDistOrNumber)
|
||||
let twoDistOrNumber = (values: ts, labels: (string, string)): result<
|
||||
(frValueDistOrNumber, frValueDistOrNumber),
|
||||
err,
|
||||
> => values->ToValueArray.Record.twoArgs(labels)->E.R.bind(twoDistOrNumber)
|
||||
|
||||
let twoDist = (values: ts, labels: (string, string)): result<
|
||||
(DistributionTypes.genericDist, DistributionTypes.genericDist),
|
||||
|
|
|
@ -147,6 +147,28 @@ let setEnvironment = (project: t, value: Reducer_T.environment): unit => {
|
|||
project.environment = value
|
||||
}
|
||||
|
||||
let toJson = (project: t): T.projectJson => {
|
||||
items: Belt.Array.map(Belt.MutableMap.String.toArray(project.items), ((id, projectItem)) => {
|
||||
let projectItem: T.projectItemJson = {
|
||||
id: id,
|
||||
source: ProjectItem.getSource(projectItem),
|
||||
continues: ProjectItem.getContinues(projectItem),
|
||||
}
|
||||
projectItem
|
||||
}),
|
||||
environment: getEnvironment(project),
|
||||
}
|
||||
|
||||
let fromJson = (json: T.projectJson): t => {
|
||||
let project = createProject()
|
||||
setEnvironment(project, json.environment)
|
||||
Belt.Array.forEach(json.items, item => {
|
||||
setSource(project, item.id, item.source)
|
||||
setContinues(project, item.id, item.continues)
|
||||
})
|
||||
project
|
||||
}
|
||||
|
||||
let getBindings = (project: t, sourceId: string): Reducer_T.namespace => {
|
||||
project->getItem(sourceId)->ProjectItem.getContinuation
|
||||
}
|
||||
|
|
|
@ -182,7 +182,6 @@ let doRun = (this: t, context: Reducer_T.context): t =>
|
|||
->setContinuation(contextAfterEvaluation.bindings->Reducer_Bindings.locals)
|
||||
} catch {
|
||||
| Reducer_ErrorValue.ErrorException(e) => this->failRun(e)
|
||||
| _ => this->failRun(RETodo("unhandled rescript exception"))
|
||||
}
|
||||
| Error(e) => this->failRun(e)
|
||||
}
|
||||
|
|
|
@ -9,6 +9,17 @@ type project = {
|
|||
}
|
||||
type t = project
|
||||
|
||||
type projectItemJson = {
|
||||
id: string,
|
||||
source: string,
|
||||
continues: array<string>,
|
||||
}
|
||||
|
||||
type projectJson = {
|
||||
items: array<projectItemJson>,
|
||||
environment: Reducer_T.environment,
|
||||
}
|
||||
|
||||
// these functions are used in ReducerProject_Topology, so they are defined here to avoid circular dependencies
|
||||
let getSourceIds = (project: t): array<string> => Belt.MutableMap.String.keysToArray(project.items)
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user