Merge branch 'develop' into Umur-reducer-dev
This commit is contained in:
commit
711b230ecb
|
@ -1,9 +1,9 @@
|
|||
[![npm version](https://badge.fury.io/js/@quri%2Fsquiggle-components.svg)](https://www.npmjs.com/package/@quri/squiggle-components)
|
||||
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/quantified-uncertainty/squiggle/blob/develop/LICENSE)
|
||||
|
||||
# Squiggle Components
|
||||
# Squiggle components
|
||||
|
||||
This package contains all the components for squiggle. These can be used either as a library or hosted as a [storybook](https://storybook.js.org/).
|
||||
This package contains the react components for squiggle. These can be used either as a library or hosted as a [storybook](https://storybook.js.org/).
|
||||
|
||||
# Usage in a `react` project
|
||||
|
||||
|
@ -17,7 +17,10 @@ Add to `App.js`:
|
|||
|
||||
```jsx
|
||||
import { SquiggleEditor } from "@quri/squiggle-components";
|
||||
<SquiggleEditor initialSquiggleString="x = beta(3, 10); x + 20" />;
|
||||
<SquiggleEditor
|
||||
initialSquiggleString="x = beta($alpha, 10); x + $shift"
|
||||
jsImports={{ alpha: 3, shift: 20 }}
|
||||
/>;
|
||||
```
|
||||
|
||||
# Build storybook for development
|
||||
|
@ -38,9 +41,3 @@ Run a development server
|
|||
```sh
|
||||
yarn start
|
||||
```
|
||||
|
||||
And build artefacts for production,
|
||||
|
||||
```sh
|
||||
yarn build # builds storybook app
|
||||
```
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
{
|
||||
"name": "@quri/squiggle-components",
|
||||
"version": "0.2.14",
|
||||
"version": "0.2.17",
|
||||
"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",
|
||||
"@quri/squiggle-lang": "^0.2.8",
|
||||
"@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,36 +25,35 @@
|
|||
"@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",
|
||||
"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",
|
||||
"build": "tsc -b && build-storybook -s public",
|
||||
"build:package": "tsc -b",
|
||||
"bundle": "webpack",
|
||||
"all": "yarn bundle && yarn build",
|
||||
"lint": "prettier --check .",
|
||||
"format": "prettier --write ."
|
||||
"format": "prettier --write .",
|
||||
"prepack": "yarn bundle && tsc -b"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
|
@ -88,7 +87,6 @@
|
|||
"@types/react": "17.0.43"
|
||||
},
|
||||
"source": "./src/index.ts",
|
||||
"browser": "dist/bundle.js",
|
||||
"main": "dist/src/index.js",
|
||||
"types": "dist/src/index.d.ts"
|
||||
"main": "./dist/src/index.js",
|
||||
"types": "./dist/src/index.d.ts"
|
||||
}
|
||||
|
|
|
@ -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`
|
||||
|
@ -143,8 +144,6 @@ export interface SquiggleChartProps {
|
|||
diagramStop?: number;
|
||||
/** If the result is a function, how many points along the function it samples */
|
||||
diagramCount?: number;
|
||||
/** variables declared before this expression */
|
||||
environment?: unknown;
|
||||
/** When the environment changes */
|
||||
onChange?(expr: squiggleExpression): void;
|
||||
/** CSS width of the element */
|
||||
|
@ -152,6 +151,8 @@ export interface SquiggleChartProps {
|
|||
height?: number;
|
||||
/** Bindings of previous variables declared */
|
||||
bindings?: bindings;
|
||||
/** JS imported parameters */
|
||||
jsImports?: jsImports;
|
||||
}
|
||||
|
||||
const ChartWrapper = styled.div`
|
||||
|
@ -166,14 +167,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 {
|
||||
|
@ -22,14 +32,14 @@ export interface SquiggleEditorProps {
|
|||
diagramStop?: number;
|
||||
/** If the result is a function, how many points along the function it samples */
|
||||
diagramCount?: number;
|
||||
/** The environment, other variables that were already declared */
|
||||
environment?: unknown;
|
||||
/** when the environment changes. Used again for notebook magic*/
|
||||
onChange?(expr: squiggleExpression): void;
|
||||
/** The width of the element */
|
||||
width: number;
|
||||
width?: number;
|
||||
/** Previous variable declarations */
|
||||
bindings: bindings;
|
||||
bindings?: bindings;
|
||||
/** JS Imports */
|
||||
jsImports?: jsImports;
|
||||
}
|
||||
|
||||
const Input = styled.div`
|
||||
|
@ -49,8 +59,8 @@ export let SquiggleEditor: React.FC<SquiggleEditorProps> = ({
|
|||
diagramStop,
|
||||
diagramCount,
|
||||
onChange,
|
||||
environment,
|
||||
bindings = {},
|
||||
bindings = defaultBindings,
|
||||
jsImports = defaultImports,
|
||||
}: SquiggleEditorProps) => {
|
||||
let [expression, setExpression] = React.useState(initialSquiggleString);
|
||||
return (
|
||||
|
@ -74,9 +84,9 @@ export let SquiggleEditor: React.FC<SquiggleEditorProps> = ({
|
|||
diagramStart={diagramStart}
|
||||
diagramStop={diagramStop}
|
||||
diagramCount={diagramCount}
|
||||
environment={environment}
|
||||
onChange={onChange}
|
||||
bindings={bindings}
|
||||
jsImports={jsImports}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -131,19 +141,31 @@ export interface SquigglePartialProps {
|
|||
diagramCount?: number;
|
||||
/** when the environment changes. Used again for notebook magic*/
|
||||
onChange?(expr: bindings): void;
|
||||
/** 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);
|
||||
}
|
||||
|
|
|
@ -88,7 +88,7 @@
|
|||
"tickOpacity": 0.0,
|
||||
"domainColor": "#fff",
|
||||
"domainOpacity": 0.0,
|
||||
"format": "~s",
|
||||
"format": "~g",
|
||||
"tickCount": 10
|
||||
}
|
||||
],
|
||||
|
|
|
@ -3,7 +3,26 @@
|
|||
|
||||
# Squiggle language
|
||||
|
||||
## Build for development
|
||||
_An estimation language_
|
||||
|
||||
# Use the `npm` package
|
||||
|
||||
For instance, in a javascript project, you can
|
||||
|
||||
```sh
|
||||
yarn add @quri/squiggle-lang
|
||||
```
|
||||
|
||||
```js
|
||||
import { run } from "@quri/squiggle-lang";
|
||||
run(
|
||||
"normal(0, 1) * fromSamples([-3,-2,-1,1,2,3,3,3,4,9]"
|
||||
).value.value.toSparkline().value;
|
||||
```
|
||||
|
||||
**However, for most use cases you'll prefer to use our [library of react components](https://www.npmjs.com/package/@quri/squiggle-components)**, and let your app transitively depend on `@quri/squiggle-lang`.
|
||||
|
||||
# Build for development
|
||||
|
||||
We assume that you ran `yarn` at the monorepo level.
|
||||
|
||||
|
@ -23,7 +42,7 @@ yarn test
|
|||
yarn coverage:rescript; o _coverage/index.html # produces coverage report and opens it in browser
|
||||
```
|
||||
|
||||
## Distributing this package or using this package from other monorepo packages
|
||||
# Distributing this package or using this package from other monorepo packages
|
||||
|
||||
As it says in the other `packages/*/README.md`s, building this package is an essential step of building other packages.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Distribution } from "../../src/js/index";
|
||||
import { expectErrorToBeBounded, failDefault } from "./TestHelpers";
|
||||
import { expectErrorToBeBounded, failDefault, testRun } from "./TestHelpers";
|
||||
import * as fc from "fast-check";
|
||||
|
||||
// Beware: float64Array makes it appear in an infinite loop.
|
||||
|
@ -212,3 +212,18 @@ describe("mean is mean", () => {
|
|||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("fromSamples function", () => {
|
||||
test.skip("gives a mean near the mean of the input", () => {
|
||||
fc.assert(
|
||||
fc.property(arrayGen(), (xs_) => {
|
||||
let xs = Array.from(xs_);
|
||||
let xsString = xs.toString();
|
||||
let squiggleString = `x = fromSamples([${xsString}]); mean(x)`;
|
||||
let squiggleResult = testRun(squiggleString);
|
||||
let mean = xs.reduce((a, b) => a + b, 0.0) / xs.length;
|
||||
expect(squiggleResult.value).toBeCloseTo(mean, 4);
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@quri/squiggle-lang",
|
||||
"version": "0.2.7",
|
||||
"version": "0.2.8",
|
||||
"homepage": "https://squiggle-language.com",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
@ -24,6 +24,7 @@
|
|||
"format:rescript": "rescript format -all",
|
||||
"format:prettier": "prettier --write .",
|
||||
"format": "yarn format:rescript && yarn format:prettier",
|
||||
"prepack": "yarn build && yarn test && yarn bundle",
|
||||
"all": "yarn build && yarn bundle && yarn test"
|
||||
},
|
||||
"keywords": [
|
||||
|
@ -35,22 +36,22 @@
|
|||
"rescript": "^9.1.4",
|
||||
"jstat": "^1.9.5",
|
||||
"pdfast": "^0.2.0",
|
||||
"mathjs": "10.5.0"
|
||||
"mathjs": "^10.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"bisect_ppx": "^2.7.1",
|
||||
"lodash": "4.17.21",
|
||||
"lodash": "^4.17.21",
|
||||
"rescript-fast-check": "^1.1.1",
|
||||
"@glennsl/rescript-jest": "^0.9.0",
|
||||
"@istanbuljs/nyc-config-typescript": "^1.0.2",
|
||||
"@types/jest": "^27.4.0",
|
||||
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.2",
|
||||
"chalk": "^5.0.1",
|
||||
"codecov": "3.8.3",
|
||||
"fast-check": "2.25.0",
|
||||
"codecov": "^3.8.3",
|
||||
"fast-check": "^2.25.0",
|
||||
"gentype": "^4.3.0",
|
||||
"jest": "^27.5.1",
|
||||
"moduleserve": "0.9.1",
|
||||
"moduleserve": "^0.9.1",
|
||||
"nyc": "^15.1.0",
|
||||
"reanalyze": "^2.19.0",
|
||||
"ts-jest": "^27.1.4",
|
||||
|
|
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,6 +1,5 @@
|
|||
import * as _ from "lodash";
|
||||
import {
|
||||
genericDist,
|
||||
samplingParams,
|
||||
evaluatePartialUsingExternalBindings,
|
||||
externalBindings,
|
||||
|
@ -21,34 +20,23 @@ export {
|
|||
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,
|
||||
|
@ -100,27 +88,54 @@ export type 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
|
||||
|
@ -183,329 +198,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 };
|
||||
}
|
|
@ -154,6 +154,16 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => {
|
|||
->GenericDist.toPointSet(~xyPointLength, ~sampleCount, ())
|
||||
->E.R2.fmap(r => Dist(PointSet(r)))
|
||||
->OutputLocal.fromResult
|
||||
| ToDist(Scale(#Logarithm, f)) =>
|
||||
dist
|
||||
->GenericDist.pointwiseCombinationFloat(~toPointSetFn, ~algebraicCombination=#Logarithm, ~f)
|
||||
->E.R2.fmap(r => Dist(r))
|
||||
->OutputLocal.fromResult
|
||||
| ToDist(Scale(#Power, f)) =>
|
||||
dist
|
||||
->GenericDist.pointwiseCombinationFloat(~toPointSetFn, ~algebraicCombination=#Power, ~f)
|
||||
->E.R2.fmap(r => Dist(r))
|
||||
->OutputLocal.fromResult
|
||||
| ToDistCombination(Algebraic(_), _, #Float(_)) => GenDistError(NotYetImplemented)
|
||||
| ToDistCombination(Algebraic(strategy), arithmeticOperation, #Dist(t2)) =>
|
||||
dist
|
||||
|
@ -189,6 +199,12 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => {
|
|||
->GenericDist.mixture(~scaleMultiplyFn=scaleMultiply, ~pointwiseAddFn=pointwiseAdd)
|
||||
->E.R2.fmap(r => Dist(r))
|
||||
->OutputLocal.fromResult
|
||||
| FromSamples(xs) =>
|
||||
xs
|
||||
->SampleSetDist.make
|
||||
->E.R2.errMap(x => DistributionTypes.SampleSetError(x))
|
||||
->E.R2.fmap(x => x->DistributionTypes.SampleSet->Dist)
|
||||
->OutputLocal.fromResult
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -229,6 +245,7 @@ module Constructors = {
|
|||
let isNormalized = (~env, dist) => C.isNormalized(dist)->run(~env)->toBoolR
|
||||
let toPointSet = (~env, dist) => C.toPointSet(dist)->run(~env)->toDistR
|
||||
let toSampleSet = (~env, dist, n) => C.toSampleSet(dist, n)->run(~env)->toDistR
|
||||
let fromSamples = (~env, xs) => C.fromSamples(xs)->run(~env)->toDistR
|
||||
let truncate = (~env, dist, leftCutoff, rightCutoff) =>
|
||||
C.truncate(dist, leftCutoff, rightCutoff)->run(~env)->toDistR
|
||||
let inspect = (~env, dist) => C.inspect(dist)->run(~env)->toDistR
|
||||
|
|
|
@ -61,6 +61,8 @@ module Constructors: {
|
|||
@genType
|
||||
let toSampleSet: (~env: env, genericDist, int) => result<genericDist, error>
|
||||
@genType
|
||||
let fromSamples: (~env: env, SampleSetDist.t) => result<genericDist, error>
|
||||
@genType
|
||||
let truncate: (~env: env, genericDist, option<float>, option<float>) => result<genericDist, error>
|
||||
@genType
|
||||
let inspect: (~env: env, genericDist) => result<genericDist, error>
|
||||
|
|
|
@ -11,7 +11,7 @@ type error =
|
|||
| NotYetImplemented
|
||||
| Unreachable
|
||||
| DistributionVerticalShiftIsInvalid
|
||||
| TooFewSamples
|
||||
| SampleSetError(SampleSetDist.sampleSetError)
|
||||
| ArgumentError(string)
|
||||
| OperationError(Operation.Error.t)
|
||||
| PointSetConversionError(SampleSetDist.pointsetConversionError)
|
||||
|
@ -35,7 +35,8 @@ module Error = {
|
|||
| DistributionVerticalShiftIsInvalid => "Distribution Vertical Shift is Invalid"
|
||||
| ArgumentError(s) => `Argument Error ${s}`
|
||||
| LogarithmOfDistributionError(s) => `Logarithm of input error: ${s}`
|
||||
| TooFewSamples => "Too Few Samples"
|
||||
| SampleSetError(TooFewSamples) => "Too Few Samples"
|
||||
| SampleSetError(NonNumericInput(err)) => `Found a non-number in input: ${err}`
|
||||
| OperationError(err) => Operation.Error.toString(err)
|
||||
| PointSetConversionError(err) => SampleSetDist.pointsetConversionErrorToString(err)
|
||||
| SparklineError(err) => PointSetTypes.sparklineErrorToString(err)
|
||||
|
@ -47,10 +48,7 @@ module Error = {
|
|||
let resultStringToResultError: result<'a, string> => result<'a, error> = n =>
|
||||
n->E.R2.errMap(r => r->fromString)
|
||||
|
||||
let sampleErrorToDistErr = (err: SampleSetDist.sampleSetError): error =>
|
||||
switch err {
|
||||
| TooFewSamples => TooFewSamples
|
||||
}
|
||||
let sampleErrorToDistErr = (err: SampleSetDist.sampleSetError): error => SampleSetError(err)
|
||||
}
|
||||
|
||||
@genType
|
||||
|
@ -68,12 +66,19 @@ module DistributionOperation = {
|
|||
| #Pdf(float)
|
||||
| #Mean
|
||||
| #Sample
|
||||
| #IntegralSum
|
||||
]
|
||||
|
||||
type toScaleFn = [
|
||||
| #Power
|
||||
| #Logarithm
|
||||
]
|
||||
|
||||
type toDist =
|
||||
| Normalize
|
||||
| ToPointSet
|
||||
| ToSampleSet(int)
|
||||
| Scale(toScaleFn, float)
|
||||
| Truncate(option<float>, option<float>)
|
||||
| Inspect
|
||||
|
||||
|
@ -99,6 +104,7 @@ module DistributionOperation = {
|
|||
type genericFunctionCallInfo =
|
||||
| FromDist(fromDist, genericDist)
|
||||
| FromFloat(fromDist, float)
|
||||
| FromSamples(array<float>)
|
||||
| Mixture(array<(genericDist, float)>)
|
||||
|
||||
let distCallToString = (distFunction: fromDist): string =>
|
||||
|
@ -113,6 +119,8 @@ module DistributionOperation = {
|
|||
| ToDist(ToSampleSet(r)) => `toSampleSet(${E.I.toString(r)})`
|
||||
| ToDist(Truncate(_, _)) => `truncate`
|
||||
| ToDist(Inspect) => `inspect`
|
||||
| ToDist(Scale(#Power, r)) => `scalePower(${E.Float.toFixed(r)})`
|
||||
| ToDist(Scale(#Logarithm, r)) => `scaleLog(${E.Float.toFixed(r)})`
|
||||
| ToString(ToString) => `toString`
|
||||
| ToString(ToSparkline(n)) => `toSparkline(${E.I.toString(n)})`
|
||||
| ToBool(IsNormalized) => `isNormalized`
|
||||
|
@ -124,6 +132,7 @@ module DistributionOperation = {
|
|||
switch d {
|
||||
| FromDist(f, _) | FromFloat(f, _) => distCallToString(f)
|
||||
| Mixture(_) => `mixture`
|
||||
| FromSamples(_) => `fromSamples`
|
||||
}
|
||||
}
|
||||
module Constructors = {
|
||||
|
@ -140,8 +149,11 @@ module Constructors = {
|
|||
let isNormalized = (dist): t => FromDist(ToBool(IsNormalized), dist)
|
||||
let toPointSet = (dist): t => FromDist(ToDist(ToPointSet), dist)
|
||||
let toSampleSet = (dist, r): t => FromDist(ToDist(ToSampleSet(r)), dist)
|
||||
let fromSamples = (xs): t => FromSamples(xs)
|
||||
let truncate = (dist, left, right): t => FromDist(ToDist(Truncate(left, right)), dist)
|
||||
let inspect = (dist): t => FromDist(ToDist(Inspect), dist)
|
||||
let scalePower = (dist, n): t => FromDist(ToDist(Scale(#Power, n)), dist)
|
||||
let scaleLogarithm = (dist, n): t => FromDist(ToDist(Scale(#Logarithm, n)), dist)
|
||||
let toString = (dist): t => FromDist(ToString(ToString), dist)
|
||||
let toSparkline = (dist, n): t => FromDist(ToString(ToSparkline(n)), dist)
|
||||
let algebraicAdd = (dist1, dist2: genericDist): t => FromDist(
|
||||
|
|
|
@ -62,26 +62,31 @@ let isNormalized = (t: t): bool => Js.Math.abs_float(integralEndY(t) -. 1.0) < 1
|
|||
let toFloatOperation = (
|
||||
t,
|
||||
~toPointSetFn: toPointSetFn,
|
||||
~distToFloatOperation: Operation.distToFloatOperation,
|
||||
~distToFloatOperation: DistributionTypes.DistributionOperation.toFloat,
|
||||
) => {
|
||||
let trySymbolicSolution = switch (t: t) {
|
||||
| Symbolic(r) => SymbolicDist.T.operate(distToFloatOperation, r)->E.R.toOption
|
||||
| _ => None
|
||||
}
|
||||
switch distToFloatOperation {
|
||||
| #IntegralSum => Ok(integralEndY(t))
|
||||
| (#Pdf(_) | #Cdf(_) | #Inv(_) | #Mean | #Sample) as op => {
|
||||
let trySymbolicSolution = switch (t: t) {
|
||||
| Symbolic(r) => SymbolicDist.T.operate(op, r)->E.R.toOption
|
||||
| _ => None
|
||||
}
|
||||
|
||||
let trySampleSetSolution = switch ((t: t), distToFloatOperation) {
|
||||
| (SampleSet(sampleSet), #Mean) => SampleSetDist.mean(sampleSet)->Some
|
||||
| (SampleSet(sampleSet), #Sample) => SampleSetDist.sample(sampleSet)->Some
|
||||
| (SampleSet(sampleSet), #Inv(r)) => SampleSetDist.percentile(sampleSet, r)->Some
|
||||
| _ => None
|
||||
}
|
||||
let trySampleSetSolution = switch ((t: t), distToFloatOperation) {
|
||||
| (SampleSet(sampleSet), #Mean) => SampleSetDist.mean(sampleSet)->Some
|
||||
| (SampleSet(sampleSet), #Sample) => SampleSetDist.sample(sampleSet)->Some
|
||||
| (SampleSet(sampleSet), #Inv(r)) => SampleSetDist.percentile(sampleSet, r)->Some
|
||||
| _ => None
|
||||
}
|
||||
|
||||
switch trySymbolicSolution {
|
||||
| Some(r) => Ok(r)
|
||||
| None =>
|
||||
switch trySampleSetSolution {
|
||||
| Some(r) => Ok(r)
|
||||
| None => toPointSetFn(t)->E.R2.fmap(PointSetDist.operate(distToFloatOperation))
|
||||
switch trySymbolicSolution {
|
||||
| Some(r) => Ok(r)
|
||||
| None =>
|
||||
switch trySampleSetSolution {
|
||||
| Some(r) => Ok(r)
|
||||
| None => toPointSetFn(t)->E.R2.fmap(PointSetDist.operate(op))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ let isNormalized: t => bool
|
|||
let toFloatOperation: (
|
||||
t,
|
||||
~toPointSetFn: toPointSetFn,
|
||||
~distToFloatOperation: Operation.distToFloatOperation,
|
||||
~distToFloatOperation: DistributionTypes.DistributionOperation.toFloat,
|
||||
) => result<float, error>
|
||||
|
||||
@genType
|
||||
|
|
|
@ -156,8 +156,10 @@ let reduce = (
|
|||
~integralSumCachesFn: (float, float) => option<float>=(_, _) => None,
|
||||
fn: (float, float) => result<float, 'e>,
|
||||
continuousShapes,
|
||||
): result<t, 'e> =>
|
||||
continuousShapes |> E.A.R.foldM(combinePointwise(~integralSumCachesFn, fn), empty)
|
||||
): result<t, 'e> => {
|
||||
let merge = combinePointwise(~integralSumCachesFn, fn)
|
||||
continuousShapes |> E.A.R.foldM(merge, empty)
|
||||
}
|
||||
|
||||
let mapYResult = (
|
||||
~integralSumCacheFn=_ => None,
|
||||
|
|
|
@ -34,9 +34,10 @@ let lastY = (t: t) => t |> getShape |> XYShape.T.lastY
|
|||
|
||||
let combinePointwise = (
|
||||
~integralSumCachesFn=(_, _) => None,
|
||||
fn,
|
||||
t1: PointSetTypes.discreteShape,
|
||||
t2: PointSetTypes.discreteShape,
|
||||
): PointSetTypes.discreteShape => {
|
||||
): result<PointSetTypes.discreteShape, 'e> => {
|
||||
let combinedIntegralSum = Common.combineIntegralSums(
|
||||
integralSumCachesFn,
|
||||
t1.integralSumCache,
|
||||
|
@ -49,16 +50,22 @@ let combinePointwise = (
|
|||
make(
|
||||
~integralSumCache=combinedIntegralSum,
|
||||
XYShape.PointwiseCombination.combine(
|
||||
(a, b) => Ok(a +. b),
|
||||
fn,
|
||||
XYShape.XtoY.discreteInterpolator,
|
||||
t1.xyShape,
|
||||
t2.xyShape,
|
||||
)->E.R.toExn("Addition operation should never fail", _),
|
||||
)
|
||||
)->Ok
|
||||
}
|
||||
|
||||
let reduce = (~integralSumCachesFn=(_, _) => None, discreteShapes): PointSetTypes.discreteShape =>
|
||||
discreteShapes |> E.A.fold_left(combinePointwise(~integralSumCachesFn), empty)
|
||||
let reduce = (
|
||||
~integralSumCachesFn=(_, _) => None,
|
||||
fn: (float, float) => result<float, 'e>,
|
||||
discreteShapes: array<PointSetTypes.discreteShape>,
|
||||
): result<t, 'e> => {
|
||||
let merge = combinePointwise(~integralSumCachesFn, fn)
|
||||
discreteShapes |> E.A.R.foldM(merge, empty)
|
||||
}
|
||||
|
||||
let updateIntegralSumCache = (integralSumCache, t: t): t => {
|
||||
...t,
|
||||
|
|
|
@ -316,7 +316,10 @@ let combinePointwise = (
|
|||
t2: t,
|
||||
): result<t, 'e> => {
|
||||
let reducedDiscrete =
|
||||
[t1, t2] |> E.A.fmap(toDiscrete) |> E.A.O.concatSomes |> Discrete.reduce(~integralSumCachesFn)
|
||||
[t1, t2]
|
||||
|> E.A.fmap(toDiscrete)
|
||||
|> E.A.O.concatSomes
|
||||
|> Discrete.reduce(~integralSumCachesFn, fn)
|
||||
|
||||
let reducedContinuous =
|
||||
[t1, t2]
|
||||
|
@ -335,11 +338,11 @@ let combinePointwise = (
|
|||
t1.integralCache,
|
||||
t2.integralCache,
|
||||
)
|
||||
reducedContinuous->E.R2.fmap(continuous =>
|
||||
E.R.merge(reducedContinuous, reducedDiscrete)->E.R2.fmap(((continuous, discrete)) =>
|
||||
make(
|
||||
~integralSumCache=combinedIntegralSum,
|
||||
~integralCache=combinedIntegral,
|
||||
~discrete=reducedDiscrete,
|
||||
~discrete,
|
||||
~continuous,
|
||||
)
|
||||
)
|
||||
|
|
|
@ -84,7 +84,12 @@ let combinePointwise = (
|
|||
m2,
|
||||
)->E.R2.fmap(x => PointSetTypes.Continuous(x))
|
||||
| (Discrete(m1), Discrete(m2)) =>
|
||||
Ok(PointSetTypes.Discrete(Discrete.combinePointwise(~integralSumCachesFn, m1, m2)))
|
||||
Discrete.combinePointwise(
|
||||
~integralSumCachesFn,
|
||||
fn,
|
||||
m1,
|
||||
m2,
|
||||
)->E.R2.fmap(x => PointSetTypes.Discrete(x))
|
||||
| (m1, m2) =>
|
||||
Mixed.combinePointwise(
|
||||
~integralSumCachesFn,
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
@genType
|
||||
module Error = {
|
||||
@genType
|
||||
type sampleSetError = TooFewSamples
|
||||
type sampleSetError = TooFewSamples | NonNumericInput(string)
|
||||
|
||||
let sampleSetErrorToString = (err: sampleSetError): string =>
|
||||
switch err {
|
||||
| TooFewSamples => "Too few samples when constructing sample set"
|
||||
| NonNumericInput(err) => `Found a non-number in input: ${err}`
|
||||
}
|
||||
|
||||
@genType
|
||||
|
|
|
@ -1,27 +1,30 @@
|
|||
//The math here was taken from https://github.com/jasondavies/science.js/blob/master/src/stats/SampleSetDist_Bandwidth.js
|
||||
|
||||
let {iqr_percentile, nrd0_lo_denominator, one, nrd0_coef, nrd_coef, nrd_fractionalPower} = module(
|
||||
MagicNumbers.SampleSetBandwidth
|
||||
)
|
||||
let len = x => E.A.length(x) |> float_of_int
|
||||
|
||||
let iqr = x => Jstat.percentile(x, 0.75, true) -. Jstat.percentile(x, 0.25, true)
|
||||
let iqr = x =>
|
||||
Jstat.percentile(x, iqr_percentile, true) -. Jstat.percentile(x, 1.0 -. iqr_percentile, true)
|
||||
|
||||
// Silverman, B. W. (1986) Density Estimation. London: Chapman and Hall.
|
||||
let nrd0 = x => {
|
||||
let hi = Js_math.sqrt(Jstat.variance(x))
|
||||
let lo = Js_math.minMany_float([hi, iqr(x) /. 1.34])
|
||||
let lo = Js_math.minMany_float([hi, iqr(x) /. nrd0_lo_denominator])
|
||||
let e = Js_math.abs_float(x[1])
|
||||
let lo' = switch (lo, hi, e) {
|
||||
| (lo, _, _) if !Js.Float.isNaN(lo) => lo
|
||||
| (_, hi, _) if !Js.Float.isNaN(hi) => hi
|
||||
| (_, _, e) if !Js.Float.isNaN(e) => e
|
||||
| _ => 1.0
|
||||
| _ => one
|
||||
}
|
||||
0.9 *. lo' *. Js.Math.pow_float(~base=len(x), ~exp=-0.2)
|
||||
nrd0_coef *. lo' *. Js.Math.pow_float(~base=len(x), ~exp=nrd_fractionalPower)
|
||||
}
|
||||
|
||||
// Scott, D. W. (1992) Multivariate Density Estimation: Theory, Practice, and Visualization. Wiley.
|
||||
let nrd = x => {
|
||||
let h = iqr(x) /. 1.34
|
||||
1.06 *.
|
||||
let h = iqr(x) /. nrd0_lo_denominator
|
||||
nrd_coef *.
|
||||
Js.Math.min_float(Js.Math.sqrt(Jstat.variance(x)), h) *.
|
||||
Js.Math.pow_float(~base=len(x), ~exp=-1.0 /. 5.0)
|
||||
Js.Math.pow_float(~base=len(x), ~exp=nrd_fractionalPower)
|
||||
}
|
||||
|
|
|
@ -35,3 +35,16 @@ module ToPointSet = {
|
|||
*/
|
||||
let minDiscreteToKeep = samples => max(20, E.A.length(samples) / 50)
|
||||
}
|
||||
|
||||
module SampleSetBandwidth = {
|
||||
// Silverman, B. W. (1986) Density Estimation. London: Chapman and Hall.
|
||||
// Scott, D. W. (1992) Multivariate Density Estimation: Theory, Practice, and Visualization. Wiley.
|
||||
let iqr_percentile = 0.75
|
||||
let iqr_percentile_complement = 1.0 -. iqr_percentile
|
||||
let nrd0_lo_denominator = 1.34
|
||||
let one = 1.0
|
||||
let nrd0_coef = 0.9
|
||||
|
||||
let nrd_coef = 1.06
|
||||
let nrd_fractionalPower = -0.2
|
||||
}
|
||||
|
|
|
@ -130,6 +130,7 @@ module Helpers = {
|
|||
| Error(err) => GenDistError(ArgumentError(err))
|
||||
}
|
||||
}
|
||||
| Some(EvNumber(_))
|
||||
| Some(EvDistribution(_)) =>
|
||||
switch parseDistributionArray(args) {
|
||||
| Ok(distributions) => mixtureWithDefaultWeights(distributions)
|
||||
|
@ -194,6 +195,7 @@ let dispatchToGenericOutput = (call: ExpressionValue.functionCall, _environment)
|
|||
->SymbolicConstructors.symbolicResultToOutput
|
||||
| ("sample", [EvDistribution(dist)]) => Helpers.toFloatFn(#Sample, dist)
|
||||
| ("mean", [EvDistribution(dist)]) => Helpers.toFloatFn(#Mean, dist)
|
||||
| ("integralSum", [EvDistribution(dist)]) => Helpers.toFloatFn(#IntegralSum, dist)
|
||||
| ("toString", [EvDistribution(dist)]) => Helpers.toStringFn(ToString, dist)
|
||||
| ("toSparkline", [EvDistribution(dist)]) => Helpers.toStringFn(ToSparkline(20), dist)
|
||||
| ("toSparkline", [EvDistribution(dist), EvNumber(n)]) =>
|
||||
|
@ -209,6 +211,15 @@ let dispatchToGenericOutput = (call: ExpressionValue.functionCall, _environment)
|
|||
| ("normalize", [EvDistribution(dist)]) => Helpers.toDistFn(Normalize, dist)
|
||||
| ("isNormalized", [EvDistribution(dist)]) => Helpers.toBoolFn(IsNormalized, dist)
|
||||
| ("toPointSet", [EvDistribution(dist)]) => Helpers.toDistFn(ToPointSet, dist)
|
||||
| ("scaleLog", [EvDistribution(dist)]) =>
|
||||
Helpers.toDistFn(Scale(#Logarithm, MagicNumbers.Math.e), dist)
|
||||
| ("scaleLog10", [EvDistribution(dist)]) => Helpers.toDistFn(Scale(#Logarithm, 10.0), dist)
|
||||
| ("scaleLog", [EvDistribution(dist), EvNumber(float)]) =>
|
||||
Helpers.toDistFn(Scale(#Logarithm, float), dist)
|
||||
| ("scalePow", [EvDistribution(dist), EvNumber(float)]) =>
|
||||
Helpers.toDistFn(Scale(#Power, float), dist)
|
||||
| ("scaleExp", [EvDistribution(dist)]) =>
|
||||
Helpers.toDistFn(Scale(#Power, MagicNumbers.Math.e), dist)
|
||||
| ("cdf", [EvDistribution(dist), EvNumber(float)]) => Helpers.toFloatFn(#Cdf(float), dist)
|
||||
| ("pdf", [EvDistribution(dist), EvNumber(float)]) => Helpers.toFloatFn(#Pdf(float), dist)
|
||||
| ("inv", [EvDistribution(dist), EvNumber(float)]) => Helpers.toFloatFn(#Inv(float), dist)
|
||||
|
@ -216,6 +227,14 @@ let dispatchToGenericOutput = (call: ExpressionValue.functionCall, _environment)
|
|||
Helpers.toDistFn(ToSampleSet(Belt.Int.fromFloat(float)), dist)
|
||||
| ("toSampleSet", [EvDistribution(dist)]) =>
|
||||
Helpers.toDistFn(ToSampleSet(MagicNumbers.Environment.defaultSampleCount), dist)
|
||||
| ("fromSamples", [EvArray(inputArray)]) => {
|
||||
let _wrapInputErrors = x => SampleSetDist.NonNumericInput(x)
|
||||
let parsedArray = Helpers.parseNumberArray(inputArray)->E.R2.errMap(_wrapInputErrors)
|
||||
switch parsedArray {
|
||||
| Ok(array) => runGenericOperation(FromSamples(array))
|
||||
| Error(e) => GenDistError(SampleSetError(e))
|
||||
}->Some
|
||||
}
|
||||
| ("inspect", [EvDistribution(dist)]) => Helpers.toDistFn(Inspect, dist)
|
||||
| ("truncateLeft", [EvDistribution(dist), EvNumber(float)]) =>
|
||||
Helpers.toDistFn(Truncate(Some(float), None), dist)
|
||||
|
|
|
@ -289,6 +289,13 @@ module R = {
|
|||
| Ok(r) => r->Ok
|
||||
| Error(x) => x->f->Error
|
||||
}
|
||||
|
||||
//I'm not sure what to call this.
|
||||
let unify = (a: result<'a, 'b>, c: 'b => 'a): 'a =>
|
||||
switch a {
|
||||
| Ok(x) => x
|
||||
| Error(x) => c(x)
|
||||
}
|
||||
}
|
||||
|
||||
module R2 = {
|
||||
|
@ -307,6 +314,8 @@ module R2 = {
|
|||
| Ok(x) => x->Ok
|
||||
| Error(x) => x->f->Error
|
||||
}
|
||||
|
||||
let toExn = (a, b) => R.toExn(b, a)
|
||||
}
|
||||
|
||||
let safe_fn_of_string = (fn, s: string): option<'a> =>
|
||||
|
@ -350,7 +359,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
|
||||
|
|
|
@ -98,6 +98,19 @@ bound `a`, mode `b` and upper bound `c`.
|
|||
|
||||
Squiggle, when the context is right, automatically casts a float to a constant distribution.
|
||||
|
||||
## `fromSamples`
|
||||
|
||||
The last distribution constructor takes an array of samples and constructs a sample set distribution.
|
||||
|
||||
<SquiggleEditor initialSquiggleString="fromSamples([1,2,3,4,6,5,5,5])" />
|
||||
|
||||
#### Validity
|
||||
|
||||
For `fromSamples(xs)`,
|
||||
|
||||
- `xs.length > 5`
|
||||
- Strictly every element of `xs` must be a number.
|
||||
|
||||
## Operating on distributions
|
||||
|
||||
Here are the ways we combine distributions.
|
||||
|
@ -315,6 +328,16 @@ Or `PointSet` format
|
|||
|
||||
<SquiggleEditor initialSquiggleString="toPointSet(normal(5, 10))" />
|
||||
|
||||
### `toSampleSet` has two signatures
|
||||
|
||||
Above, we saw the unary `toSampleSet`, which uses an internal hardcoded number of samples. If you'd like to provide the number of samples, it has a binary signature as well (floored)
|
||||
|
||||
<SquiggleEditor initialSquiggleString="toSampleSet(0.1 to 1, 100.1)" />
|
||||
|
||||
#### Validity
|
||||
|
||||
- Second argument to `toSampleSet` must be a number.
|
||||
|
||||
## Normalization
|
||||
|
||||
Some distribution operations (like horizontal shift) return an unnormalized distriibution.
|
||||
|
@ -333,18 +356,6 @@ We provide a predicate `isNormalized`, for when we have simple control flow
|
|||
|
||||
- Input to `isNormalized` must be a dist
|
||||
|
||||
## Convert any distribution to a sample set distribution
|
||||
|
||||
`toSampleSet` has two signatures
|
||||
|
||||
It is unary when you use an internal hardcoded number of samples
|
||||
|
||||
<SquiggleEditor initialSquiggleString="toSampleSet(0.1 to 1)" />
|
||||
|
||||
And binary when you provide a number of samples (floored)
|
||||
|
||||
<SquiggleEditor initialSquiggleString="toSampleSet(0.1 to 1, 100)" />
|
||||
|
||||
## `inspect`
|
||||
|
||||
You may like to debug by right clicking your browser and using the _inspect_ functionality on the webpage, and viewing the _console_ tab. Then, wrap your squiggle output with `inspect` to log an internal representation.
|
||||
|
|
14
yarn.lock
14
yarn.lock
|
@ -6498,7 +6498,7 @@ coa@^2.0.2:
|
|||
chalk "^2.4.1"
|
||||
q "^1.1.2"
|
||||
|
||||
codecov@3.8.3:
|
||||
codecov@^3.8.3:
|
||||
version "3.8.3"
|
||||
resolved "https://registry.yarnpkg.com/codecov/-/codecov-3.8.3.tgz#9c3e364b8a700c597346ae98418d09880a3fdbe7"
|
||||
integrity sha512-Y8Hw+V3HgR7V71xWH2vQ9lyS358CbGCldWlJFR0JirqoGtOoas3R3/OclRTvgUYFK29mmJICDPauVKmpqbwhOA==
|
||||
|
@ -8607,7 +8607,7 @@ extglob@^2.0.4:
|
|||
snapdragon "^0.8.1"
|
||||
to-regex "^3.0.1"
|
||||
|
||||
fast-check@2.25.0, fast-check@^2.17.0:
|
||||
fast-check@^2.17.0, fast-check@^2.25.0:
|
||||
version "2.25.0"
|
||||
resolved "https://registry.yarnpkg.com/fast-check/-/fast-check-2.25.0.tgz#5146601851bf3be0953bd17eb2b7d547936c6561"
|
||||
integrity sha512-wRUT2KD2lAmT75WNIJIHECawoUUMHM0I5jrlLXGtGeqmPL8jl/EldUDjY1VCp6fDY8yflyfUeIOsOBrIbIiArg==
|
||||
|
@ -11627,7 +11627,7 @@ lodash.uniq@4.5.0, lodash.uniq@^4.5.0:
|
|||
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
|
||||
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
|
||||
|
||||
lodash@4.17.21, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0:
|
||||
lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0:
|
||||
version "4.17.21"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||
|
@ -11751,7 +11751,7 @@ markdown-to-jsx@^7.1.3:
|
|||
resolved "https://registry.yarnpkg.com/markdown-to-jsx/-/markdown-to-jsx-7.1.7.tgz#a5f22102fb12241c8cea1ca6a4050bb76b23a25d"
|
||||
integrity sha512-VI3TyyHlGkO8uFle0IOibzpO1c1iJDcXcS/zBrQrXQQvJ2tpdwVzVZ7XdKsyRz1NdRmre4dqQkMZzUHaKIG/1w==
|
||||
|
||||
mathjs@10.5.0:
|
||||
mathjs@^10.5.0:
|
||||
version "10.5.0"
|
||||
resolved "https://registry.yarnpkg.com/mathjs/-/mathjs-10.5.0.tgz#f81d0518fe7b4b2a0b85e1125b8ecfc364fb0292"
|
||||
integrity sha512-gRnSY9psN9zgiB2QV9F4XbuX5hwjxY5Ou7qoTFWDbn2vZ3UEs+sjfK/SRg2WP30TNfZWpwlGdp8H1knFJnpFdA==
|
||||
|
@ -12120,7 +12120,7 @@ mkdirp@^1.0.3, mkdirp@^1.0.4:
|
|||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
|
||||
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
|
||||
|
||||
moduleserve@0.9.1:
|
||||
moduleserve@^0.9.1:
|
||||
version "0.9.1"
|
||||
resolved "https://registry.yarnpkg.com/moduleserve/-/moduleserve-0.9.1.tgz#11bad4337ea248d7eaf10d2c7f8649a8c3b9c1f8"
|
||||
integrity sha512-WF2BeGnM2Ko7bdICgJO+Ibu+ZD33ExJHzOzTDsCUzfZnvnFfheEIYBTWyIqSRU0tXh4UTQ1krDOCglFTJPBMow==
|
||||
|
@ -14439,7 +14439,7 @@ react-ace@10.0.0:
|
|||
lodash.isequal "^4.5.0"
|
||||
prop-types "^15.7.2"
|
||||
|
||||
react-ace@10.1.0:
|
||||
react-ace@^10.1.0:
|
||||
version "10.1.0"
|
||||
resolved "https://registry.yarnpkg.com/react-ace/-/react-ace-10.1.0.tgz#d348eac2b16475231779070b6cd16768deed565f"
|
||||
integrity sha512-VkvUjZNhdYTuKOKQpMIZi7uzZZVgzCjM7cLYu6F64V0mejY8a2XTyPUIMszC6A4trbeMIHbK5fYFcT/wkP/8VA==
|
||||
|
@ -14693,7 +14693,7 @@ react-router@6.3.0, react-router@^6.0.0:
|
|||
dependencies:
|
||||
history "^5.2.0"
|
||||
|
||||
react-scripts@5.0.1:
|
||||
react-scripts@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/react-scripts/-/react-scripts-5.0.1.tgz#6285dbd65a8ba6e49ca8d651ce30645a6d980003"
|
||||
integrity sha512-8VAmEm/ZAwQzJ+GOMLbBsTdDKOpuZh7RPs0UymvBR2vRk4iZWCskjbFnxqjrzoIvlNNRZ3QJFx6/qDSi6zSnaQ==
|
||||
|
|
Loading…
Reference in New Issue
Block a user