Merge pull request #437 from quantified-uncertainty/js-parameters
JS Parameter import
This commit is contained in:
commit
a2eec0b2e6
|
@ -1,19 +1,19 @@
|
|||
{
|
||||
"name": "@quri/squiggle-components",
|
||||
"version": "0.2.14",
|
||||
"version": "0.2.15",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"react-ace": "10.1.0",
|
||||
"@quri/squiggle-lang": "^0.2.7",
|
||||
"react-dom": "^18.1.0",
|
||||
"vega": "^5.22.1",
|
||||
"vega-embed": "^6.20.6",
|
||||
"vega-lite": "^5.2.0",
|
||||
"react-vega": "^7.5.0",
|
||||
"react": "^18.1.0",
|
||||
"@react-hook/size": "^2.1.2",
|
||||
"lodash": "^4.17.21",
|
||||
"styled-components": "^5.3.5"
|
||||
"react": "^18.1.0",
|
||||
"react-ace": "10.1.0",
|
||||
"react-dom": "^18.1.0",
|
||||
"react-vega": "^7.5.0",
|
||||
"styled-components": "^5.3.5",
|
||||
"vega": "^5.22.1",
|
||||
"vega-embed": "^6.20.6",
|
||||
"vega-lite": "^5.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/plugin-proposal-private-property-in-object": "^7.16.7",
|
||||
|
@ -25,27 +25,26 @@
|
|||
"@storybook/node-logger": "^6.4.22",
|
||||
"@storybook/preset-create-react-app": "^4.1.0",
|
||||
"@storybook/react": "^6.4.22",
|
||||
"@types/styled-components": "^5.1.24",
|
||||
"@types/webpack": "^5.28.0",
|
||||
"style-loader": "^3.3.1",
|
||||
"ts-loader": "^9.2.9",
|
||||
"webpack": "^5.72.0",
|
||||
"webpack-cli": "^4.9.2",
|
||||
"webpack-dev-server": "^4.8.1",
|
||||
"@testing-library/jest-dom": "^5.16.4",
|
||||
"@testing-library/react": "^13.1.1",
|
||||
"@testing-library/user-event": "^14.1.1",
|
||||
"@types/jest": "^27.4.0",
|
||||
"web-vitals": "^2.1.4",
|
||||
"@types/lodash": "^4.14.182",
|
||||
"@types/node": "^17.0.29",
|
||||
"@types/react": "^18.0.3",
|
||||
"@types/react-dom": "^18.0.2",
|
||||
"@types/styled-components": "^5.1.24",
|
||||
"@types/webpack": "^5.28.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"react-scripts": "5.0.1",
|
||||
"style-loader": "^3.3.1",
|
||||
"ts-loader": "^9.2.9",
|
||||
"tsconfig-paths-webpack-plugin": "^3.5.2",
|
||||
"typescript": "^4.6.3",
|
||||
"webpack-cli": "^4.9.2"
|
||||
"web-vitals": "^2.1.4",
|
||||
"webpack": "^5.72.0",
|
||||
"webpack-cli": "^4.9.2",
|
||||
"webpack-dev-server": "^4.8.1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "cross-env REACT_APP_FAST_REFRESH=false && start-storybook -p 6006 -s public",
|
||||
|
|
|
@ -3,16 +3,17 @@ import _ from "lodash";
|
|||
import styled from "styled-components";
|
||||
import {
|
||||
run,
|
||||
runPartial,
|
||||
errorValueToString,
|
||||
squiggleExpression,
|
||||
bindings,
|
||||
samplingParams,
|
||||
jsImports,
|
||||
defaultImports,
|
||||
defaultBindings,
|
||||
} from "@quri/squiggle-lang";
|
||||
import { NumberShower } from "./NumberShower";
|
||||
import { DistributionChart } from "./DistributionChart";
|
||||
import { ErrorBox } from "./ErrorBox";
|
||||
import useSize from "@react-hook/size";
|
||||
|
||||
const variableBox = {
|
||||
Component: styled.div`
|
||||
|
@ -152,6 +153,8 @@ export interface SquiggleChartProps {
|
|||
height?: number;
|
||||
/** Bindings of previous variables declared */
|
||||
bindings?: bindings;
|
||||
/** JS imported parameters */
|
||||
jsImports?: jsImports;
|
||||
}
|
||||
|
||||
const ChartWrapper = styled.div`
|
||||
|
@ -166,14 +169,20 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = ({
|
|||
outputXYPoints = 1000,
|
||||
onChange = () => {},
|
||||
height = 60,
|
||||
bindings = {},
|
||||
bindings = defaultBindings,
|
||||
jsImports = defaultImports,
|
||||
width = NaN,
|
||||
}: SquiggleChartProps) => {
|
||||
let samplingInputs: samplingParams = {
|
||||
sampleCount: sampleCount,
|
||||
xyPointLength: outputXYPoints,
|
||||
};
|
||||
let expressionResult = run(squiggleString, bindings, samplingInputs);
|
||||
let expressionResult = run(
|
||||
squiggleString,
|
||||
bindings,
|
||||
samplingInputs,
|
||||
jsImports
|
||||
);
|
||||
let internal: JSX.Element;
|
||||
if (expressionResult.tag === "Ok") {
|
||||
let expression = expressionResult.value;
|
||||
|
|
|
@ -3,8 +3,18 @@ import * as ReactDOM from "react-dom";
|
|||
import { SquiggleChart } from "./SquiggleChart";
|
||||
import { CodeEditor } from "./CodeEditor";
|
||||
import styled from "styled-components";
|
||||
import type { squiggleExpression, bindings } from "@quri/squiggle-lang";
|
||||
import { runPartial, errorValueToString } from "@quri/squiggle-lang";
|
||||
import type {
|
||||
squiggleExpression,
|
||||
samplingParams,
|
||||
bindings,
|
||||
jsImports,
|
||||
} from "@quri/squiggle-lang";
|
||||
import {
|
||||
runPartial,
|
||||
errorValueToString,
|
||||
defaultImports,
|
||||
defaultBindings,
|
||||
} from "@quri/squiggle-lang";
|
||||
import { ErrorBox } from "./ErrorBox";
|
||||
|
||||
export interface SquiggleEditorProps {
|
||||
|
@ -30,6 +40,8 @@ export interface SquiggleEditorProps {
|
|||
width: number;
|
||||
/** Previous variable declarations */
|
||||
bindings: bindings;
|
||||
/** JS Imports */
|
||||
jsImports: jsImports;
|
||||
}
|
||||
|
||||
const Input = styled.div`
|
||||
|
@ -50,7 +62,8 @@ export let SquiggleEditor: React.FC<SquiggleEditorProps> = ({
|
|||
diagramCount,
|
||||
onChange,
|
||||
environment,
|
||||
bindings = {},
|
||||
bindings = defaultBindings,
|
||||
jsImports = defaultImports,
|
||||
}: SquiggleEditorProps) => {
|
||||
let [expression, setExpression] = React.useState(initialSquiggleString);
|
||||
return (
|
||||
|
@ -77,6 +90,7 @@ export let SquiggleEditor: React.FC<SquiggleEditorProps> = ({
|
|||
environment={environment}
|
||||
onChange={onChange}
|
||||
bindings={bindings}
|
||||
jsImports={jsImports}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -134,16 +148,30 @@ export interface SquigglePartialProps {
|
|||
/** The width of the element */
|
||||
width: number;
|
||||
/** Previously declared variables */
|
||||
bindings: bindings;
|
||||
bindings?: bindings;
|
||||
/** Variables imported from js */
|
||||
jsImports?: jsImports;
|
||||
}
|
||||
|
||||
export let SquigglePartial: React.FC<SquigglePartialProps> = ({
|
||||
initialSquiggleString = "",
|
||||
onChange,
|
||||
bindings,
|
||||
bindings = defaultBindings,
|
||||
sampleCount = 1000,
|
||||
outputXYPoints = 1000,
|
||||
jsImports = defaultImports,
|
||||
}: SquigglePartialProps) => {
|
||||
let samplingInputs: samplingParams = {
|
||||
sampleCount: sampleCount,
|
||||
xyPointLength: outputXYPoints,
|
||||
};
|
||||
let [expression, setExpression] = React.useState(initialSquiggleString);
|
||||
let squiggleResult = runPartial(expression, bindings);
|
||||
let squiggleResult = runPartial(
|
||||
expression,
|
||||
bindings,
|
||||
samplingInputs,
|
||||
jsImports
|
||||
);
|
||||
if (squiggleResult.tag == "Ok") {
|
||||
if (onChange) onChange(squiggleResult.value);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Distribution, resultMap } from "../../src/js/index";
|
||||
import { Distribution, resultMap, defaultBindings } from "../../src/js/index";
|
||||
import { testRun, testRunPartial } from "./TestHelpers";
|
||||
|
||||
function Ok<b>(x: b) {
|
||||
|
@ -68,6 +68,28 @@ describe("Partials", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("JS Imports", () => {
|
||||
test("Can pass parameters into partials and cells", () => {
|
||||
let bindings = testRunPartial(`y = $x + 2`, defaultBindings, { x: 1 });
|
||||
let bindings2 = testRunPartial(`z = y + $a`, bindings, { a: 3 });
|
||||
expect(testRun(`z`, bindings2)).toEqual({
|
||||
tag: "number",
|
||||
value: 6,
|
||||
});
|
||||
});
|
||||
test("Complicated deep parameters", () => {
|
||||
expect(
|
||||
testRun(`$x.y[0][0].w + $x.z + $u.v`, defaultBindings, {
|
||||
x: { y: [[{ w: 1 }]], z: 2 },
|
||||
u: { v: 3 },
|
||||
})
|
||||
).toEqual({
|
||||
tag: "number",
|
||||
value: 6,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Distribution", () => {
|
||||
//It's important that sampleCount is less than 9. If it's more, than that will create randomness
|
||||
//Also, note, the value should be created using makeSampleSetDist() later on.
|
||||
|
|
|
@ -4,14 +4,25 @@ import {
|
|||
bindings,
|
||||
squiggleExpression,
|
||||
errorValueToString,
|
||||
defaultImports,
|
||||
defaultBindings,
|
||||
jsImports,
|
||||
} from "../../src/js/index";
|
||||
|
||||
export function testRun(x: string, bindings = {}): squiggleExpression {
|
||||
let squiggleResult = run(x, bindings, {
|
||||
sampleCount: 1000,
|
||||
xyPointLength: 100,
|
||||
});
|
||||
// return squiggleResult.value
|
||||
export function testRun(
|
||||
x: string,
|
||||
bindings: bindings = defaultBindings,
|
||||
imports: jsImports = defaultImports
|
||||
): squiggleExpression {
|
||||
let squiggleResult = run(
|
||||
x,
|
||||
bindings,
|
||||
{
|
||||
sampleCount: 1000,
|
||||
xyPointLength: 100,
|
||||
},
|
||||
imports
|
||||
);
|
||||
if (squiggleResult.tag === "Ok") {
|
||||
return squiggleResult.value;
|
||||
} else {
|
||||
|
@ -23,11 +34,20 @@ export function testRun(x: string, bindings = {}): squiggleExpression {
|
|||
}
|
||||
}
|
||||
|
||||
export function testRunPartial(x: string, bindings: bindings = {}): bindings {
|
||||
let squiggleResult = runPartial(x, bindings, {
|
||||
sampleCount: 1000,
|
||||
xyPointLength: 100,
|
||||
});
|
||||
export function testRunPartial(
|
||||
x: string,
|
||||
bindings: bindings = defaultBindings,
|
||||
imports: jsImports = defaultImports
|
||||
): bindings {
|
||||
let squiggleResult = runPartial(
|
||||
x,
|
||||
bindings,
|
||||
{
|
||||
sampleCount: 1000,
|
||||
xyPointLength: 100,
|
||||
},
|
||||
imports
|
||||
);
|
||||
if (squiggleResult.tag === "Ok") {
|
||||
return squiggleResult.value;
|
||||
} else {
|
||||
|
|
247
packages/squiggle-lang/src/js/distribution.ts
Normal file
247
packages/squiggle-lang/src/js/distribution.ts
Normal file
|
@ -0,0 +1,247 @@
|
|||
import * as _ from "lodash";
|
||||
import {
|
||||
genericDist,
|
||||
continuousShape,
|
||||
discreteShape,
|
||||
samplingParams,
|
||||
distributionError,
|
||||
toPointSet,
|
||||
distributionErrorToString,
|
||||
} from "../rescript/TypescriptInterface.gen";
|
||||
import { result, resultMap, Ok } from "./types";
|
||||
import {
|
||||
Constructors_mean,
|
||||
Constructors_sample,
|
||||
Constructors_pdf,
|
||||
Constructors_cdf,
|
||||
Constructors_inv,
|
||||
Constructors_normalize,
|
||||
Constructors_isNormalized,
|
||||
Constructors_toPointSet,
|
||||
Constructors_toSampleSet,
|
||||
Constructors_truncate,
|
||||
Constructors_inspect,
|
||||
Constructors_toString,
|
||||
Constructors_toSparkline,
|
||||
Constructors_algebraicAdd,
|
||||
Constructors_algebraicMultiply,
|
||||
Constructors_algebraicDivide,
|
||||
Constructors_algebraicSubtract,
|
||||
Constructors_algebraicLogarithm,
|
||||
Constructors_algebraicPower,
|
||||
Constructors_pointwiseAdd,
|
||||
Constructors_pointwiseMultiply,
|
||||
Constructors_pointwiseDivide,
|
||||
Constructors_pointwiseSubtract,
|
||||
Constructors_pointwiseLogarithm,
|
||||
Constructors_pointwisePower,
|
||||
} from "../rescript/Distributions/DistributionOperation/DistributionOperation.gen";
|
||||
|
||||
export type point = { x: number; y: number };
|
||||
|
||||
function shapePoints(x: continuousShape | discreteShape): point[] {
|
||||
let xs = x.xyShape.xs;
|
||||
let ys = x.xyShape.ys;
|
||||
return _.zipWith(xs, ys, (x, y) => ({ x, y }));
|
||||
}
|
||||
export type shape = {
|
||||
continuous: point[];
|
||||
discrete: point[];
|
||||
};
|
||||
|
||||
export class Distribution {
|
||||
t: genericDist;
|
||||
env: samplingParams;
|
||||
|
||||
constructor(t: genericDist, env: samplingParams) {
|
||||
this.t = t;
|
||||
this.env = env;
|
||||
return this;
|
||||
}
|
||||
|
||||
mapResultDist(
|
||||
r: result<genericDist, distributionError>
|
||||
): result<Distribution, distributionError> {
|
||||
return resultMap(r, (v: genericDist) => new Distribution(v, this.env));
|
||||
}
|
||||
|
||||
mean(): result<number, distributionError> {
|
||||
return Constructors_mean({ env: this.env }, this.t);
|
||||
}
|
||||
|
||||
sample(): result<number, distributionError> {
|
||||
return Constructors_sample({ env: this.env }, this.t);
|
||||
}
|
||||
|
||||
pdf(n: number): result<number, distributionError> {
|
||||
return Constructors_pdf({ env: this.env }, this.t, n);
|
||||
}
|
||||
|
||||
cdf(n: number): result<number, distributionError> {
|
||||
return Constructors_cdf({ env: this.env }, this.t, n);
|
||||
}
|
||||
|
||||
inv(n: number): result<number, distributionError> {
|
||||
return Constructors_inv({ env: this.env }, this.t, n);
|
||||
}
|
||||
|
||||
isNormalized(): result<boolean, distributionError> {
|
||||
return Constructors_isNormalized({ env: this.env }, this.t);
|
||||
}
|
||||
|
||||
normalize(): result<Distribution, distributionError> {
|
||||
return this.mapResultDist(
|
||||
Constructors_normalize({ env: this.env }, this.t)
|
||||
);
|
||||
}
|
||||
|
||||
type() {
|
||||
return this.t.tag;
|
||||
}
|
||||
|
||||
pointSet(): result<shape, distributionError> {
|
||||
let pointSet = toPointSet(
|
||||
this.t,
|
||||
{
|
||||
xyPointLength: this.env.xyPointLength,
|
||||
sampleCount: this.env.sampleCount,
|
||||
},
|
||||
undefined
|
||||
);
|
||||
if (pointSet.tag === "Ok") {
|
||||
let distribution = pointSet.value;
|
||||
if (distribution.tag === "Continuous") {
|
||||
return Ok({
|
||||
continuous: shapePoints(distribution.value),
|
||||
discrete: [],
|
||||
});
|
||||
} else if (distribution.tag === "Discrete") {
|
||||
return Ok({
|
||||
discrete: shapePoints(distribution.value),
|
||||
continuous: [],
|
||||
});
|
||||
} else {
|
||||
return Ok({
|
||||
discrete: shapePoints(distribution.value.discrete),
|
||||
continuous: shapePoints(distribution.value.continuous),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return pointSet;
|
||||
}
|
||||
}
|
||||
|
||||
toPointSet(): result<Distribution, distributionError> {
|
||||
return this.mapResultDist(
|
||||
Constructors_toPointSet({ env: this.env }, this.t)
|
||||
);
|
||||
}
|
||||
|
||||
toSampleSet(n: number): result<Distribution, distributionError> {
|
||||
return this.mapResultDist(
|
||||
Constructors_toSampleSet({ env: this.env }, this.t, n)
|
||||
);
|
||||
}
|
||||
|
||||
truncate(
|
||||
left: number,
|
||||
right: number
|
||||
): result<Distribution, distributionError> {
|
||||
return this.mapResultDist(
|
||||
Constructors_truncate({ env: this.env }, this.t, left, right)
|
||||
);
|
||||
}
|
||||
|
||||
inspect(): result<Distribution, distributionError> {
|
||||
return this.mapResultDist(Constructors_inspect({ env: this.env }, this.t));
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
let result = Constructors_toString({ env: this.env }, this.t);
|
||||
if (result.tag === "Ok") {
|
||||
return result.value;
|
||||
} else {
|
||||
return distributionErrorToString(result.value);
|
||||
}
|
||||
}
|
||||
|
||||
toSparkline(n: number): result<string, distributionError> {
|
||||
return Constructors_toSparkline({ env: this.env }, this.t, n);
|
||||
}
|
||||
|
||||
algebraicAdd(d2: Distribution): result<Distribution, distributionError> {
|
||||
return this.mapResultDist(
|
||||
Constructors_algebraicAdd({ env: this.env }, this.t, d2.t)
|
||||
);
|
||||
}
|
||||
|
||||
algebraicMultiply(d2: Distribution): result<Distribution, distributionError> {
|
||||
return this.mapResultDist(
|
||||
Constructors_algebraicMultiply({ env: this.env }, this.t, d2.t)
|
||||
);
|
||||
}
|
||||
|
||||
algebraicDivide(d2: Distribution): result<Distribution, distributionError> {
|
||||
return this.mapResultDist(
|
||||
Constructors_algebraicDivide({ env: this.env }, this.t, d2.t)
|
||||
);
|
||||
}
|
||||
|
||||
algebraicSubtract(d2: Distribution): result<Distribution, distributionError> {
|
||||
return this.mapResultDist(
|
||||
Constructors_algebraicSubtract({ env: this.env }, this.t, d2.t)
|
||||
);
|
||||
}
|
||||
|
||||
algebraicLogarithm(
|
||||
d2: Distribution
|
||||
): result<Distribution, distributionError> {
|
||||
return this.mapResultDist(
|
||||
Constructors_algebraicLogarithm({ env: this.env }, this.t, d2.t)
|
||||
);
|
||||
}
|
||||
|
||||
algebraicPower(d2: Distribution): result<Distribution, distributionError> {
|
||||
return this.mapResultDist(
|
||||
Constructors_algebraicPower({ env: this.env }, this.t, d2.t)
|
||||
);
|
||||
}
|
||||
|
||||
pointwiseAdd(d2: Distribution): result<Distribution, distributionError> {
|
||||
return this.mapResultDist(
|
||||
Constructors_pointwiseAdd({ env: this.env }, this.t, d2.t)
|
||||
);
|
||||
}
|
||||
|
||||
pointwiseMultiply(d2: Distribution): result<Distribution, distributionError> {
|
||||
return this.mapResultDist(
|
||||
Constructors_pointwiseMultiply({ env: this.env }, this.t, d2.t)
|
||||
);
|
||||
}
|
||||
|
||||
pointwiseDivide(d2: Distribution): result<Distribution, distributionError> {
|
||||
return this.mapResultDist(
|
||||
Constructors_pointwiseDivide({ env: this.env }, this.t, d2.t)
|
||||
);
|
||||
}
|
||||
|
||||
pointwiseSubtract(d2: Distribution): result<Distribution, distributionError> {
|
||||
return this.mapResultDist(
|
||||
Constructors_pointwiseSubtract({ env: this.env }, this.t, d2.t)
|
||||
);
|
||||
}
|
||||
|
||||
pointwiseLogarithm(
|
||||
d2: Distribution
|
||||
): result<Distribution, distributionError> {
|
||||
return this.mapResultDist(
|
||||
Constructors_pointwiseLogarithm({ env: this.env }, this.t, d2.t)
|
||||
);
|
||||
}
|
||||
|
||||
pointwisePower(d2: Distribution): result<Distribution, distributionError> {
|
||||
return this.mapResultDist(
|
||||
Constructors_pointwisePower({ env: this.env }, this.t, d2.t)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,125 +1,91 @@
|
|||
import * as _ from "lodash";
|
||||
import {
|
||||
genericDist,
|
||||
samplingParams,
|
||||
evaluateUsingExternalBindings,
|
||||
evaluatePartialUsingExternalBindings,
|
||||
externalBindings,
|
||||
expressionValue,
|
||||
errorValue,
|
||||
distributionError,
|
||||
toPointSet,
|
||||
continuousShape,
|
||||
discreteShape,
|
||||
distributionErrorToString,
|
||||
mixedShape,
|
||||
sampleSetDist,
|
||||
symbolicDist,
|
||||
} from "../rescript/TypescriptInterface.gen";
|
||||
export {
|
||||
makeSampleSetDist,
|
||||
errorValueToString,
|
||||
distributionErrorToString,
|
||||
} from "../rescript/TypescriptInterface.gen";
|
||||
export type {
|
||||
samplingParams,
|
||||
errorValue,
|
||||
externalBindings as bindings,
|
||||
jsImports,
|
||||
};
|
||||
import {
|
||||
Constructors_mean,
|
||||
Constructors_sample,
|
||||
Constructors_pdf,
|
||||
Constructors_cdf,
|
||||
Constructors_inv,
|
||||
Constructors_normalize,
|
||||
Constructors_isNormalized,
|
||||
Constructors_toPointSet,
|
||||
Constructors_toSampleSet,
|
||||
Constructors_truncate,
|
||||
Constructors_inspect,
|
||||
Constructors_toString,
|
||||
Constructors_toSparkline,
|
||||
Constructors_algebraicAdd,
|
||||
Constructors_algebraicMultiply,
|
||||
Constructors_algebraicDivide,
|
||||
Constructors_algebraicSubtract,
|
||||
Constructors_algebraicLogarithm,
|
||||
Constructors_algebraicPower,
|
||||
Constructors_pointwiseAdd,
|
||||
Constructors_pointwiseMultiply,
|
||||
Constructors_pointwiseDivide,
|
||||
Constructors_pointwiseSubtract,
|
||||
Constructors_pointwiseLogarithm,
|
||||
Constructors_pointwisePower,
|
||||
} from "../rescript/Distributions/DistributionOperation/DistributionOperation.gen";
|
||||
export type { samplingParams, errorValue, externalBindings as bindings };
|
||||
jsValueToBinding,
|
||||
jsValue,
|
||||
rescriptExport,
|
||||
squiggleExpression,
|
||||
convertRawToTypescript,
|
||||
} from "./rescript_interop";
|
||||
import { result, resultMap, tag, tagged } from "./types";
|
||||
import { Distribution } from "./distribution";
|
||||
|
||||
export { Distribution, squiggleExpression, result, resultMap };
|
||||
|
||||
export let defaultSamplingInputs: samplingParams = {
|
||||
sampleCount: 10000,
|
||||
xyPointLength: 10000,
|
||||
};
|
||||
|
||||
export type result<a, b> =
|
||||
| {
|
||||
tag: "Ok";
|
||||
value: a;
|
||||
}
|
||||
| {
|
||||
tag: "Error";
|
||||
value: b;
|
||||
};
|
||||
|
||||
export function resultMap<a, b, c>(
|
||||
r: result<a, c>,
|
||||
mapFn: (x: a) => b
|
||||
): result<b, c> {
|
||||
if (r.tag === "Ok") {
|
||||
return { tag: "Ok", value: mapFn(r.value) };
|
||||
} else {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
function Ok<a, b>(x: a): result<a, b> {
|
||||
return { tag: "Ok", value: x };
|
||||
}
|
||||
|
||||
type tagged<a, b> = { tag: a; value: b };
|
||||
|
||||
function tag<a, b>(x: a, y: b): tagged<a, b> {
|
||||
return { tag: x, value: y };
|
||||
}
|
||||
|
||||
export type squiggleExpression =
|
||||
| tagged<"symbol", string>
|
||||
| tagged<"string", string>
|
||||
| tagged<"call", string>
|
||||
| tagged<"array", squiggleExpression[]>
|
||||
| tagged<"boolean", boolean>
|
||||
| tagged<"distribution", Distribution>
|
||||
| tagged<"number", number>
|
||||
| tagged<"record", { [key: string]: squiggleExpression }>;
|
||||
|
||||
export function run(
|
||||
squiggleString: string,
|
||||
bindings?: externalBindings,
|
||||
samplingInputs?: samplingParams
|
||||
samplingInputs?: samplingParams,
|
||||
imports?: jsImports
|
||||
): result<squiggleExpression, errorValue> {
|
||||
let b = bindings ? bindings : {};
|
||||
let b = bindings ? bindings : defaultBindings;
|
||||
let i = imports ? imports : defaultImports;
|
||||
let si: samplingParams = samplingInputs
|
||||
? samplingInputs
|
||||
: defaultSamplingInputs;
|
||||
|
||||
let result: result<expressionValue, errorValue> =
|
||||
evaluateUsingExternalBindings(squiggleString, b);
|
||||
evaluateUsingExternalBindings(squiggleString, mergeImports(b, i));
|
||||
return resultMap(result, (x) => createTsExport(x, si));
|
||||
}
|
||||
|
||||
// Run Partial. A partial is a block of code that doesn't return a value
|
||||
export function runPartial(
|
||||
squiggleString: string,
|
||||
bindings: externalBindings,
|
||||
_samplingInputs?: samplingParams
|
||||
bindings?: externalBindings,
|
||||
_samplingInputs?: samplingParams,
|
||||
imports?: jsImports
|
||||
): result<externalBindings, errorValue> {
|
||||
return evaluatePartialUsingExternalBindings(squiggleString, bindings);
|
||||
let b = bindings ? bindings : defaultBindings;
|
||||
let i = imports ? imports : defaultImports;
|
||||
|
||||
return evaluatePartialUsingExternalBindings(
|
||||
squiggleString,
|
||||
mergeImports(b, i)
|
||||
);
|
||||
}
|
||||
|
||||
function mergeImports(
|
||||
bindings: externalBindings,
|
||||
imports: jsImports
|
||||
): externalBindings {
|
||||
let transformedImports = Object.fromEntries(
|
||||
Object.entries(imports).map(([key, value]) => [
|
||||
"$" + key,
|
||||
jsValueToBinding(value),
|
||||
])
|
||||
);
|
||||
return _.merge(bindings, transformedImports);
|
||||
}
|
||||
|
||||
type jsImports = { [key: string]: jsValue };
|
||||
|
||||
export let defaultImports: jsImports = {};
|
||||
export let defaultBindings: externalBindings = {};
|
||||
|
||||
function createTsExport(
|
||||
x: expressionValue,
|
||||
sampEnv: samplingParams
|
||||
|
@ -180,329 +146,3 @@ function createTsExport(
|
|||
return tag("symbol", x.value);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper functions to convert the rescript representations that genType doesn't
|
||||
// cover
|
||||
function convertRawToTypescript(
|
||||
result: rescriptExport,
|
||||
sampEnv: samplingParams
|
||||
): squiggleExpression {
|
||||
switch (result.TAG) {
|
||||
case 0: // EvArray
|
||||
return tag(
|
||||
"array",
|
||||
result._0.map((x) => convertRawToTypescript(x, sampEnv))
|
||||
);
|
||||
case 1: // EvBool
|
||||
return tag("boolean", result._0);
|
||||
case 2: // EvCall
|
||||
return tag("call", result._0);
|
||||
case 3: // EvDistribution
|
||||
return tag(
|
||||
"distribution",
|
||||
new Distribution(
|
||||
convertRawDistributionToGenericDist(result._0),
|
||||
sampEnv
|
||||
)
|
||||
);
|
||||
case 4: // EvNumber
|
||||
return tag("number", result._0);
|
||||
case 5: // EvRecord
|
||||
return tag(
|
||||
"record",
|
||||
_.mapValues(result._0, (x) => convertRawToTypescript(x, sampEnv))
|
||||
);
|
||||
case 6: // EvString
|
||||
return tag("string", result._0);
|
||||
case 7: // EvSymbol
|
||||
return tag("symbol", result._0);
|
||||
}
|
||||
}
|
||||
|
||||
function convertRawDistributionToGenericDist(
|
||||
result: rescriptDist
|
||||
): genericDist {
|
||||
switch (result.TAG) {
|
||||
case 0: // Point Set Dist
|
||||
switch (result._0.TAG) {
|
||||
case 0: // Mixed
|
||||
return tag("PointSet", tag("Mixed", result._0._0));
|
||||
case 1: // Discrete
|
||||
return tag("PointSet", tag("Discrete", result._0._0));
|
||||
case 2: // Continuous
|
||||
return tag("PointSet", tag("Continuous", result._0._0));
|
||||
}
|
||||
case 1: // Sample Set Dist
|
||||
return tag("SampleSet", result._0);
|
||||
case 2: // Symbolic Dist
|
||||
return tag("Symbolic", result._0);
|
||||
}
|
||||
}
|
||||
|
||||
// Raw rescript types.
|
||||
type rescriptExport =
|
||||
| {
|
||||
TAG: 0; // EvArray
|
||||
_0: rescriptExport[];
|
||||
}
|
||||
| {
|
||||
TAG: 1; // EvBool
|
||||
_0: boolean;
|
||||
}
|
||||
| {
|
||||
TAG: 2; // EvCall
|
||||
_0: string;
|
||||
}
|
||||
| {
|
||||
TAG: 3; // EvDistribution
|
||||
_0: rescriptDist;
|
||||
}
|
||||
| {
|
||||
TAG: 4; // EvNumber
|
||||
_0: number;
|
||||
}
|
||||
| {
|
||||
TAG: 5; // EvRecord
|
||||
_0: { [key: string]: rescriptExport };
|
||||
}
|
||||
| {
|
||||
TAG: 6; // EvString
|
||||
_0: string;
|
||||
}
|
||||
| {
|
||||
TAG: 7; // EvSymbol
|
||||
_0: string;
|
||||
};
|
||||
|
||||
type rescriptDist =
|
||||
| { TAG: 0; _0: rescriptPointSetDist }
|
||||
| { TAG: 1; _0: sampleSetDist }
|
||||
| { TAG: 2; _0: symbolicDist };
|
||||
|
||||
type rescriptPointSetDist =
|
||||
| {
|
||||
TAG: 0; // Mixed
|
||||
_0: mixedShape;
|
||||
}
|
||||
| {
|
||||
TAG: 1; // Discrete
|
||||
_0: discreteShape;
|
||||
}
|
||||
| {
|
||||
TAG: 2; // ContinuousShape
|
||||
_0: continuousShape;
|
||||
};
|
||||
|
||||
export function resultExn<a, c>(r: result<a, c>): a | c {
|
||||
return r.value;
|
||||
}
|
||||
|
||||
export type point = { x: number; y: number };
|
||||
|
||||
export type shape = {
|
||||
continuous: point[];
|
||||
discrete: point[];
|
||||
};
|
||||
|
||||
function shapePoints(x: continuousShape | discreteShape): point[] {
|
||||
let xs = x.xyShape.xs;
|
||||
let ys = x.xyShape.ys;
|
||||
return _.zipWith(xs, ys, (x, y) => ({ x, y }));
|
||||
}
|
||||
|
||||
export class Distribution {
|
||||
t: genericDist;
|
||||
env: samplingParams;
|
||||
|
||||
constructor(t: genericDist, env: samplingParams) {
|
||||
this.t = t;
|
||||
this.env = env;
|
||||
return this;
|
||||
}
|
||||
|
||||
mapResultDist(
|
||||
r: result<genericDist, distributionError>
|
||||
): result<Distribution, distributionError> {
|
||||
return resultMap(r, (v: genericDist) => new Distribution(v, this.env));
|
||||
}
|
||||
|
||||
mean(): result<number, distributionError> {
|
||||
return Constructors_mean({ env: this.env }, this.t);
|
||||
}
|
||||
|
||||
sample(): result<number, distributionError> {
|
||||
return Constructors_sample({ env: this.env }, this.t);
|
||||
}
|
||||
|
||||
pdf(n: number): result<number, distributionError> {
|
||||
return Constructors_pdf({ env: this.env }, this.t, n);
|
||||
}
|
||||
|
||||
cdf(n: number): result<number, distributionError> {
|
||||
return Constructors_cdf({ env: this.env }, this.t, n);
|
||||
}
|
||||
|
||||
inv(n: number): result<number, distributionError> {
|
||||
return Constructors_inv({ env: this.env }, this.t, n);
|
||||
}
|
||||
|
||||
isNormalized(): result<boolean, distributionError> {
|
||||
return Constructors_isNormalized({ env: this.env }, this.t);
|
||||
}
|
||||
|
||||
normalize(): result<Distribution, distributionError> {
|
||||
return this.mapResultDist(
|
||||
Constructors_normalize({ env: this.env }, this.t)
|
||||
);
|
||||
}
|
||||
|
||||
type() {
|
||||
return this.t.tag;
|
||||
}
|
||||
|
||||
pointSet(): result<shape, distributionError> {
|
||||
let pointSet = toPointSet(
|
||||
this.t,
|
||||
{
|
||||
xyPointLength: this.env.xyPointLength,
|
||||
sampleCount: this.env.sampleCount,
|
||||
},
|
||||
undefined
|
||||
);
|
||||
if (pointSet.tag === "Ok") {
|
||||
let distribution = pointSet.value;
|
||||
if (distribution.tag === "Continuous") {
|
||||
return Ok({
|
||||
continuous: shapePoints(distribution.value),
|
||||
discrete: [],
|
||||
});
|
||||
} else if (distribution.tag === "Discrete") {
|
||||
return Ok({
|
||||
discrete: shapePoints(distribution.value),
|
||||
continuous: [],
|
||||
});
|
||||
} else {
|
||||
return Ok({
|
||||
discrete: shapePoints(distribution.value.discrete),
|
||||
continuous: shapePoints(distribution.value.continuous),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return pointSet;
|
||||
}
|
||||
}
|
||||
|
||||
toPointSet(): result<Distribution, distributionError> {
|
||||
return this.mapResultDist(
|
||||
Constructors_toPointSet({ env: this.env }, this.t)
|
||||
);
|
||||
}
|
||||
|
||||
toSampleSet(n: number): result<Distribution, distributionError> {
|
||||
return this.mapResultDist(
|
||||
Constructors_toSampleSet({ env: this.env }, this.t, n)
|
||||
);
|
||||
}
|
||||
|
||||
truncate(
|
||||
left: number,
|
||||
right: number
|
||||
): result<Distribution, distributionError> {
|
||||
return this.mapResultDist(
|
||||
Constructors_truncate({ env: this.env }, this.t, left, right)
|
||||
);
|
||||
}
|
||||
|
||||
inspect(): result<Distribution, distributionError> {
|
||||
return this.mapResultDist(Constructors_inspect({ env: this.env }, this.t));
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
let result = Constructors_toString({ env: this.env }, this.t);
|
||||
if (result.tag === "Ok") {
|
||||
return result.value;
|
||||
} else {
|
||||
return distributionErrorToString(result.value);
|
||||
}
|
||||
}
|
||||
|
||||
toSparkline(n: number): result<string, distributionError> {
|
||||
return Constructors_toSparkline({ env: this.env }, this.t, n);
|
||||
}
|
||||
|
||||
algebraicAdd(d2: Distribution): result<Distribution, distributionError> {
|
||||
return this.mapResultDist(
|
||||
Constructors_algebraicAdd({ env: this.env }, this.t, d2.t)
|
||||
);
|
||||
}
|
||||
|
||||
algebraicMultiply(d2: Distribution): result<Distribution, distributionError> {
|
||||
return this.mapResultDist(
|
||||
Constructors_algebraicMultiply({ env: this.env }, this.t, d2.t)
|
||||
);
|
||||
}
|
||||
|
||||
algebraicDivide(d2: Distribution): result<Distribution, distributionError> {
|
||||
return this.mapResultDist(
|
||||
Constructors_algebraicDivide({ env: this.env }, this.t, d2.t)
|
||||
);
|
||||
}
|
||||
|
||||
algebraicSubtract(d2: Distribution): result<Distribution, distributionError> {
|
||||
return this.mapResultDist(
|
||||
Constructors_algebraicSubtract({ env: this.env }, this.t, d2.t)
|
||||
);
|
||||
}
|
||||
|
||||
algebraicLogarithm(
|
||||
d2: Distribution
|
||||
): result<Distribution, distributionError> {
|
||||
return this.mapResultDist(
|
||||
Constructors_algebraicLogarithm({ env: this.env }, this.t, d2.t)
|
||||
);
|
||||
}
|
||||
|
||||
algebraicPower(d2: Distribution): result<Distribution, distributionError> {
|
||||
return this.mapResultDist(
|
||||
Constructors_algebraicPower({ env: this.env }, this.t, d2.t)
|
||||
);
|
||||
}
|
||||
|
||||
pointwiseAdd(d2: Distribution): result<Distribution, distributionError> {
|
||||
return this.mapResultDist(
|
||||
Constructors_pointwiseAdd({ env: this.env }, this.t, d2.t)
|
||||
);
|
||||
}
|
||||
|
||||
pointwiseMultiply(d2: Distribution): result<Distribution, distributionError> {
|
||||
return this.mapResultDist(
|
||||
Constructors_pointwiseMultiply({ env: this.env }, this.t, d2.t)
|
||||
);
|
||||
}
|
||||
|
||||
pointwiseDivide(d2: Distribution): result<Distribution, distributionError> {
|
||||
return this.mapResultDist(
|
||||
Constructors_pointwiseDivide({ env: this.env }, this.t, d2.t)
|
||||
);
|
||||
}
|
||||
|
||||
pointwiseSubtract(d2: Distribution): result<Distribution, distributionError> {
|
||||
return this.mapResultDist(
|
||||
Constructors_pointwiseSubtract({ env: this.env }, this.t, d2.t)
|
||||
);
|
||||
}
|
||||
|
||||
pointwiseLogarithm(
|
||||
d2: Distribution
|
||||
): result<Distribution, distributionError> {
|
||||
return this.mapResultDist(
|
||||
Constructors_pointwiseLogarithm({ env: this.env }, this.t, d2.t)
|
||||
);
|
||||
}
|
||||
|
||||
pointwisePower(d2: Distribution): result<Distribution, distributionError> {
|
||||
return this.mapResultDist(
|
||||
Constructors_pointwisePower({ env: this.env }, this.t, d2.t)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
155
packages/squiggle-lang/src/js/rescript_interop.ts
Normal file
155
packages/squiggle-lang/src/js/rescript_interop.ts
Normal file
|
@ -0,0 +1,155 @@
|
|||
import * as _ from "lodash";
|
||||
import {
|
||||
mixedShape,
|
||||
sampleSetDist,
|
||||
genericDist,
|
||||
samplingParams,
|
||||
symbolicDist,
|
||||
discreteShape,
|
||||
continuousShape,
|
||||
} from "../rescript/TypescriptInterface.gen";
|
||||
import { Distribution } from "./distribution";
|
||||
import { tagged, tag } from "./types";
|
||||
// This file is here to compensate for genType not fully recursively converting types
|
||||
|
||||
// Raw rescript types.
|
||||
export type rescriptExport =
|
||||
| {
|
||||
TAG: 0; // EvArray
|
||||
_0: rescriptExport[];
|
||||
}
|
||||
| {
|
||||
TAG: 1; // EvBool
|
||||
_0: boolean;
|
||||
}
|
||||
| {
|
||||
TAG: 2; // EvCall
|
||||
_0: string;
|
||||
}
|
||||
| {
|
||||
TAG: 3; // EvDistribution
|
||||
_0: rescriptDist;
|
||||
}
|
||||
| {
|
||||
TAG: 4; // EvNumber
|
||||
_0: number;
|
||||
}
|
||||
| {
|
||||
TAG: 5; // EvRecord
|
||||
_0: { [key: string]: rescriptExport };
|
||||
}
|
||||
| {
|
||||
TAG: 6; // EvString
|
||||
_0: string;
|
||||
}
|
||||
| {
|
||||
TAG: 7; // EvSymbol
|
||||
_0: string;
|
||||
};
|
||||
|
||||
type rescriptDist =
|
||||
| { TAG: 0; _0: rescriptPointSetDist }
|
||||
| { TAG: 1; _0: sampleSetDist }
|
||||
| { TAG: 2; _0: symbolicDist };
|
||||
|
||||
type rescriptPointSetDist =
|
||||
| {
|
||||
TAG: 0; // Mixed
|
||||
_0: mixedShape;
|
||||
}
|
||||
| {
|
||||
TAG: 1; // Discrete
|
||||
_0: discreteShape;
|
||||
}
|
||||
| {
|
||||
TAG: 2; // ContinuousShape
|
||||
_0: continuousShape;
|
||||
};
|
||||
|
||||
export type squiggleExpression =
|
||||
| tagged<"symbol", string>
|
||||
| tagged<"string", string>
|
||||
| tagged<"call", string>
|
||||
| tagged<"array", squiggleExpression[]>
|
||||
| tagged<"boolean", boolean>
|
||||
| tagged<"distribution", Distribution>
|
||||
| tagged<"number", number>
|
||||
| tagged<"record", { [key: string]: squiggleExpression }>;
|
||||
|
||||
export function convertRawToTypescript(
|
||||
result: rescriptExport,
|
||||
sampEnv: samplingParams
|
||||
): squiggleExpression {
|
||||
switch (result.TAG) {
|
||||
case 0: // EvArray
|
||||
return tag(
|
||||
"array",
|
||||
result._0.map((x) => convertRawToTypescript(x, sampEnv))
|
||||
);
|
||||
case 1: // EvBool
|
||||
return tag("boolean", result._0);
|
||||
case 2: // EvCall
|
||||
return tag("call", result._0);
|
||||
case 3: // EvDistribution
|
||||
return tag(
|
||||
"distribution",
|
||||
new Distribution(
|
||||
convertRawDistributionToGenericDist(result._0),
|
||||
sampEnv
|
||||
)
|
||||
);
|
||||
case 4: // EvNumber
|
||||
return tag("number", result._0);
|
||||
case 5: // EvRecord
|
||||
return tag(
|
||||
"record",
|
||||
_.mapValues(result._0, (x) => convertRawToTypescript(x, sampEnv))
|
||||
);
|
||||
case 6: // EvString
|
||||
return tag("string", result._0);
|
||||
case 7: // EvSymbol
|
||||
return tag("symbol", result._0);
|
||||
}
|
||||
}
|
||||
|
||||
function convertRawDistributionToGenericDist(
|
||||
result: rescriptDist
|
||||
): genericDist {
|
||||
switch (result.TAG) {
|
||||
case 0: // Point Set Dist
|
||||
switch (result._0.TAG) {
|
||||
case 0: // Mixed
|
||||
return tag("PointSet", tag("Mixed", result._0._0));
|
||||
case 1: // Discrete
|
||||
return tag("PointSet", tag("Discrete", result._0._0));
|
||||
case 2: // Continuous
|
||||
return tag("PointSet", tag("Continuous", result._0._0));
|
||||
}
|
||||
case 1: // Sample Set Dist
|
||||
return tag("SampleSet", result._0);
|
||||
case 2: // Symbolic Dist
|
||||
return tag("Symbolic", result._0);
|
||||
}
|
||||
}
|
||||
|
||||
export type jsValue =
|
||||
| string
|
||||
| number
|
||||
| jsValue[]
|
||||
| { [key: string]: jsValue }
|
||||
| boolean;
|
||||
|
||||
export function jsValueToBinding(value: jsValue): rescriptExport {
|
||||
if (typeof value === "boolean") {
|
||||
return { TAG: 1, _0: value as boolean };
|
||||
} else if (typeof value === "string") {
|
||||
return { TAG: 6, _0: value as string };
|
||||
} else if (typeof value === "number") {
|
||||
return { TAG: 4, _0: value as number };
|
||||
} else if (Array.isArray(value)) {
|
||||
return { TAG: 0, _0: value.map(jsValueToBinding) };
|
||||
} else {
|
||||
// Record
|
||||
return { TAG: 5, _0: _.mapValues(value, jsValueToBinding) };
|
||||
}
|
||||
}
|
30
packages/squiggle-lang/src/js/types.ts
Normal file
30
packages/squiggle-lang/src/js/types.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
export type result<a, b> =
|
||||
| {
|
||||
tag: "Ok";
|
||||
value: a;
|
||||
}
|
||||
| {
|
||||
tag: "Error";
|
||||
value: b;
|
||||
};
|
||||
|
||||
export function resultMap<a, b, c>(
|
||||
r: result<a, c>,
|
||||
mapFn: (x: a) => b
|
||||
): result<b, c> {
|
||||
if (r.tag === "Ok") {
|
||||
return { tag: "Ok", value: mapFn(r.value) };
|
||||
} else {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
export function Ok<a, b>(x: a): result<a, b> {
|
||||
return { tag: "Ok", value: x };
|
||||
}
|
||||
|
||||
export type tagged<a, b> = { tag: a; value: b };
|
||||
|
||||
export function tag<a, b>(x: a, y: b): tagged<a, b> {
|
||||
return { tag: x, value: y };
|
||||
}
|
|
@ -350,7 +350,7 @@ module JsDate = {
|
|||
/* List */
|
||||
module L = {
|
||||
module Util = {
|
||||
let eq = (a, b) => a == b
|
||||
let eq = \"=="
|
||||
}
|
||||
let fmap = List.map
|
||||
let get = Belt.List.get
|
||||
|
|
Loading…
Reference in New Issue
Block a user