Merge remote-tracking branch 'origin/develop' into log-score-attempt
This commit is contained in:
commit
58c885f963
18
README.md
18
README.md
|
@ -1,12 +1,22 @@
|
||||||
# Squiggle
|
# Squiggle
|
||||||
|
|
||||||
[![Packages check](https://github.com/quantified-uncertainty/squiggle/actions/workflows/ci.yml/badge.svg)](https://github.com/quantified-uncertainty/squiggle/actions/workflows/ci.yml)
|
[![Packages check](https://github.com/quantified-uncertainty/squiggle/actions/workflows/ci.yml/badge.svg)](https://github.com/quantified-uncertainty/squiggle/actions/workflows/ci.yml)
|
||||||
[![npm version](https://badge.fury.io/js/@quri%2Fsquiggle-lang.svg)](https://www.npmjs.com/package/@quri/squiggle-lang)
|
[![npm version - lang](https://badge.fury.io/js/@quri%2Fsquiggle-lang.svg)](https://www.npmjs.com/package/@quri/squiggle-lang)
|
||||||
[![npm version](https://badge.fury.io/js/@quri%2Fsquiggle-components.svg)](https://www.npmjs.com/package/@quri/squiggle-components)
|
[![npm version - components](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)
|
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/quantified-uncertainty/squiggle/blob/develop/LICENSE)
|
||||||
[![codecov](https://codecov.io/gh/quantified-uncertainty/squiggle/branch/develop/graph/badge.svg?token=QRLBL5CQ7C)](https://codecov.io/gh/quantified-uncertainty/squiggle)
|
[![codecov](https://codecov.io/gh/quantified-uncertainty/squiggle/branch/develop/graph/badge.svg?token=QRLBL5CQ7C)](https://codecov.io/gh/quantified-uncertainty/squiggle)
|
||||||
|
|
||||||
This is an experimental DSL/language for making probabilistic estimates. The full story can be found [here](https://www.lesswrong.com/s/rDe8QE5NvXcZYzgZ3).
|
_An estimation language_.
|
||||||
|
|
||||||
|
## Get started
|
||||||
|
|
||||||
|
- [Gallery](https://www.squiggle-language.com/docs/Discussions/Gallery)
|
||||||
|
- [Squiggle playground](https://squiggle-language.com/playground)
|
||||||
|
- [Language basics](https://www.squiggle-language.com/docs/Features/Language)
|
||||||
|
- [Squiggle functions source of truth](https://www.squiggle-language.com/docs/Features/Functions)
|
||||||
|
- [Known bugs](https://www.squiggle-language.com/docs/Discussions/Bugs)
|
||||||
|
- [Original lesswrong sequence](https://www.lesswrong.com/s/rDe8QE5NvXcZYzgZ3)
|
||||||
|
- [Author your squiggle models as Observable notebooks](https://observablehq.com/@hazelfire/squiggle)
|
||||||
|
|
||||||
## Our deployments
|
## Our deployments
|
||||||
|
|
||||||
|
@ -27,7 +37,7 @@ the packages can be found in `packages`.
|
||||||
- `@quri/squiggle-components` in `packages/components` contains React components that
|
- `@quri/squiggle-components` in `packages/components` contains React components that
|
||||||
can be passed squiggle strings as props, and return a presentation of the result
|
can be passed squiggle strings as props, and return a presentation of the result
|
||||||
of the calculation.
|
of the calculation.
|
||||||
- `@quri/squiggle-website` in `packages/website` The main descriptive website for squiggle,
|
- `packages/website` is the main descriptive website for squiggle,
|
||||||
it is hosted at `squiggle-language.com`.
|
it is hosted at `squiggle-language.com`.
|
||||||
|
|
||||||
The playground depends on the components library which then depends on the language. This means that if you wish to work on the components library, you will need to build (no need to bundle) the language, and as of this writing playground doesn't really work.
|
The playground depends on the components library which then depends on the language. This means that if you wish to work on the components library, you will need to build (no need to bundle) the language, and as of this writing playground doesn't really work.
|
||||||
|
|
|
@ -1,8 +1,29 @@
|
||||||
# Squiggle Components
|
[![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)
|
||||||
|
|
||||||
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/).
|
# Squiggle components
|
||||||
|
|
||||||
# Build for development
|
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
|
||||||
|
|
||||||
|
For example, in a fresh `create-react-app` project
|
||||||
|
|
||||||
|
```sh
|
||||||
|
yarn add @quri/squiggle-components
|
||||||
|
```
|
||||||
|
|
||||||
|
Add to `App.js`:
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
import { SquiggleEditor } from "@quri/squiggle-components";
|
||||||
|
<SquiggleEditor
|
||||||
|
initialSquiggleString="x = beta($alpha, 10); x + $shift"
|
||||||
|
jsImports={{ alpha: 3, shift: 20 }}
|
||||||
|
/>;
|
||||||
|
```
|
||||||
|
|
||||||
|
# Build storybook for development
|
||||||
|
|
||||||
We assume that you had run `yarn` at monorepo level, installing dependencies.
|
We assume that you had run `yarn` at monorepo level, installing dependencies.
|
||||||
|
|
||||||
|
@ -20,10 +41,3 @@ Run a development server
|
||||||
```sh
|
```sh
|
||||||
yarn start
|
yarn start
|
||||||
```
|
```
|
||||||
|
|
||||||
And build artefacts for production,
|
|
||||||
|
|
||||||
```sh
|
|
||||||
yarn bundle # builds components library
|
|
||||||
yarn build # builds storybook app
|
|
||||||
```
|
|
||||||
|
|
|
@ -1,13 +1,19 @@
|
||||||
{
|
{
|
||||||
"name": "@quri/squiggle-components",
|
"name": "@quri/squiggle-components",
|
||||||
"version": "0.2.9",
|
"version": "0.2.17",
|
||||||
"licence": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"antd": "^4.20.1",
|
"@quri/squiggle-lang": "^0.2.8",
|
||||||
"react-ace": "10.1.0",
|
|
||||||
"react-dom": "^18.1.0",
|
|
||||||
"@react-hook/size": "^2.1.2",
|
"@react-hook/size": "^2.1.2",
|
||||||
"styled-components": "^5.3.5"
|
"lodash": "^4.17.21",
|
||||||
|
"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": {
|
"devDependencies": {
|
||||||
"@babel/plugin-proposal-private-property-in-object": "^7.16.7",
|
"@babel/plugin-proposal-private-property-in-object": "^7.16.7",
|
||||||
|
@ -19,34 +25,26 @@
|
||||||
"@storybook/node-logger": "^6.4.22",
|
"@storybook/node-logger": "^6.4.22",
|
||||||
"@storybook/preset-create-react-app": "^4.1.0",
|
"@storybook/preset-create-react-app": "^4.1.0",
|
||||||
"@storybook/react": "^6.4.22",
|
"@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",
|
|
||||||
"@quri/squiggle-lang": "0.2.5",
|
|
||||||
"@testing-library/jest-dom": "^5.16.4",
|
"@testing-library/jest-dom": "^5.16.4",
|
||||||
"@testing-library/react": "^13.1.1",
|
"@testing-library/react": "^13.1.1",
|
||||||
"@testing-library/user-event": "^14.1.1",
|
"@testing-library/user-event": "^14.1.1",
|
||||||
"@types/jest": "^27.4.0",
|
"@types/jest": "^27.4.0",
|
||||||
"@types/lodash": "^4.14.182",
|
"@types/lodash": "^4.14.182",
|
||||||
"@types/node": "^17.0.29",
|
"@types/node": "^17.0.31",
|
||||||
"@types/react": "^18.0.3",
|
"@types/react": "^18.0.3",
|
||||||
"@types/react-dom": "^18.0.2",
|
"@types/react-dom": "^18.0.2",
|
||||||
|
"@types/styled-components": "^5.1.24",
|
||||||
|
"@types/webpack": "^5.28.0",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"lodash": "^4.17.21",
|
"react-scripts": "^5.0.1",
|
||||||
"react": "^18.1.0",
|
"style-loader": "^3.3.1",
|
||||||
"react-scripts": "5.0.1",
|
"ts-loader": "^9.3.0",
|
||||||
"react-vega": "^7.5.0",
|
|
||||||
"tsconfig-paths-webpack-plugin": "^3.5.2",
|
"tsconfig-paths-webpack-plugin": "^3.5.2",
|
||||||
"typescript": "^4.6.3",
|
"typescript": "^4.6.3",
|
||||||
"vega": "^5.22.1",
|
|
||||||
"vega-embed": "^6.20.6",
|
|
||||||
"vega-lite": "^5.2.0",
|
|
||||||
"web-vitals": "^2.1.4",
|
"web-vitals": "^2.1.4",
|
||||||
"webpack-cli": "^4.9.2"
|
"webpack": "^5.72.0",
|
||||||
|
"webpack-cli": "^4.9.2",
|
||||||
|
"webpack-dev-server": "^4.8.1"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "cross-env REACT_APP_FAST_REFRESH=false && start-storybook -p 6006 -s public",
|
"start": "cross-env REACT_APP_FAST_REFRESH=false && start-storybook -p 6006 -s public",
|
||||||
|
@ -54,7 +52,8 @@
|
||||||
"bundle": "webpack",
|
"bundle": "webpack",
|
||||||
"all": "yarn bundle && yarn build",
|
"all": "yarn bundle && yarn build",
|
||||||
"lint": "prettier --check .",
|
"lint": "prettier --check .",
|
||||||
"format": "prettier --write ."
|
"format": "prettier --write .",
|
||||||
|
"prepack": "yarn bundle && tsc -b"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"extends": [
|
"extends": [
|
||||||
|
@ -88,6 +87,6 @@
|
||||||
"@types/react": "17.0.43"
|
"@types/react": "17.0.43"
|
||||||
},
|
},
|
||||||
"source": "./src/index.ts",
|
"source": "./src/index.ts",
|
||||||
"main": "dist/bundle.js",
|
"main": "./dist/src/index.js",
|
||||||
"types": "dist/src/index.d.ts"
|
"types": "./dist/src/index.d.ts"
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { distributionErrorToString } from "@quri/squiggle-lang";
|
||||||
import { createClassFromSpec } from "react-vega";
|
import { createClassFromSpec } from "react-vega";
|
||||||
import * as chartSpecification from "../vega-specs/spec-distributions.json";
|
import * as chartSpecification from "../vega-specs/spec-distributions.json";
|
||||||
import { ErrorBox } from "./ErrorBox";
|
import { ErrorBox } from "./ErrorBox";
|
||||||
|
import styled from "styled-components";
|
||||||
|
|
||||||
let SquiggleVegaChart = createClassFromSpec({
|
let SquiggleVegaChart = createClassFromSpec({
|
||||||
spec: chartSpecification as Spec,
|
spec: chartSpecification as Spec,
|
||||||
|
@ -24,19 +25,21 @@ export const DistributionChart: React.FC<DistributionChartProps> = ({
|
||||||
}: DistributionChartProps) => {
|
}: DistributionChartProps) => {
|
||||||
let shape = distribution.pointSet();
|
let shape = distribution.pointSet();
|
||||||
if (shape.tag === "Ok") {
|
if (shape.tag === "Ok") {
|
||||||
return (
|
let widthProp = width ? width - 20 : undefined;
|
||||||
|
var result = (
|
||||||
<SquiggleVegaChart
|
<SquiggleVegaChart
|
||||||
data={{ con: shape.value.continuous, dis: shape.value.discrete }}
|
data={{ con: shape.value.continuous, dis: shape.value.discrete }}
|
||||||
width={width - 20}
|
width={widthProp}
|
||||||
height={height}
|
height={height}
|
||||||
actions={false}
|
actions={false}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
var result = (
|
||||||
<ErrorBox heading="Distribution Error">
|
<ErrorBox heading="Distribution Error">
|
||||||
{distributionErrorToString(shape.value)}
|
{distributionErrorToString(shape.value)}
|
||||||
</ErrorBox>
|
</ErrorBox>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,12 +5,15 @@ import {
|
||||||
run,
|
run,
|
||||||
errorValueToString,
|
errorValueToString,
|
||||||
squiggleExpression,
|
squiggleExpression,
|
||||||
|
bindings,
|
||||||
|
samplingParams,
|
||||||
|
jsImports,
|
||||||
|
defaultImports,
|
||||||
|
defaultBindings,
|
||||||
} from "@quri/squiggle-lang";
|
} from "@quri/squiggle-lang";
|
||||||
import type { samplingParams } from "@quri/squiggle-lang";
|
|
||||||
import { NumberShower } from "./NumberShower";
|
import { NumberShower } from "./NumberShower";
|
||||||
import { DistributionChart } from "./DistributionChart";
|
import { DistributionChart } from "./DistributionChart";
|
||||||
import { ErrorBox } from "./ErrorBox";
|
import { ErrorBox } from "./ErrorBox";
|
||||||
import useSize from "@react-hook/size";
|
|
||||||
|
|
||||||
const variableBox = {
|
const variableBox = {
|
||||||
Component: styled.div`
|
Component: styled.div`
|
||||||
|
@ -44,6 +47,8 @@ export const VariableBox: React.FC<{
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let RecordKeyHeader = styled.h3``;
|
||||||
|
|
||||||
export interface SquiggleItemProps {
|
export interface SquiggleItemProps {
|
||||||
/** The input string for squiggle */
|
/** The input string for squiggle */
|
||||||
expression: squiggleExpression;
|
expression: squiggleExpression;
|
||||||
|
@ -104,6 +109,17 @@ const SquiggleItem: React.FC<SquiggleItemProps> = ({
|
||||||
))}
|
))}
|
||||||
</VariableBox>
|
</VariableBox>
|
||||||
);
|
);
|
||||||
|
case "record":
|
||||||
|
return (
|
||||||
|
<VariableBox heading="Record">
|
||||||
|
{Object.entries(expression.value).map(([key, r]) => (
|
||||||
|
<>
|
||||||
|
<RecordKeyHeader>{key}</RecordKeyHeader>
|
||||||
|
<SquiggleItem expression={r} width={width - 20} height={50} />
|
||||||
|
</>
|
||||||
|
))}
|
||||||
|
</VariableBox>
|
||||||
|
);
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<ErrorBox heading="No Viewer">
|
<ErrorBox heading="No Viewer">
|
||||||
|
@ -128,47 +144,56 @@ export interface SquiggleChartProps {
|
||||||
diagramStop?: number;
|
diagramStop?: number;
|
||||||
/** If the result is a function, how many points along the function it samples */
|
/** If the result is a function, how many points along the function it samples */
|
||||||
diagramCount?: number;
|
diagramCount?: number;
|
||||||
/** variables declared before this expression */
|
|
||||||
environment?: unknown;
|
|
||||||
/** When the environment changes */
|
/** When the environment changes */
|
||||||
onChange?(expr: squiggleExpression): void;
|
onChange?(expr: squiggleExpression): void;
|
||||||
/** CSS width of the element */
|
/** CSS width of the element */
|
||||||
width?: number;
|
width?: number;
|
||||||
height?: number;
|
height?: number;
|
||||||
|
/** Bindings of previous variables declared */
|
||||||
|
bindings?: bindings;
|
||||||
|
/** JS imported parameters */
|
||||||
|
jsImports?: jsImports;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ChartWrapper = styled.div`
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
||||||
|
"Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji",
|
||||||
|
"Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||||
|
`;
|
||||||
|
|
||||||
export const SquiggleChart: React.FC<SquiggleChartProps> = ({
|
export const SquiggleChart: React.FC<SquiggleChartProps> = ({
|
||||||
squiggleString = "",
|
squiggleString = "",
|
||||||
sampleCount = 1000,
|
sampleCount = 1000,
|
||||||
outputXYPoints = 1000,
|
outputXYPoints = 1000,
|
||||||
onChange = () => {},
|
onChange = () => {},
|
||||||
height = 60,
|
height = 60,
|
||||||
|
bindings = defaultBindings,
|
||||||
|
jsImports = defaultImports,
|
||||||
width = NaN,
|
width = NaN,
|
||||||
}: SquiggleChartProps) => {
|
}: SquiggleChartProps) => {
|
||||||
const target = React.useRef(null);
|
|
||||||
const [componentWidth] = useSize(target);
|
|
||||||
// I would have wanted to just use componentWidth, but this created infinite loops with SquiggleChart.stories.
|
|
||||||
//So you can manually add a width, as an escape hatch.
|
|
||||||
let _width = width || componentWidth;
|
|
||||||
let samplingInputs: samplingParams = {
|
let samplingInputs: samplingParams = {
|
||||||
sampleCount: sampleCount,
|
sampleCount: sampleCount,
|
||||||
xyPointLength: outputXYPoints,
|
xyPointLength: outputXYPoints,
|
||||||
};
|
};
|
||||||
let expressionResult = run(squiggleString, samplingInputs);
|
let expressionResult = run(
|
||||||
|
squiggleString,
|
||||||
|
bindings,
|
||||||
|
samplingInputs,
|
||||||
|
jsImports
|
||||||
|
);
|
||||||
let internal: JSX.Element;
|
let internal: JSX.Element;
|
||||||
if (expressionResult.tag === "Ok") {
|
if (expressionResult.tag === "Ok") {
|
||||||
let expression = expressionResult.value;
|
let expression = expressionResult.value;
|
||||||
onChange(expression);
|
onChange(expression);
|
||||||
internal = (
|
internal = (
|
||||||
<SquiggleItem expression={expression} width={_width} height={height} />
|
<SquiggleItem expression={expression} width={width} height={height} />
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// At this point, we came across an error. What was our error?
|
|
||||||
internal = (
|
internal = (
|
||||||
<ErrorBox heading={"Parse Error"}>
|
<ErrorBox heading={"Parse Error"}>
|
||||||
{errorValueToString(expressionResult.value)}
|
{errorValueToString(expressionResult.value)}
|
||||||
</ErrorBox>
|
</ErrorBox>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return <div ref={target}>{internal}</div>;
|
return <ChartWrapper>{internal}</ChartWrapper>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,7 +3,19 @@ import * as ReactDOM from "react-dom";
|
||||||
import { SquiggleChart } from "./SquiggleChart";
|
import { SquiggleChart } from "./SquiggleChart";
|
||||||
import { CodeEditor } from "./CodeEditor";
|
import { CodeEditor } from "./CodeEditor";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import type { squiggleExpression } 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 {
|
export interface SquiggleEditorProps {
|
||||||
/** The input string for squiggle */
|
/** The input string for squiggle */
|
||||||
|
@ -20,12 +32,14 @@ export interface SquiggleEditorProps {
|
||||||
diagramStop?: number;
|
diagramStop?: number;
|
||||||
/** If the result is a function, how many points along the function it samples */
|
/** If the result is a function, how many points along the function it samples */
|
||||||
diagramCount?: number;
|
diagramCount?: number;
|
||||||
/** The environment, other variables that were already declared */
|
|
||||||
environment?: unknown;
|
|
||||||
/** when the environment changes. Used again for notebook magic*/
|
/** when the environment changes. Used again for notebook magic*/
|
||||||
onChange?(expr: squiggleExpression): void;
|
onChange?(expr: squiggleExpression): void;
|
||||||
/** The width of the element */
|
/** The width of the element */
|
||||||
width: number;
|
width?: number;
|
||||||
|
/** Previous variable declarations */
|
||||||
|
bindings?: bindings;
|
||||||
|
/** JS Imports */
|
||||||
|
jsImports?: jsImports;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Input = styled.div`
|
const Input = styled.div`
|
||||||
|
@ -45,7 +59,8 @@ export let SquiggleEditor: React.FC<SquiggleEditorProps> = ({
|
||||||
diagramStop,
|
diagramStop,
|
||||||
diagramCount,
|
diagramCount,
|
||||||
onChange,
|
onChange,
|
||||||
environment,
|
bindings = defaultBindings,
|
||||||
|
jsImports = defaultImports,
|
||||||
}: SquiggleEditorProps) => {
|
}: SquiggleEditorProps) => {
|
||||||
let [expression, setExpression] = React.useState(initialSquiggleString);
|
let [expression, setExpression] = React.useState(initialSquiggleString);
|
||||||
return (
|
return (
|
||||||
|
@ -69,8 +84,9 @@ export let SquiggleEditor: React.FC<SquiggleEditorProps> = ({
|
||||||
diagramStart={diagramStart}
|
diagramStart={diagramStart}
|
||||||
diagramStop={diagramStop}
|
diagramStop={diagramStop}
|
||||||
diagramCount={diagramCount}
|
diagramCount={diagramCount}
|
||||||
environment={environment}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
|
bindings={bindings}
|
||||||
|
jsImports={jsImports}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -107,3 +123,88 @@ export function renderSquiggleEditorToDom(props: SquiggleEditorProps) {
|
||||||
);
|
);
|
||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SquigglePartialProps {
|
||||||
|
/** The input string for squiggle */
|
||||||
|
initialSquiggleString?: string;
|
||||||
|
/** If the output requires monte carlo sampling, the amount of samples */
|
||||||
|
sampleCount?: number;
|
||||||
|
/** The amount of points returned to draw the distribution */
|
||||||
|
outputXYPoints?: number;
|
||||||
|
kernelWidth?: number;
|
||||||
|
pointDistLength?: number;
|
||||||
|
/** If the result is a function, where the function starts */
|
||||||
|
diagramStart?: number;
|
||||||
|
/** If the result is a function, where the function ends */
|
||||||
|
diagramStop?: number;
|
||||||
|
/** If the result is a function, how many points along the function it samples */
|
||||||
|
diagramCount?: number;
|
||||||
|
/** when the environment changes. Used again for notebook magic*/
|
||||||
|
onChange?(expr: bindings): void;
|
||||||
|
/** Previously declared variables */
|
||||||
|
bindings?: bindings;
|
||||||
|
/** Variables imported from js */
|
||||||
|
jsImports?: jsImports;
|
||||||
|
}
|
||||||
|
|
||||||
|
export let SquigglePartial: React.FC<SquigglePartialProps> = ({
|
||||||
|
initialSquiggleString = "",
|
||||||
|
onChange,
|
||||||
|
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,
|
||||||
|
samplingInputs,
|
||||||
|
jsImports
|
||||||
|
);
|
||||||
|
if (squiggleResult.tag == "Ok") {
|
||||||
|
if (onChange) onChange(squiggleResult.value);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Input>
|
||||||
|
<CodeEditor
|
||||||
|
value={expression}
|
||||||
|
onChange={setExpression}
|
||||||
|
oneLine={true}
|
||||||
|
showGutter={false}
|
||||||
|
height={20}
|
||||||
|
/>
|
||||||
|
</Input>
|
||||||
|
{squiggleResult.tag == "Error" ? (
|
||||||
|
<ErrorBox heading="Error">
|
||||||
|
{errorValueToString(squiggleResult.value)}
|
||||||
|
</ErrorBox>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export function renderSquigglePartialToDom(props: SquigglePartialProps) {
|
||||||
|
let parent = document.createElement("div");
|
||||||
|
ReactDOM.render(
|
||||||
|
<SquigglePartial
|
||||||
|
{...props}
|
||||||
|
onChange={(bindings) => {
|
||||||
|
// @ts-ignore
|
||||||
|
parent.value = bindings;
|
||||||
|
|
||||||
|
parent.dispatchEvent(new CustomEvent("input"));
|
||||||
|
if (props.onChange) props.onChange(bindings);
|
||||||
|
}}
|
||||||
|
/>,
|
||||||
|
parent
|
||||||
|
);
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import React, { FC, useState } from "react";
|
import React, { FC, ReactElement, useState } from "react";
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
import { SquiggleChart } from "./SquiggleChart";
|
import { SquiggleChart } from "./SquiggleChart";
|
||||||
import CodeEditor from "./CodeEditor";
|
import CodeEditor from "./CodeEditor";
|
||||||
import { Form, Input, Row, Col } from "antd";
|
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import "antd/dist/antd.css";
|
|
||||||
|
|
||||||
interface FieldFloatProps {
|
interface FieldFloatProps {
|
||||||
label: string;
|
label: string;
|
||||||
|
@ -14,10 +12,19 @@ interface FieldFloatProps {
|
||||||
onChange: (value: number) => void;
|
onChange: (value: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Input = styled.input``;
|
||||||
|
|
||||||
|
const FormItem = (props: { label: string; children: ReactElement }) => (
|
||||||
|
<div>
|
||||||
|
<label>{props.label}</label>
|
||||||
|
{props.children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
function FieldFloat(Props: FieldFloatProps) {
|
function FieldFloat(Props: FieldFloatProps) {
|
||||||
let [contents, setContents] = useState(Props.value + "");
|
let [contents, setContents] = useState(Props.value + "");
|
||||||
return (
|
return (
|
||||||
<Form.Item label={Props.label}>
|
<FormItem label={Props.label}>
|
||||||
<Input
|
<Input
|
||||||
value={contents}
|
value={contents}
|
||||||
className={Props.className ? Props.className : ""}
|
className={Props.className ? Props.className : ""}
|
||||||
|
@ -29,7 +36,7 @@ function FieldFloat(Props: FieldFloatProps) {
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</FormItem>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,6 +72,12 @@ const Display = styled.div<TitleProps>`
|
||||||
max-height: ${(props) => props.maxHeight}px;
|
max-height: ${(props) => props.maxHeight}px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const Row = styled.div`
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
`;
|
||||||
|
const Col = styled.div``;
|
||||||
|
|
||||||
let SquigglePlayground: FC<Props> = ({
|
let SquigglePlayground: FC<Props> = ({
|
||||||
initialSquiggleString = "",
|
initialSquiggleString = "",
|
||||||
height = 300,
|
height = 300,
|
||||||
|
@ -79,7 +92,7 @@ let SquigglePlayground: FC<Props> = ({
|
||||||
return (
|
return (
|
||||||
<ShowBox height={height}>
|
<ShowBox height={height}>
|
||||||
<Row>
|
<Row>
|
||||||
<Col span={12}>
|
<Col>
|
||||||
<CodeEditor
|
<CodeEditor
|
||||||
value={squiggleString}
|
value={squiggleString}
|
||||||
onChange={setSquiggleString}
|
onChange={setSquiggleString}
|
||||||
|
@ -88,7 +101,7 @@ let SquigglePlayground: FC<Props> = ({
|
||||||
height={height - 3}
|
height={height - 3}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={12}>
|
<Col>
|
||||||
<Display maxHeight={height - 3}>
|
<Display maxHeight={height - 3}>
|
||||||
<SquiggleChart
|
<SquiggleChart
|
||||||
squiggleString={squiggleString}
|
squiggleString={squiggleString}
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
export { SquiggleChart } from "./components/SquiggleChart";
|
export { SquiggleChart } from "./components/SquiggleChart";
|
||||||
export {
|
export {
|
||||||
SquiggleEditor,
|
SquiggleEditor,
|
||||||
|
SquigglePartial,
|
||||||
renderSquiggleEditorToDom,
|
renderSquiggleEditorToDom,
|
||||||
|
renderSquigglePartialToDom,
|
||||||
} from "./components/SquiggleEditor";
|
} from "./components/SquiggleEditor";
|
||||||
import SquigglePlayground, {
|
import SquigglePlayground, {
|
||||||
renderSquigglePlaygroundToDom,
|
renderSquigglePlaygroundToDom,
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
"description": "A basic area chart example",
|
"description": "A basic area chart example",
|
||||||
"width": 500,
|
"width": 500,
|
||||||
"height": 100,
|
"height": 100,
|
||||||
|
"autosize": "fit",
|
||||||
"padding": 5,
|
"padding": 5,
|
||||||
"data": [
|
"data": [
|
||||||
{
|
{
|
||||||
|
@ -87,7 +88,7 @@
|
||||||
"tickOpacity": 0.0,
|
"tickOpacity": 0.0,
|
||||||
"domainColor": "#fff",
|
"domainColor": "#fff",
|
||||||
"domainOpacity": 0.0,
|
"domainOpacity": 0.0,
|
||||||
"format": "~s",
|
"format": "~g",
|
||||||
"tickCount": 10
|
"tickCount": 10
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
@ -2,7 +2,6 @@ node_modules
|
||||||
shell.nix
|
shell.nix
|
||||||
.cache
|
.cache
|
||||||
.direnv
|
.direnv
|
||||||
src
|
|
||||||
__tests__
|
__tests__
|
||||||
lib
|
lib
|
||||||
examples
|
examples
|
||||||
|
|
|
@ -1,6 +1,28 @@
|
||||||
|
[![npm version](https://badge.fury.io/js/@quri%2Fsquiggle-lang.svg)](https://www.npmjs.com/package/@quri/squiggle-lang)
|
||||||
|
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/quantified-uncertainty/squiggle/blob/develop/LICENSE)
|
||||||
|
|
||||||
# Squiggle language
|
# 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.
|
We assume that you ran `yarn` at the monorepo level.
|
||||||
|
|
||||||
|
@ -15,13 +37,16 @@ Other:
|
||||||
```sh
|
```sh
|
||||||
yarn start # listens to files and recompiles at every mutation
|
yarn start # listens to files and recompiles at every mutation
|
||||||
yarn test
|
yarn test
|
||||||
yarn test:watch # keeps an active session and runs all tests at every mutation
|
|
||||||
|
|
||||||
# where o := open in osx and o := xdg-open in linux,
|
# where o := open in osx and o := xdg-open in linux,
|
||||||
yarn coverage; o _coverage/index.html # produces coverage report and opens it in browser
|
yarn coverage:rescript; o _coverage/index.html # produces coverage report and opens it in browser
|
||||||
```
|
```
|
||||||
|
|
||||||
## Information
|
# 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.
|
||||||
|
|
||||||
|
# Information
|
||||||
|
|
||||||
Squiggle is a language for representing probability distributions, as well as functions that return probability distributions. Its original intended use is for improving epistemics around EA decisions.
|
Squiggle is a language for representing probability distributions, as well as functions that return probability distributions. Its original intended use is for improving epistemics around EA decisions.
|
||||||
|
|
||||||
|
@ -34,11 +59,3 @@ This package is mainly written in [ReScript](https://rescript-lang.org/), but ha
|
||||||
ReScript has an interesting philosophy of not providing much in the way of effective build tools. Every ReScript file is compiled into `.bs.js` and `.gen.ts` files with the same name and same location, and then you can use these files in other `.js` files to create your program. To generate these files to build the package, you run `yarn build`.
|
ReScript has an interesting philosophy of not providing much in the way of effective build tools. Every ReScript file is compiled into `.bs.js` and `.gen.ts` files with the same name and same location, and then you can use these files in other `.js` files to create your program. To generate these files to build the package, you run `yarn build`.
|
||||||
|
|
||||||
`.gen.ts` files are created by the [`@genType`](https://rescript-lang.org/docs/gentype/latest/getting-started) decorator, which creates typescript typings for needed parts of the codebase so that they can be easily used in typescript. These .gen.ts files reference the .bs.js files generated by rescript.
|
`.gen.ts` files are created by the [`@genType`](https://rescript-lang.org/docs/gentype/latest/getting-started) decorator, which creates typescript typings for needed parts of the codebase so that they can be easily used in typescript. These .gen.ts files reference the .bs.js files generated by rescript.
|
||||||
|
|
||||||
### Errors regarding the `rationale` package
|
|
||||||
|
|
||||||
You may notice sometimes, that there are errors about the `rationale` package. If you ever get these errors, `yarn build` should fix this issue. These errors occur because `yarn build` also needs to create build files that are in `node_modules`. So if you replace `node_modules` you may need to rebuild to get those files back.
|
|
||||||
|
|
||||||
## 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,23 +1,5 @@
|
||||||
import {
|
import { Distribution, resultMap, defaultBindings } from "../../src/js/index";
|
||||||
run,
|
import { testRun, testRunPartial } from "./TestHelpers";
|
||||||
Distribution,
|
|
||||||
resultMap,
|
|
||||||
squiggleExpression,
|
|
||||||
errorValueToString,
|
|
||||||
} from "../../src/js/index";
|
|
||||||
|
|
||||||
let testRun = (x: string): squiggleExpression => {
|
|
||||||
let result = run(x, { sampleCount: 100, xyPointLength: 100 });
|
|
||||||
expect(result.tag).toEqual("Ok");
|
|
||||||
if (result.tag === "Ok") {
|
|
||||||
return result.value;
|
|
||||||
} else {
|
|
||||||
throw Error(
|
|
||||||
"Expected squiggle expression to evaluate but got error: " +
|
|
||||||
errorValueToString(result.value)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function Ok<b>(x: b) {
|
function Ok<b>(x: b) {
|
||||||
return { tag: "Ok", value: x };
|
return { tag: "Ok", value: x };
|
||||||
|
@ -42,6 +24,72 @@ describe("Log function", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("Array", () => {
|
||||||
|
test("nested Array", () => {
|
||||||
|
expect(testRun("[[1]]")).toEqual({
|
||||||
|
tag: "array",
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
tag: "array",
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
tag: "number",
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Record", () => {
|
||||||
|
test("Return record", () => {
|
||||||
|
expect(testRun("{a: 1}")).toEqual({
|
||||||
|
tag: "record",
|
||||||
|
value: {
|
||||||
|
a: {
|
||||||
|
tag: "number",
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Partials", () => {
|
||||||
|
test("Can pass variables between partials and cells", () => {
|
||||||
|
let bindings = testRunPartial(`x = 5`);
|
||||||
|
let bindings2 = testRunPartial(`y = x + 2`, bindings);
|
||||||
|
expect(testRun(`y + 3`, bindings2)).toEqual({
|
||||||
|
tag: "number",
|
||||||
|
value: 10,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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", () => {
|
describe("Distribution", () => {
|
||||||
//It's important that sampleCount is less than 9. If it's more, than that will create randomness
|
//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.
|
//Also, note, the value should be created using makeSampleSetDist() later on.
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Distribution } from "../../src/js/index";
|
import { Distribution } from "../../src/js/index";
|
||||||
import { expectErrorToBeBounded, failDefault } from "./TestHelpers";
|
import { expectErrorToBeBounded, failDefault, testRun } from "./TestHelpers";
|
||||||
import * as fc from "fast-check";
|
import * as fc from "fast-check";
|
||||||
|
|
||||||
// Beware: float64Array makes it appear in an infinite loop.
|
// 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);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -1,15 +1,53 @@
|
||||||
import {
|
import {
|
||||||
run,
|
run,
|
||||||
// Distribution,
|
runPartial,
|
||||||
|
bindings,
|
||||||
squiggleExpression,
|
squiggleExpression,
|
||||||
errorValueToString,
|
errorValueToString,
|
||||||
// errorValue,
|
defaultImports,
|
||||||
// result,
|
defaultBindings,
|
||||||
|
jsImports,
|
||||||
} from "../../src/js/index";
|
} from "../../src/js/index";
|
||||||
|
|
||||||
export function testRun(x: string): squiggleExpression {
|
export function testRun(
|
||||||
let squiggleResult = run(x, { sampleCount: 1000, xyPointLength: 100 });
|
x: string,
|
||||||
// return squiggleResult.value
|
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 {
|
||||||
|
throw new Error(
|
||||||
|
`Expected squiggle expression to evaluate but got error: ${errorValueToString(
|
||||||
|
squiggleResult.value
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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") {
|
if (squiggleResult.tag === "Ok") {
|
||||||
return squiggleResult.value;
|
return squiggleResult.value;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
{
|
{
|
||||||
"name": "@quri/squiggle-lang",
|
"name": "@quri/squiggle-lang",
|
||||||
"version": "0.2.5",
|
"version": "0.2.8",
|
||||||
"homepage": "https://squiggle-language.com",
|
"homepage": "https://squiggle-language.com",
|
||||||
"licence": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "rescript build -with-deps",
|
"build": "rescript build -with-deps && tsc",
|
||||||
"bundle": "webpack",
|
"bundle": "webpack",
|
||||||
"start": "rescript build -w -with-deps",
|
"start": "rescript build -w -with-deps",
|
||||||
"clean": "rescript clean",
|
"clean": "rescript clean && rm -r dist",
|
||||||
"test:reducer": "jest __tests__/Reducer*/",
|
"test:reducer": "jest __tests__/Reducer*/",
|
||||||
"benchmark": "ts-node benchmark/conversion_tests.ts",
|
"benchmark": "ts-node benchmark/conversion_tests.ts",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
|
@ -24,6 +24,7 @@
|
||||||
"format:rescript": "rescript format -all",
|
"format:rescript": "rescript format -all",
|
||||||
"format:prettier": "prettier --write .",
|
"format:prettier": "prettier --write .",
|
||||||
"format": "yarn format:rescript && yarn format:prettier",
|
"format": "yarn format:rescript && yarn format:prettier",
|
||||||
|
"prepack": "yarn build && yarn test && yarn bundle",
|
||||||
"all": "yarn build && yarn bundle && yarn test"
|
"all": "yarn build && yarn bundle && yarn test"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
@ -31,34 +32,36 @@
|
||||||
],
|
],
|
||||||
"author": "Quantified Uncertainty Research Institute",
|
"author": "Quantified Uncertainty Research Institute",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"rescript": "^9.1.4",
|
||||||
|
"jstat": "^1.9.5",
|
||||||
|
"pdfast": "^0.2.0",
|
||||||
|
"mathjs": "^10.5.0"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"bisect_ppx": "^2.7.1",
|
"bisect_ppx": "^2.7.1",
|
||||||
"jstat": "^1.9.5",
|
"lodash": "^4.17.21",
|
||||||
"lodash": "4.17.21",
|
|
||||||
"rescript": "^9.1.4",
|
|
||||||
"rescript-fast-check": "^1.1.1",
|
"rescript-fast-check": "^1.1.1",
|
||||||
"@glennsl/rescript-jest": "^0.9.0",
|
"@glennsl/rescript-jest": "^0.9.0",
|
||||||
"@istanbuljs/nyc-config-typescript": "^1.0.2",
|
"@istanbuljs/nyc-config-typescript": "^1.0.2",
|
||||||
"@types/jest": "^27.4.0",
|
"@types/jest": "^27.4.0",
|
||||||
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.2",
|
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.2",
|
||||||
"chalk": "^5.0.1",
|
"chalk": "^5.0.1",
|
||||||
"codecov": "3.8.3",
|
"codecov": "^3.8.3",
|
||||||
"fast-check": "2.25.0",
|
"fast-check": "^2.25.0",
|
||||||
"gentype": "^4.3.0",
|
"gentype": "^4.3.0",
|
||||||
"jest": "^27.5.1",
|
"jest": "^27.5.1",
|
||||||
"mathjs": "10.5.0",
|
"moduleserve": "^0.9.1",
|
||||||
"moduleserve": "0.9.1",
|
|
||||||
"nyc": "^15.1.0",
|
"nyc": "^15.1.0",
|
||||||
"pdfast": "^0.2.0",
|
|
||||||
"reanalyze": "^2.19.0",
|
"reanalyze": "^2.19.0",
|
||||||
"ts-jest": "^27.1.4",
|
"ts-jest": "^27.1.4",
|
||||||
"ts-loader": "^9.2.8",
|
"ts-loader": "^9.3.0",
|
||||||
"ts-node": "^10.7.0",
|
"ts-node": "^10.7.0",
|
||||||
"typescript": "^4.6.3",
|
"typescript": "^4.6.3",
|
||||||
"webpack": "^5.72.0",
|
"webpack": "^5.72.0",
|
||||||
"webpack-cli": "^4.9.2"
|
"webpack-cli": "^4.9.2"
|
||||||
},
|
},
|
||||||
"source": "./src/js/index.ts",
|
"source": "./src/js/index.ts",
|
||||||
"main": "./dist/bundle.js",
|
"main": "./dist/src/js/index.js",
|
||||||
"types": "./dist/js/index.d.ts"
|
"types": "./dist/src/js/index.d.ts"
|
||||||
}
|
}
|
||||||
|
|
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,115 +1,127 @@
|
||||||
import * as _ from "lodash";
|
import * as _ from "lodash";
|
||||||
import {
|
import {
|
||||||
genericDist,
|
|
||||||
samplingParams,
|
samplingParams,
|
||||||
evaluate,
|
evaluateUsingExternalBindings,
|
||||||
|
evaluatePartialUsingExternalBindings,
|
||||||
|
externalBindings,
|
||||||
expressionValue,
|
expressionValue,
|
||||||
errorValue,
|
errorValue,
|
||||||
distributionError,
|
|
||||||
toPointSet,
|
|
||||||
continuousShape,
|
|
||||||
discreteShape,
|
|
||||||
distributionErrorToString,
|
|
||||||
} from "../rescript/TypescriptInterface.gen";
|
} from "../rescript/TypescriptInterface.gen";
|
||||||
export {
|
export {
|
||||||
makeSampleSetDist,
|
makeSampleSetDist,
|
||||||
errorValueToString,
|
errorValueToString,
|
||||||
distributionErrorToString,
|
distributionErrorToString,
|
||||||
} from "../rescript/TypescriptInterface.gen";
|
} from "../rescript/TypescriptInterface.gen";
|
||||||
|
export type {
|
||||||
|
samplingParams,
|
||||||
|
errorValue,
|
||||||
|
externalBindings as bindings,
|
||||||
|
jsImports,
|
||||||
|
};
|
||||||
import {
|
import {
|
||||||
Constructors_mean,
|
jsValueToBinding,
|
||||||
Constructors_sample,
|
jsValue,
|
||||||
Constructors_pdf,
|
rescriptExport,
|
||||||
Constructors_cdf,
|
squiggleExpression,
|
||||||
Constructors_inv,
|
convertRawToTypescript,
|
||||||
Constructors_normalize,
|
} from "./rescript_interop";
|
||||||
Constructors_isNormalized,
|
import { result, resultMap, tag, tagged } from "./types";
|
||||||
Constructors_toPointSet,
|
import { Distribution } from "./distribution";
|
||||||
Constructors_toSampleSet,
|
|
||||||
Constructors_truncate,
|
export { Distribution, squiggleExpression, result, resultMap };
|
||||||
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 };
|
|
||||||
|
|
||||||
export let defaultSamplingInputs: samplingParams = {
|
export let defaultSamplingInputs: samplingParams = {
|
||||||
sampleCount: 10000,
|
sampleCount: 10000,
|
||||||
xyPointLength: 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(
|
export function run(
|
||||||
squiggleString: string,
|
squiggleString: string,
|
||||||
samplingInputs?: samplingParams
|
bindings?: externalBindings,
|
||||||
|
samplingInputs?: samplingParams,
|
||||||
|
imports?: jsImports
|
||||||
): result<squiggleExpression, errorValue> {
|
): result<squiggleExpression, errorValue> {
|
||||||
|
let b = bindings ? bindings : defaultBindings;
|
||||||
|
let i = imports ? imports : defaultImports;
|
||||||
let si: samplingParams = samplingInputs
|
let si: samplingParams = samplingInputs
|
||||||
? samplingInputs
|
? samplingInputs
|
||||||
: defaultSamplingInputs;
|
: defaultSamplingInputs;
|
||||||
let result: result<expressionValue, errorValue> = evaluate(squiggleString);
|
|
||||||
|
let result: result<expressionValue, errorValue> =
|
||||||
|
evaluateUsingExternalBindings(squiggleString, mergeImports(b, i));
|
||||||
return resultMap(result, (x) => createTsExport(x, si));
|
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,
|
||||||
|
imports?: jsImports
|
||||||
|
): result<externalBindings, errorValue> {
|
||||||
|
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(
|
function createTsExport(
|
||||||
x: expressionValue,
|
x: expressionValue,
|
||||||
sampEnv: samplingParams
|
sampEnv: samplingParams
|
||||||
): squiggleExpression {
|
): squiggleExpression {
|
||||||
switch (x.tag) {
|
switch (x.tag) {
|
||||||
case "EvArray":
|
case "EvArray":
|
||||||
|
// genType doesn't convert anything more than 2 layers down into {tag: x, value: x}
|
||||||
|
// format, leaving it as the raw values. This converts the raw values
|
||||||
|
// directly into typescript values.
|
||||||
|
//
|
||||||
|
// The casting here is because genType is about the types of the returned
|
||||||
|
// values, claiming they are fully recursive when that's not actually the
|
||||||
|
// case
|
||||||
return tag(
|
return tag(
|
||||||
"array",
|
"array",
|
||||||
x.value.map((x) => createTsExport(x, sampEnv))
|
x.value.map((arrayItem): squiggleExpression => {
|
||||||
|
switch (arrayItem.tag) {
|
||||||
|
case "EvRecord":
|
||||||
|
return tag(
|
||||||
|
"record",
|
||||||
|
_.mapValues(arrayItem.value, (recordValue: unknown) =>
|
||||||
|
convertRawToTypescript(recordValue as rescriptExport, sampEnv)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
case "EvArray":
|
||||||
|
let y = arrayItem.value as unknown as rescriptExport[];
|
||||||
|
return tag(
|
||||||
|
"array",
|
||||||
|
y.map((childArrayItem) =>
|
||||||
|
convertRawToTypescript(childArrayItem, sampEnv)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return createTsExport(arrayItem, sampEnv);
|
||||||
|
}
|
||||||
|
})
|
||||||
);
|
);
|
||||||
case "EvBool":
|
case "EvBool":
|
||||||
return tag("boolean", x.value);
|
return tag("boolean", x.value);
|
||||||
|
@ -120,227 +132,17 @@ function createTsExport(
|
||||||
case "EvNumber":
|
case "EvNumber":
|
||||||
return tag("number", x.value);
|
return tag("number", x.value);
|
||||||
case "EvRecord":
|
case "EvRecord":
|
||||||
return tag(
|
// genType doesn't support records, so we have to do the raw conversion ourself
|
||||||
|
let result: tagged<"record", { [key: string]: squiggleExpression }> = tag(
|
||||||
"record",
|
"record",
|
||||||
_.mapValues(x.value, (x) => createTsExport(x, sampEnv))
|
_.mapValues(x.value, (x: unknown) =>
|
||||||
|
convertRawToTypescript(x as rescriptExport, sampEnv)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
return result;
|
||||||
case "EvString":
|
case "EvString":
|
||||||
return tag("string", x.value);
|
return tag("string", x.value);
|
||||||
case "EvSymbol":
|
case "EvSymbol":
|
||||||
return tag("symbol", x.value);
|
return tag("symbol", x.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 };
|
||||||
|
}
|
|
@ -158,6 +158,16 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => {
|
||||||
->GenericDist.toPointSet(~xyPointLength, ~sampleCount, ())
|
->GenericDist.toPointSet(~xyPointLength, ~sampleCount, ())
|
||||||
->E.R2.fmap(r => Dist(PointSet(r)))
|
->E.R2.fmap(r => Dist(PointSet(r)))
|
||||||
->OutputLocal.fromResult
|
->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(_), _, #Float(_)) => GenDistError(NotYetImplemented)
|
||||||
| ToDistCombination(Algebraic(strategy), arithmeticOperation, #Dist(t2)) =>
|
| ToDistCombination(Algebraic(strategy), arithmeticOperation, #Dist(t2)) =>
|
||||||
dist
|
dist
|
||||||
|
@ -193,6 +203,12 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => {
|
||||||
->GenericDist.mixture(~scaleMultiplyFn=scaleMultiply, ~pointwiseAddFn=pointwiseAdd)
|
->GenericDist.mixture(~scaleMultiplyFn=scaleMultiply, ~pointwiseAddFn=pointwiseAdd)
|
||||||
->E.R2.fmap(r => Dist(r))
|
->E.R2.fmap(r => Dist(r))
|
||||||
->OutputLocal.fromResult
|
->OutputLocal.fromResult
|
||||||
|
| FromSamples(xs) =>
|
||||||
|
xs
|
||||||
|
->SampleSetDist.make
|
||||||
|
->E.R2.errMap(x => DistributionTypes.SampleSetError(x))
|
||||||
|
->E.R2.fmap(x => x->DistributionTypes.SampleSet->Dist)
|
||||||
|
->OutputLocal.fromResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,6 +250,7 @@ module Constructors = {
|
||||||
let logScore = (~env, dist1, dist2) => C.logScore(dist1, dist2)->run(~env)->toFloatR
|
let logScore = (~env, dist1, dist2) => C.logScore(dist1, dist2)->run(~env)->toFloatR
|
||||||
let toPointSet = (~env, dist) => C.toPointSet(dist)->run(~env)->toDistR
|
let toPointSet = (~env, dist) => C.toPointSet(dist)->run(~env)->toDistR
|
||||||
let toSampleSet = (~env, dist, n) => C.toSampleSet(dist, n)->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) =>
|
let truncate = (~env, dist, leftCutoff, rightCutoff) =>
|
||||||
C.truncate(dist, leftCutoff, rightCutoff)->run(~env)->toDistR
|
C.truncate(dist, leftCutoff, rightCutoff)->run(~env)->toDistR
|
||||||
let inspect = (~env, dist) => C.inspect(dist)->run(~env)->toDistR
|
let inspect = (~env, dist) => C.inspect(dist)->run(~env)->toDistR
|
||||||
|
|
|
@ -63,6 +63,8 @@ module Constructors: {
|
||||||
@genType
|
@genType
|
||||||
let toSampleSet: (~env: env, genericDist, int) => result<genericDist, error>
|
let toSampleSet: (~env: env, genericDist, int) => result<genericDist, error>
|
||||||
@genType
|
@genType
|
||||||
|
let fromSamples: (~env: env, SampleSetDist.t) => result<genericDist, error>
|
||||||
|
@genType
|
||||||
let truncate: (~env: env, genericDist, option<float>, option<float>) => result<genericDist, error>
|
let truncate: (~env: env, genericDist, option<float>, option<float>) => result<genericDist, error>
|
||||||
@genType
|
@genType
|
||||||
let inspect: (~env: env, genericDist) => result<genericDist, error>
|
let inspect: (~env: env, genericDist) => result<genericDist, error>
|
||||||
|
|
|
@ -11,7 +11,7 @@ type error =
|
||||||
| NotYetImplemented
|
| NotYetImplemented
|
||||||
| Unreachable
|
| Unreachable
|
||||||
| DistributionVerticalShiftIsInvalid
|
| DistributionVerticalShiftIsInvalid
|
||||||
| TooFewSamples
|
| SampleSetError(SampleSetDist.sampleSetError)
|
||||||
| ArgumentError(string)
|
| ArgumentError(string)
|
||||||
| OperationError(Operation.Error.t)
|
| OperationError(Operation.Error.t)
|
||||||
| PointSetConversionError(SampleSetDist.pointsetConversionError)
|
| PointSetConversionError(SampleSetDist.pointsetConversionError)
|
||||||
|
@ -35,7 +35,8 @@ module Error = {
|
||||||
| DistributionVerticalShiftIsInvalid => "Distribution Vertical Shift is Invalid"
|
| DistributionVerticalShiftIsInvalid => "Distribution Vertical Shift is Invalid"
|
||||||
| ArgumentError(s) => `Argument Error ${s}`
|
| ArgumentError(s) => `Argument Error ${s}`
|
||||||
| LogarithmOfDistributionError(s) => `Logarithm of input 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)
|
| OperationError(err) => Operation.Error.toString(err)
|
||||||
| PointSetConversionError(err) => SampleSetDist.pointsetConversionErrorToString(err)
|
| PointSetConversionError(err) => SampleSetDist.pointsetConversionErrorToString(err)
|
||||||
| SparklineError(err) => PointSetTypes.sparklineErrorToString(err)
|
| SparklineError(err) => PointSetTypes.sparklineErrorToString(err)
|
||||||
|
@ -47,10 +48,7 @@ module Error = {
|
||||||
let resultStringToResultError: result<'a, string> => result<'a, error> = n =>
|
let resultStringToResultError: result<'a, string> => result<'a, error> = n =>
|
||||||
n->E.R2.errMap(r => r->fromString)
|
n->E.R2.errMap(r => r->fromString)
|
||||||
|
|
||||||
let sampleErrorToDistErr = (err: SampleSetDist.sampleSetError): error =>
|
let sampleErrorToDistErr = (err: SampleSetDist.sampleSetError): error => SampleSetError(err)
|
||||||
switch err {
|
|
||||||
| TooFewSamples => TooFewSamples
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@genType
|
@genType
|
||||||
|
@ -68,12 +66,19 @@ module DistributionOperation = {
|
||||||
| #Pdf(float)
|
| #Pdf(float)
|
||||||
| #Mean
|
| #Mean
|
||||||
| #Sample
|
| #Sample
|
||||||
|
| #IntegralSum
|
||||||
|
]
|
||||||
|
|
||||||
|
type toScaleFn = [
|
||||||
|
| #Power
|
||||||
|
| #Logarithm
|
||||||
]
|
]
|
||||||
|
|
||||||
type toDist =
|
type toDist =
|
||||||
| Normalize
|
| Normalize
|
||||||
| ToPointSet
|
| ToPointSet
|
||||||
| ToSampleSet(int)
|
| ToSampleSet(int)
|
||||||
|
| Scale(toScaleFn, float)
|
||||||
| Truncate(option<float>, option<float>)
|
| Truncate(option<float>, option<float>)
|
||||||
| Inspect
|
| Inspect
|
||||||
|
|
||||||
|
@ -102,6 +107,7 @@ module DistributionOperation = {
|
||||||
type genericFunctionCallInfo =
|
type genericFunctionCallInfo =
|
||||||
| FromDist(fromDist, genericDist)
|
| FromDist(fromDist, genericDist)
|
||||||
| FromFloat(fromDist, float)
|
| FromFloat(fromDist, float)
|
||||||
|
| FromSamples(array<float>)
|
||||||
| Mixture(array<(genericDist, float)>)
|
| Mixture(array<(genericDist, float)>)
|
||||||
|
|
||||||
let distCallToString = (distFunction: fromDist): string =>
|
let distCallToString = (distFunction: fromDist): string =>
|
||||||
|
@ -117,6 +123,8 @@ module DistributionOperation = {
|
||||||
| ToDist(ToSampleSet(r)) => `toSampleSet(${E.I.toString(r)})`
|
| ToDist(ToSampleSet(r)) => `toSampleSet(${E.I.toString(r)})`
|
||||||
| ToDist(Truncate(_, _)) => `truncate`
|
| ToDist(Truncate(_, _)) => `truncate`
|
||||||
| ToDist(Inspect) => `inspect`
|
| 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(ToString) => `toString`
|
||||||
| ToString(ToSparkline(n)) => `toSparkline(${E.I.toString(n)})`
|
| ToString(ToSparkline(n)) => `toSparkline(${E.I.toString(n)})`
|
||||||
| ToBool(IsNormalized) => `isNormalized`
|
| ToBool(IsNormalized) => `isNormalized`
|
||||||
|
@ -128,6 +136,7 @@ module DistributionOperation = {
|
||||||
switch d {
|
switch d {
|
||||||
| FromDist(f, _) | FromFloat(f, _) => distCallToString(f)
|
| FromDist(f, _) | FromFloat(f, _) => distCallToString(f)
|
||||||
| Mixture(_) => `mixture`
|
| Mixture(_) => `mixture`
|
||||||
|
| FromSamples(_) => `fromSamples`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
module Constructors = {
|
module Constructors = {
|
||||||
|
@ -144,9 +153,12 @@ module Constructors = {
|
||||||
let isNormalized = (dist): t => FromDist(ToBool(IsNormalized), dist)
|
let isNormalized = (dist): t => FromDist(ToBool(IsNormalized), dist)
|
||||||
let toPointSet = (dist): t => FromDist(ToDist(ToPointSet), dist)
|
let toPointSet = (dist): t => FromDist(ToDist(ToPointSet), dist)
|
||||||
let toSampleSet = (dist, r): t => FromDist(ToDist(ToSampleSet(r)), 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 truncate = (dist, left, right): t => FromDist(ToDist(Truncate(left, right)), dist)
|
||||||
let inspect = (dist): t => FromDist(ToDist(Inspect), dist)
|
let inspect = (dist): t => FromDist(ToDist(Inspect), dist)
|
||||||
let logScore = (dist1, dist2): t => FromDist(ToScore(LogScore(dist2)), dist1)
|
let logScore = (dist1, dist2): t => FromDist(ToScore(LogScore(dist2)), dist1)
|
||||||
|
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 toString = (dist): t => FromDist(ToString(ToString), dist)
|
||||||
let toSparkline = (dist, n): t => FromDist(ToString(ToSparkline(n)), dist)
|
let toSparkline = (dist, n): t => FromDist(ToString(ToSparkline(n)), dist)
|
||||||
let algebraicAdd = (dist1, dist2: genericDist): t => FromDist(
|
let algebraicAdd = (dist1, dist2: genericDist): t => FromDist(
|
||||||
|
|
|
@ -69,26 +69,31 @@ let logScore = (t1, t2, ~toPointSetFn: toPointSetFn): result<float, error> => {
|
||||||
let toFloatOperation = (
|
let toFloatOperation = (
|
||||||
t,
|
t,
|
||||||
~toPointSetFn: toPointSetFn,
|
~toPointSetFn: toPointSetFn,
|
||||||
~distToFloatOperation: Operation.distToFloatOperation,
|
~distToFloatOperation: DistributionTypes.DistributionOperation.toFloat,
|
||||||
) => {
|
) => {
|
||||||
let trySymbolicSolution = switch (t: t) {
|
switch distToFloatOperation {
|
||||||
| Symbolic(r) => SymbolicDist.T.operate(distToFloatOperation, r)->E.R.toOption
|
| #IntegralSum => Ok(integralEndY(t))
|
||||||
| _ => None
|
| (#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) {
|
let trySampleSetSolution = switch ((t: t), distToFloatOperation) {
|
||||||
| (SampleSet(sampleSet), #Mean) => SampleSetDist.mean(sampleSet)->Some
|
| (SampleSet(sampleSet), #Mean) => SampleSetDist.mean(sampleSet)->Some
|
||||||
| (SampleSet(sampleSet), #Sample) => SampleSetDist.sample(sampleSet)->Some
|
| (SampleSet(sampleSet), #Sample) => SampleSetDist.sample(sampleSet)->Some
|
||||||
| (SampleSet(sampleSet), #Inv(r)) => SampleSetDist.percentile(sampleSet, r)->Some
|
| (SampleSet(sampleSet), #Inv(r)) => SampleSetDist.percentile(sampleSet, r)->Some
|
||||||
| _ => None
|
| _ => None
|
||||||
}
|
}
|
||||||
|
|
||||||
switch trySymbolicSolution {
|
switch trySymbolicSolution {
|
||||||
| Some(r) => Ok(r)
|
| Some(r) => Ok(r)
|
||||||
| None =>
|
| None =>
|
||||||
switch trySampleSetSolution {
|
switch trySampleSetSolution {
|
||||||
| Some(r) => Ok(r)
|
| Some(r) => Ok(r)
|
||||||
| None => toPointSetFn(t)->E.R2.fmap(PointSetDist.operate(distToFloatOperation))
|
| None => toPointSetFn(t)->E.R2.fmap(PointSetDist.operate(op))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ let isNormalized: t => bool
|
||||||
let toFloatOperation: (
|
let toFloatOperation: (
|
||||||
t,
|
t,
|
||||||
~toPointSetFn: toPointSetFn,
|
~toPointSetFn: toPointSetFn,
|
||||||
~distToFloatOperation: Operation.distToFloatOperation,
|
~distToFloatOperation: DistributionTypes.DistributionOperation.toFloat,
|
||||||
) => result<float, error>
|
) => result<float, error>
|
||||||
|
|
||||||
let logScore: (t, t, ~toPointSetFn: toPointSetFn) => result<float, error>
|
let logScore: (t, t, ~toPointSetFn: toPointSetFn) => result<float, error>
|
||||||
|
|
|
@ -156,8 +156,10 @@ let reduce = (
|
||||||
~integralSumCachesFn: (float, float) => option<float>=(_, _) => None,
|
~integralSumCachesFn: (float, float) => option<float>=(_, _) => None,
|
||||||
fn: (float, float) => result<float, 'e>,
|
fn: (float, float) => result<float, 'e>,
|
||||||
continuousShapes,
|
continuousShapes,
|
||||||
): result<t, 'e> =>
|
): result<t, 'e> => {
|
||||||
continuousShapes |> E.A.R.foldM(combinePointwise(~integralSumCachesFn, fn), empty)
|
let merge = combinePointwise(~integralSumCachesFn, fn)
|
||||||
|
continuousShapes |> E.A.R.foldM(merge, empty)
|
||||||
|
}
|
||||||
|
|
||||||
let mapYResult = (
|
let mapYResult = (
|
||||||
~integralSumCacheFn=_ => None,
|
~integralSumCacheFn=_ => None,
|
||||||
|
|
|
@ -37,7 +37,7 @@ let combinePointwise = (
|
||||||
~fn=(a, b) => Ok(a +. b),
|
~fn=(a, b) => Ok(a +. b),
|
||||||
t1: PointSetTypes.discreteShape,
|
t1: PointSetTypes.discreteShape,
|
||||||
t2: PointSetTypes.discreteShape,
|
t2: PointSetTypes.discreteShape,
|
||||||
): PointSetTypes.discreteShape => {
|
): result<PointSetTypes.discreteShape, 'e> => {
|
||||||
let combinedIntegralSum = Common.combineIntegralSums(
|
let combinedIntegralSum = Common.combineIntegralSums(
|
||||||
integralSumCachesFn,
|
integralSumCachesFn,
|
||||||
t1.integralSumCache,
|
t1.integralSumCache,
|
||||||
|
@ -54,11 +54,17 @@ let combinePointwise = (
|
||||||
t1.xyShape,
|
t1.xyShape,
|
||||||
t2.xyShape,
|
t2.xyShape,
|
||||||
)->E.R.toExn("Addition operation should never fail", _),
|
)->E.R.toExn("Addition operation should never fail", _),
|
||||||
)
|
)->Ok
|
||||||
}
|
}
|
||||||
|
|
||||||
let reduce = (~integralSumCachesFn=(_, _) => None, discreteShapes): PointSetTypes.discreteShape =>
|
let reduce = (
|
||||||
discreteShapes |> E.A.fold_left(combinePointwise(~integralSumCachesFn), empty)
|
~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 => {
|
let updateIntegralSumCache = (integralSumCache, t: t): t => {
|
||||||
...t,
|
...t,
|
||||||
|
|
|
@ -350,3 +350,43 @@ let combineAlgebraically = (op: Operation.convolutionOperation, t1: t, t2: t): t
|
||||||
integralCache: None,
|
integralCache: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let combinePointwise = (
|
||||||
|
~integralSumCachesFn=(_, _) => None,
|
||||||
|
~integralCachesFn=(_, _) => None,
|
||||||
|
fn: (float, float) => result<float, 'e>,
|
||||||
|
t1: t,
|
||||||
|
t2: t,
|
||||||
|
): result<t, 'e> => {
|
||||||
|
let reducedDiscrete =
|
||||||
|
[t1, t2]
|
||||||
|
|> E.A.fmap(toDiscrete)
|
||||||
|
|> E.A.O.concatSomes
|
||||||
|
|> Discrete.reduce(~integralSumCachesFn, fn)
|
||||||
|
|
||||||
|
let reducedContinuous =
|
||||||
|
[t1, t2]
|
||||||
|
|> E.A.fmap(toContinuous)
|
||||||
|
|> E.A.O.concatSomes
|
||||||
|
|> Continuous.reduce(~integralSumCachesFn, fn)
|
||||||
|
|
||||||
|
let combinedIntegralSum = Common.combineIntegralSums(
|
||||||
|
integralSumCachesFn,
|
||||||
|
t1.integralSumCache,
|
||||||
|
t2.integralSumCache,
|
||||||
|
)
|
||||||
|
|
||||||
|
let combinedIntegral = Common.combineIntegrals(
|
||||||
|
integralCachesFn,
|
||||||
|
t1.integralCache,
|
||||||
|
t2.integralCache,
|
||||||
|
)
|
||||||
|
E.R.merge(reducedContinuous, reducedDiscrete)->E.R2.fmap(((continuous, discrete)) =>
|
||||||
|
make(
|
||||||
|
~integralSumCache=combinedIntegralSum,
|
||||||
|
~integralCache=combinedIntegral,
|
||||||
|
~discrete,
|
||||||
|
~continuous,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -84,7 +84,12 @@ let combinePointwise = (
|
||||||
m2,
|
m2,
|
||||||
)->E.R2.fmap(x => PointSetTypes.Continuous(x))
|
)->E.R2.fmap(x => PointSetTypes.Continuous(x))
|
||||||
| (Discrete(m1), Discrete(m2)) =>
|
| (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) =>
|
| (m1, m2) =>
|
||||||
Mixed.combinePointwise(
|
Mixed.combinePointwise(
|
||||||
~integralSumCachesFn,
|
~integralSumCachesFn,
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
@genType
|
@genType
|
||||||
module Error = {
|
module Error = {
|
||||||
@genType
|
@genType
|
||||||
type sampleSetError = TooFewSamples
|
type sampleSetError = TooFewSamples | NonNumericInput(string)
|
||||||
|
|
||||||
let sampleSetErrorToString = (err: sampleSetError): string =>
|
let sampleSetErrorToString = (err: sampleSetError): string =>
|
||||||
switch err {
|
switch err {
|
||||||
| TooFewSamples => "Too few samples when constructing sample set"
|
| TooFewSamples => "Too few samples when constructing sample set"
|
||||||
|
| NonNumericInput(err) => `Found a non-number in input: ${err}`
|
||||||
}
|
}
|
||||||
|
|
||||||
@genType
|
@genType
|
||||||
|
|
|
@ -1,27 +1,30 @@
|
||||||
//The math here was taken from https://github.com/jasondavies/science.js/blob/master/src/stats/SampleSetDist_Bandwidth.js
|
//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 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.
|
// Silverman, B. W. (1986) Density Estimation. London: Chapman and Hall.
|
||||||
let nrd0 = x => {
|
let nrd0 = x => {
|
||||||
let hi = Js_math.sqrt(Jstat.variance(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 e = Js_math.abs_float(x[1])
|
||||||
let lo' = switch (lo, hi, e) {
|
let lo' = switch (lo, hi, e) {
|
||||||
| (lo, _, _) if !Js.Float.isNaN(lo) => lo
|
| (lo, _, _) if !Js.Float.isNaN(lo) => lo
|
||||||
| (_, hi, _) if !Js.Float.isNaN(hi) => hi
|
| (_, hi, _) if !Js.Float.isNaN(hi) => hi
|
||||||
| (_, _, e) if !Js.Float.isNaN(e) => e
|
| (_, _, 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.
|
// Scott, D. W. (1992) Multivariate Density Estimation: Theory, Practice, and Visualization. Wiley.
|
||||||
let nrd = x => {
|
let nrd = x => {
|
||||||
let h = iqr(x) /. 1.34
|
let h = iqr(x) /. nrd0_lo_denominator
|
||||||
1.06 *.
|
nrd_coef *.
|
||||||
Js.Math.min_float(Js.Math.sqrt(Jstat.variance(x)), h) *.
|
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)
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -132,6 +132,7 @@ module Helpers = {
|
||||||
| Error(err) => GenDistError(ArgumentError(err))
|
| Error(err) => GenDistError(ArgumentError(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
| Some(EvNumber(_))
|
||||||
| Some(EvDistribution(_)) =>
|
| Some(EvDistribution(_)) =>
|
||||||
switch parseDistributionArray(args) {
|
switch parseDistributionArray(args) {
|
||||||
| Ok(distributions) => mixtureWithDefaultWeights(distributions)
|
| Ok(distributions) => mixtureWithDefaultWeights(distributions)
|
||||||
|
@ -197,6 +198,7 @@ let dispatchToGenericOutput = (call: ExpressionValue.functionCall): option<
|
||||||
->SymbolicConstructors.symbolicResultToOutput
|
->SymbolicConstructors.symbolicResultToOutput
|
||||||
| ("sample", [EvDistribution(dist)]) => Helpers.toFloatFn(#Sample, dist)
|
| ("sample", [EvDistribution(dist)]) => Helpers.toFloatFn(#Sample, dist)
|
||||||
| ("mean", [EvDistribution(dist)]) => Helpers.toFloatFn(#Mean, dist)
|
| ("mean", [EvDistribution(dist)]) => Helpers.toFloatFn(#Mean, dist)
|
||||||
|
| ("integralSum", [EvDistribution(dist)]) => Helpers.toFloatFn(#IntegralSum, dist)
|
||||||
| ("toString", [EvDistribution(dist)]) => Helpers.toStringFn(ToString, dist)
|
| ("toString", [EvDistribution(dist)]) => Helpers.toStringFn(ToString, dist)
|
||||||
| ("toSparkline", [EvDistribution(dist)]) => Helpers.toStringFn(ToSparkline(20), dist)
|
| ("toSparkline", [EvDistribution(dist)]) => Helpers.toStringFn(ToSparkline(20), dist)
|
||||||
| ("toSparkline", [EvDistribution(dist), EvNumber(n)]) =>
|
| ("toSparkline", [EvDistribution(dist), EvNumber(n)]) =>
|
||||||
|
@ -215,6 +217,15 @@ let dispatchToGenericOutput = (call: ExpressionValue.functionCall): option<
|
||||||
)
|
)
|
||||||
| ("isNormalized", [EvDistribution(dist)]) => Helpers.toBoolFn(IsNormalized, dist)
|
| ("isNormalized", [EvDistribution(dist)]) => Helpers.toBoolFn(IsNormalized, dist)
|
||||||
| ("toPointSet", [EvDistribution(dist)]) => Helpers.toDistFn(ToPointSet, 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)
|
| ("cdf", [EvDistribution(dist), EvNumber(float)]) => Helpers.toFloatFn(#Cdf(float), dist)
|
||||||
| ("pdf", [EvDistribution(dist), EvNumber(float)]) => Helpers.toFloatFn(#Pdf(float), dist)
|
| ("pdf", [EvDistribution(dist), EvNumber(float)]) => Helpers.toFloatFn(#Pdf(float), dist)
|
||||||
| ("inv", [EvDistribution(dist), EvNumber(float)]) => Helpers.toFloatFn(#Inv(float), dist)
|
| ("inv", [EvDistribution(dist), EvNumber(float)]) => Helpers.toFloatFn(#Inv(float), dist)
|
||||||
|
@ -222,6 +233,14 @@ let dispatchToGenericOutput = (call: ExpressionValue.functionCall): option<
|
||||||
Helpers.toDistFn(ToSampleSet(Belt.Int.fromFloat(float)), dist)
|
Helpers.toDistFn(ToSampleSet(Belt.Int.fromFloat(float)), dist)
|
||||||
| ("toSampleSet", [EvDistribution(dist)]) =>
|
| ("toSampleSet", [EvDistribution(dist)]) =>
|
||||||
Helpers.toDistFn(ToSampleSet(MagicNumbers.Environment.defaultSampleCount), 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)
|
| ("inspect", [EvDistribution(dist)]) => Helpers.toDistFn(Inspect, dist)
|
||||||
| ("truncateLeft", [EvDistribution(dist), EvNumber(float)]) =>
|
| ("truncateLeft", [EvDistribution(dist), EvNumber(float)]) =>
|
||||||
Helpers.toDistFn(Truncate(Some(float), None), dist)
|
Helpers.toDistFn(Truncate(Some(float), None), dist)
|
||||||
|
|
|
@ -13,6 +13,12 @@ type samplingParams = DistributionOperation.env
|
||||||
@genType
|
@genType
|
||||||
type genericDist = DistributionTypes.genericDist
|
type genericDist = DistributionTypes.genericDist
|
||||||
|
|
||||||
|
@genType
|
||||||
|
type sampleSetDist = SampleSetDist.t
|
||||||
|
|
||||||
|
@genType
|
||||||
|
type symbolicDist = SymbolicDistTypes.symbolicDist
|
||||||
|
|
||||||
@genType
|
@genType
|
||||||
type distributionError = DistributionTypes.error
|
type distributionError = DistributionTypes.error
|
||||||
|
|
||||||
|
@ -34,6 +40,12 @@ let evaluate = Reducer.evaluate
|
||||||
@genType
|
@genType
|
||||||
let evaluateUsingExternalBindings = Reducer.evaluateUsingExternalBindings
|
let evaluateUsingExternalBindings = Reducer.evaluateUsingExternalBindings
|
||||||
|
|
||||||
|
@genType
|
||||||
|
let evaluatePartialUsingExternalBindings = Reducer.evaluatePartialUsingExternalBindings
|
||||||
|
|
||||||
|
@genType
|
||||||
|
type externalBindings = Reducer.externalBindings
|
||||||
|
|
||||||
@genType
|
@genType
|
||||||
type expressionValue = ReducerInterface_ExpressionValue.expressionValue
|
type expressionValue = ReducerInterface_ExpressionValue.expressionValue
|
||||||
|
|
||||||
|
|
|
@ -289,6 +289,13 @@ module R = {
|
||||||
| Ok(r) => r->Ok
|
| Ok(r) => r->Ok
|
||||||
| Error(x) => x->f->Error
|
| 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 = {
|
module R2 = {
|
||||||
|
@ -307,6 +314,8 @@ module R2 = {
|
||||||
| Ok(x) => x->Ok
|
| Ok(x) => x->Ok
|
||||||
| Error(x) => x->f->Error
|
| Error(x) => x->f->Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let toExn = (a, b) => R.toExn(b, a)
|
||||||
}
|
}
|
||||||
|
|
||||||
let safe_fn_of_string = (fn, s: string): option<'a> =>
|
let safe_fn_of_string = (fn, s: string): option<'a> =>
|
||||||
|
@ -350,7 +359,7 @@ module JsDate = {
|
||||||
/* List */
|
/* List */
|
||||||
module L = {
|
module L = {
|
||||||
module Util = {
|
module Util = {
|
||||||
let eq = (a, b) => a == b
|
let eq = \"=="
|
||||||
}
|
}
|
||||||
let fmap = List.map
|
let fmap = List.map
|
||||||
let get = Belt.List.get
|
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.
|
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
|
## Operating on distributions
|
||||||
|
|
||||||
Here are the ways we combine distributions.
|
Here are the ways we combine distributions.
|
||||||
|
@ -315,6 +328,16 @@ Or `PointSet` format
|
||||||
|
|
||||||
<SquiggleEditor initialSquiggleString="toPointSet(normal(5, 10))" />
|
<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
|
## Normalization
|
||||||
|
|
||||||
Some distribution operations (like horizontal shift) return an unnormalized distriibution.
|
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
|
- 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`
|
## `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.
|
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.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user