Merge branch 'develop' into collapsible
This commit is contained in:
commit
8f4259cef3
|
@ -13,7 +13,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chalk": "^5.0.1",
|
"chalk": "^5.0.1",
|
||||||
"chokidar": "^3.5.3",
|
"chokidar": "^3.5.3",
|
||||||
"commander": "^9.3.0",
|
"commander": "^9.4.0",
|
||||||
"fs": "^0.0.1-security",
|
"fs": "^0.0.1-security",
|
||||||
"glob": "^8.0.3",
|
"glob": "^8.0.3",
|
||||||
"indent-string": "^5.0.0"
|
"indent-string": "^5.0.0"
|
||||||
|
|
|
@ -1,31 +1,31 @@
|
||||||
{
|
{
|
||||||
"name": "@quri/squiggle-components",
|
"name": "@quri/squiggle-components",
|
||||||
"version": "0.2.20",
|
"version": "0.2.23",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@floating-ui/react-dom": "^0.7.2",
|
"@floating-ui/react-dom": "^0.7.2",
|
||||||
"@floating-ui/react-dom-interactions": "^0.6.6",
|
"@floating-ui/react-dom-interactions": "^0.6.6",
|
||||||
"@headlessui/react": "^1.6.5",
|
"@headlessui/react": "^1.6.6",
|
||||||
"@heroicons/react": "^1.0.6",
|
"@heroicons/react": "^1.0.6",
|
||||||
"@hookform/resolvers": "^2.9.3",
|
"@hookform/resolvers": "^2.9.6",
|
||||||
"@quri/squiggle-lang": "^0.2.8",
|
"@quri/squiggle-lang": "^0.2.8",
|
||||||
"@react-hook/size": "^2.1.2",
|
"@react-hook/size": "^2.1.2",
|
||||||
"clsx": "^1.1.1",
|
"clsx": "^1.2.1",
|
||||||
"framer-motion": "^6.4.1",
|
"framer-motion": "^6.5.1",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"react": "^18.1.0",
|
"react": "^18.1.0",
|
||||||
"react-ace": "^10.1.0",
|
"react-ace": "^10.1.0",
|
||||||
"react-hook-form": "^7.33.0",
|
"react-hook-form": "^7.33.1",
|
||||||
"react-use": "^17.4.0",
|
"react-use": "^17.4.0",
|
||||||
"react-vega": "^7.5.1",
|
"react-vega": "^7.6.0",
|
||||||
"vega": "^5.22.1",
|
"vega": "^5.22.1",
|
||||||
"vega-embed": "^6.21.0",
|
"vega-embed": "^6.21.0",
|
||||||
"vega-lite": "^5.2.0",
|
"vega-lite": "^5.3.0",
|
||||||
"vscode-uri": "^3.0.3",
|
"vscode-uri": "^3.0.3",
|
||||||
"yup": "^0.32.11"
|
"yup": "^0.32.11"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/plugin-proposal-private-property-in-object": "^7.17.12",
|
"@babel/plugin-proposal-private-property-in-object": "^7.18.6",
|
||||||
"@storybook/addon-actions": "^6.5.9",
|
"@storybook/addon-actions": "^6.5.9",
|
||||||
"@storybook/addon-essentials": "^6.5.9",
|
"@storybook/addon-essentials": "^6.5.9",
|
||||||
"@storybook/addon-links": "^6.5.9",
|
"@storybook/addon-links": "^6.5.9",
|
||||||
|
@ -36,29 +36,29 @@
|
||||||
"@storybook/react": "^6.5.9",
|
"@storybook/react": "^6.5.9",
|
||||||
"@testing-library/jest-dom": "^5.16.4",
|
"@testing-library/jest-dom": "^5.16.4",
|
||||||
"@testing-library/react": "^13.3.0",
|
"@testing-library/react": "^13.3.0",
|
||||||
"@testing-library/user-event": "^14.2.1",
|
"@testing-library/user-event": "^14.2.6",
|
||||||
"@types/jest": "^27.5.0",
|
"@types/jest": "^27.5.0",
|
||||||
"@types/lodash": "^4.14.182",
|
"@types/lodash": "^4.14.182",
|
||||||
"@types/node": "^18.0.0",
|
"@types/node": "^18.0.6",
|
||||||
"@types/react": "^18.0.9",
|
"@types/react": "^18.0.9",
|
||||||
"@types/styled-components": "^5.1.24",
|
"@types/styled-components": "^5.1.24",
|
||||||
"@types/webpack": "^5.28.0",
|
"@types/webpack": "^5.28.0",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"mini-css-extract-plugin": "^2.6.1",
|
"mini-css-extract-plugin": "^2.6.1",
|
||||||
"postcss-cli": "^9.1.0",
|
"postcss-cli": "^10.0.0",
|
||||||
"postcss-import": "^14.1.0",
|
"postcss-import": "^14.1.0",
|
||||||
"postcss-loader": "^7.0.0",
|
"postcss-loader": "^7.0.1",
|
||||||
"react": "^18.1.0",
|
"react": "^18.1.0",
|
||||||
"react-scripts": "^5.0.1",
|
"react-scripts": "^5.0.1",
|
||||||
"style-loader": "^3.3.1",
|
"style-loader": "^3.3.1",
|
||||||
"tailwindcss": "^3.1.3",
|
"tailwindcss": "^3.1.6",
|
||||||
"ts-loader": "^9.3.0",
|
"ts-loader": "^9.3.0",
|
||||||
"tsconfig-paths-webpack-plugin": "^3.5.2",
|
"tsconfig-paths-webpack-plugin": "^3.5.2",
|
||||||
"typescript": "^4.7.4",
|
"typescript": "^4.7.4",
|
||||||
"web-vitals": "^2.1.4",
|
"web-vitals": "^2.1.4",
|
||||||
"webpack": "^5.73.0",
|
"webpack": "^5.73.0",
|
||||||
"webpack-cli": "^4.10.0",
|
"webpack-cli": "^4.10.0",
|
||||||
"webpack-dev-server": "^4.9.2"
|
"webpack-dev-server": "^4.9.3"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "^16.8.0 || ^17 || ^18",
|
"react": "^16.8.0 || ^17 || ^18",
|
||||||
|
|
|
@ -5,18 +5,15 @@ import {
|
||||||
distributionError,
|
distributionError,
|
||||||
distributionErrorToString,
|
distributionErrorToString,
|
||||||
} from "@quri/squiggle-lang";
|
} from "@quri/squiggle-lang";
|
||||||
import { Vega, VisualizationSpec } from "react-vega";
|
import { Vega } from "react-vega";
|
||||||
import * as chartSpecification from "../vega-specs/spec-distributions.json";
|
|
||||||
import { ErrorAlert } from "./Alert";
|
import { ErrorAlert } from "./Alert";
|
||||||
import { useSize } from "react-use";
|
import { useSize } from "react-use";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
linearXScale,
|
buildVegaSpec,
|
||||||
logXScale,
|
DistributionChartSpecOptions,
|
||||||
linearYScale,
|
} from "../lib/distributionSpecBuilder";
|
||||||
expYScale,
|
|
||||||
} from "./DistributionVegaScales";
|
|
||||||
import { NumberShower } from "./NumberShower";
|
import { NumberShower } from "./NumberShower";
|
||||||
|
|
||||||
export type DistributionPlottingSettings = {
|
export type DistributionPlottingSettings = {
|
||||||
|
@ -24,19 +21,17 @@ export type DistributionPlottingSettings = {
|
||||||
showSummary: boolean;
|
showSummary: boolean;
|
||||||
/** Whether to show the user graph controls (scale etc) */
|
/** Whether to show the user graph controls (scale etc) */
|
||||||
showControls: boolean;
|
showControls: boolean;
|
||||||
/** Set the x scale to be logarithmic by deault */
|
} & DistributionChartSpecOptions;
|
||||||
logX: boolean;
|
|
||||||
/** Set the y scale to be exponential by deault */
|
|
||||||
expY: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type DistributionChartProps = {
|
export type DistributionChartProps = {
|
||||||
distribution: Distribution;
|
distribution: Distribution;
|
||||||
width?: number;
|
width?: number;
|
||||||
height: number;
|
height: number;
|
||||||
|
actions?: boolean;
|
||||||
} & DistributionPlottingSettings;
|
} & DistributionPlottingSettings;
|
||||||
|
|
||||||
export const DistributionChart: React.FC<DistributionChartProps> = ({
|
export const DistributionChart: React.FC<DistributionChartProps> = (props) => {
|
||||||
|
const {
|
||||||
distribution,
|
distribution,
|
||||||
height,
|
height,
|
||||||
showSummary,
|
showSummary,
|
||||||
|
@ -44,7 +39,8 @@ export const DistributionChart: React.FC<DistributionChartProps> = ({
|
||||||
showControls,
|
showControls,
|
||||||
logX,
|
logX,
|
||||||
expY,
|
expY,
|
||||||
}) => {
|
actions = false,
|
||||||
|
} = props;
|
||||||
const [isLogX, setLogX] = React.useState(logX);
|
const [isLogX, setLogX] = React.useState(logX);
|
||||||
const [isExpY, setExpY] = React.useState(expY);
|
const [isExpY, setExpY] = React.useState(expY);
|
||||||
|
|
||||||
|
@ -64,7 +60,7 @@ export const DistributionChart: React.FC<DistributionChartProps> = ({
|
||||||
const massBelow0 =
|
const massBelow0 =
|
||||||
shape.value.continuous.some((x) => x.x <= 0) ||
|
shape.value.continuous.some((x) => x.x <= 0) ||
|
||||||
shape.value.discrete.some((x) => x.x <= 0);
|
shape.value.discrete.some((x) => x.x <= 0);
|
||||||
const spec = buildVegaSpec(isLogX, isExpY);
|
const spec = buildVegaSpec(props);
|
||||||
|
|
||||||
let widthProp = width ? width : size.width;
|
let widthProp = width ? width : size.width;
|
||||||
if (widthProp < 20) {
|
if (widthProp < 20) {
|
||||||
|
@ -82,7 +78,7 @@ export const DistributionChart: React.FC<DistributionChartProps> = ({
|
||||||
data={{ con: shape.value.continuous, dis: shape.value.discrete }}
|
data={{ con: shape.value.continuous, dis: shape.value.discrete }}
|
||||||
width={widthProp - 10}
|
width={widthProp - 10}
|
||||||
height={height}
|
height={height}
|
||||||
actions={false}
|
actions={actions}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<ErrorAlert heading="Log Domain Error">
|
<ErrorAlert heading="Log Domain Error">
|
||||||
|
@ -116,16 +112,6 @@ export const DistributionChart: React.FC<DistributionChartProps> = ({
|
||||||
return sized;
|
return sized;
|
||||||
};
|
};
|
||||||
|
|
||||||
function buildVegaSpec(isLogX: boolean, isExpY: boolean): VisualizationSpec {
|
|
||||||
return {
|
|
||||||
...chartSpecification,
|
|
||||||
scales: [
|
|
||||||
isLogX ? logXScale : linearXScale,
|
|
||||||
isExpY ? expYScale : linearYScale,
|
|
||||||
],
|
|
||||||
} as VisualizationSpec;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface CheckBoxProps {
|
interface CheckBoxProps {
|
||||||
label: string;
|
label: string;
|
||||||
onChange: (x: boolean) => void;
|
onChange: (x: boolean) => void;
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { lambdaValue, environment, runForeign } from "@quri/squiggle-lang";
|
import {
|
||||||
|
lambdaValue,
|
||||||
|
environment,
|
||||||
|
runForeign,
|
||||||
|
errorValueToString,
|
||||||
|
} from "@quri/squiggle-lang";
|
||||||
import { FunctionChart1Dist } from "./FunctionChart1Dist";
|
import { FunctionChart1Dist } from "./FunctionChart1Dist";
|
||||||
import { FunctionChart1Number } from "./FunctionChart1Number";
|
import { FunctionChart1Number } from "./FunctionChart1Number";
|
||||||
import { DistributionPlottingSettings } from "./DistributionChart";
|
import { DistributionPlottingSettings } from "./DistributionChart";
|
||||||
|
@ -45,10 +50,16 @@ export const FunctionChart: React.FC<FunctionChartProps> = ({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const validResult = getValidResult();
|
const validResult = getValidResult();
|
||||||
const resultType =
|
|
||||||
validResult.tag === "Ok" ? validResult.value.tag : ("Error" as const);
|
|
||||||
|
|
||||||
switch (resultType) {
|
if (validResult.tag === "Error") {
|
||||||
|
return (
|
||||||
|
<ErrorAlert heading="Error">
|
||||||
|
{errorValueToString(validResult.value)}
|
||||||
|
</ErrorAlert>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (validResult.value.tag) {
|
||||||
case "distribution":
|
case "distribution":
|
||||||
return (
|
return (
|
||||||
<FunctionChart1Dist
|
<FunctionChart1Dist
|
||||||
|
@ -68,15 +79,11 @@ export const FunctionChart: React.FC<FunctionChartProps> = ({
|
||||||
height={height}
|
height={height}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case "Error":
|
|
||||||
return (
|
|
||||||
<ErrorAlert heading="Error">The function failed to be run</ErrorAlert>
|
|
||||||
);
|
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<MessageAlert heading="Function Display Not Supported">
|
<MessageAlert heading="Function Display Not Supported">
|
||||||
There is no function visualization for this type of output:{" "}
|
There is no function visualization for this type of output:{" "}
|
||||||
<span className="font-bold">{resultType}</span>
|
<span className="font-bold">{validResult.value.tag}</span>
|
||||||
</MessageAlert>
|
</MessageAlert>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,7 +88,7 @@ let getPercentiles = ({ chartSettings, fn, environment }) => {
|
||||||
let chartPointsData: point[] = chartPointsToRender.map((x) => {
|
let chartPointsData: point[] = chartPointsToRender.map((x) => {
|
||||||
let result = runForeign(fn, [x], environment);
|
let result = runForeign(fn, [x], environment);
|
||||||
if (result.tag === "Ok") {
|
if (result.tag === "Ok") {
|
||||||
if (result.value.tag == "distribution") {
|
if (result.value.tag === "distribution") {
|
||||||
return { x, value: { tag: "Ok", value: result.value.value } };
|
return { x, value: { tag: "Ok", value: result.value.value } };
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
|
@ -165,12 +165,14 @@ export const FunctionChart1Dist: React.FC<FunctionChart1DistProps> = ({
|
||||||
setMouseOverlay(NaN);
|
setMouseOverlay(NaN);
|
||||||
}
|
}
|
||||||
const signalListeners = { mousemove: handleHover, mouseout: handleOut };
|
const signalListeners = { mousemove: handleHover, mouseout: handleOut };
|
||||||
|
|
||||||
|
//TODO: This custom error handling is a bit hacky and should be improved.
|
||||||
let mouseItem: result<squiggleExpression, errorValue> = !!mouseOverlay
|
let mouseItem: result<squiggleExpression, errorValue> = !!mouseOverlay
|
||||||
? runForeign(fn, [mouseOverlay], environment)
|
? runForeign(fn, [mouseOverlay], environment)
|
||||||
: {
|
: {
|
||||||
tag: "Error",
|
tag: "Error",
|
||||||
value: {
|
value: {
|
||||||
tag: "REExpectedType",
|
tag: "RETodo",
|
||||||
value: "Hover x-coordinate returned NaN. Expected a number.",
|
value: "Hover x-coordinate returned NaN. Expected a number.",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -41,6 +41,18 @@ export interface SquiggleChartProps {
|
||||||
logX?: boolean;
|
logX?: boolean;
|
||||||
/** Set the y scale to be exponential by deault */
|
/** Set the y scale to be exponential by deault */
|
||||||
expY?: boolean;
|
expY?: boolean;
|
||||||
|
/** How to format numbers on the x axis */
|
||||||
|
tickFormat?: string;
|
||||||
|
/** Title of the graphed distribution */
|
||||||
|
title?: string;
|
||||||
|
/** Color of the graphed distribution */
|
||||||
|
color?: string;
|
||||||
|
/** Specify the lower bound of the x scale */
|
||||||
|
minX?: number;
|
||||||
|
/** Specify the upper bound of the x scale */
|
||||||
|
maxX?: number;
|
||||||
|
/** Whether to show vega actions to the user, so they can copy the chart spec */
|
||||||
|
distributionChartActions?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultOnChange = () => {};
|
const defaultOnChange = () => {};
|
||||||
|
@ -61,6 +73,12 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = React.memo(
|
||||||
diagramStart = 0,
|
diagramStart = 0,
|
||||||
diagramStop = 10,
|
diagramStop = 10,
|
||||||
diagramCount = 100,
|
diagramCount = 100,
|
||||||
|
tickFormat,
|
||||||
|
minX,
|
||||||
|
maxX,
|
||||||
|
color,
|
||||||
|
title,
|
||||||
|
distributionChartActions,
|
||||||
}) => {
|
}) => {
|
||||||
const result = useSquiggle({
|
const result = useSquiggle({
|
||||||
code,
|
code,
|
||||||
|
@ -75,6 +93,12 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = React.memo(
|
||||||
showSummary,
|
showSummary,
|
||||||
logX,
|
logX,
|
||||||
expY,
|
expY,
|
||||||
|
format: tickFormat,
|
||||||
|
minX,
|
||||||
|
maxX,
|
||||||
|
color,
|
||||||
|
title,
|
||||||
|
actions: distributionChartActions,
|
||||||
};
|
};
|
||||||
|
|
||||||
const chartSettings = {
|
const chartSettings = {
|
||||||
|
|
|
@ -18,7 +18,7 @@ import clsx from "clsx";
|
||||||
|
|
||||||
import { defaultBindings, environment } from "@quri/squiggle-lang";
|
import { defaultBindings, environment } from "@quri/squiggle-lang";
|
||||||
|
|
||||||
import { SquiggleChart } from "./SquiggleChart";
|
import { SquiggleChart, SquiggleChartProps } from "./SquiggleChart";
|
||||||
import { CodeEditor } from "./CodeEditor";
|
import { CodeEditor } from "./CodeEditor";
|
||||||
import { JsonEditor } from "./JsonEditor";
|
import { JsonEditor } from "./JsonEditor";
|
||||||
import { ErrorAlert, SuccessAlert } from "./Alert";
|
import { ErrorAlert, SuccessAlert } from "./Alert";
|
||||||
|
@ -27,26 +27,15 @@ import { Toggle } from "./ui/Toggle";
|
||||||
import { Checkbox } from "./ui/Checkbox";
|
import { Checkbox } from "./ui/Checkbox";
|
||||||
import { StyledTab } from "./ui/StyledTab";
|
import { StyledTab } from "./ui/StyledTab";
|
||||||
|
|
||||||
interface PlaygroundProps {
|
type PlaygroundProps = SquiggleChartProps & {
|
||||||
/** The initial squiggle string to put in the playground */
|
/** The initial squiggle string to put in the playground */
|
||||||
defaultCode?: string;
|
defaultCode?: string;
|
||||||
/** How many pixels high is the playground */
|
|
||||||
height?: number;
|
|
||||||
/** Whether to show the log scale controls in the playground */
|
|
||||||
showControls?: boolean;
|
|
||||||
/** Whether to show the summary table in the playground */
|
|
||||||
showSummary?: boolean;
|
|
||||||
/** Whether to log the x coordinate on distribution charts */
|
|
||||||
logX?: boolean;
|
|
||||||
/** Whether to exp the y coordinate on distribution charts */
|
|
||||||
expY?: boolean;
|
|
||||||
/** If code is set, component becomes controlled */
|
|
||||||
code?: string;
|
|
||||||
onCodeChange?(expr: string): void;
|
onCodeChange?(expr: string): void;
|
||||||
|
/* When settings change */
|
||||||
onSettingsChange?(settings: any): void;
|
onSettingsChange?(settings: any): void;
|
||||||
/** Should we show the editor? */
|
/** Should we show the editor? */
|
||||||
showEditor?: boolean;
|
showEditor?: boolean;
|
||||||
}
|
};
|
||||||
|
|
||||||
const schema = yup.object({}).shape({
|
const schema = yup.object({}).shape({
|
||||||
sampleCount: yup
|
sampleCount: yup
|
||||||
|
@ -79,6 +68,12 @@ const schema = yup.object({}).shape({
|
||||||
showEditor: yup.boolean().required(),
|
showEditor: yup.boolean().required(),
|
||||||
logX: yup.boolean().required(),
|
logX: yup.boolean().required(),
|
||||||
expY: yup.boolean().required(),
|
expY: yup.boolean().required(),
|
||||||
|
tickFormat: yup.string().default(".9~s"),
|
||||||
|
title: yup.string(),
|
||||||
|
color: yup.string().default("#739ECC").required(),
|
||||||
|
minX: yup.number(),
|
||||||
|
maxX: yup.number(),
|
||||||
|
distributionChartActions: yup.boolean(),
|
||||||
showSettingsPage: yup.boolean().default(false),
|
showSettingsPage: yup.boolean().default(false),
|
||||||
diagramStart: yup.number().required().positive().integer().default(0).min(0),
|
diagramStart: yup.number().required().positive().integer().default(0).min(0),
|
||||||
diagramStop: yup.number().required().positive().integer().default(10).min(0),
|
diagramStop: yup.number().required().positive().integer().default(10).min(0),
|
||||||
|
@ -111,7 +106,7 @@ function InputItem<T>({
|
||||||
}: {
|
}: {
|
||||||
name: Path<T>;
|
name: Path<T>;
|
||||||
label: string;
|
label: string;
|
||||||
type: "number";
|
type: "number" | "text" | "color";
|
||||||
register: UseFormRegister<T>;
|
register: UseFormRegister<T>;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
|
@ -119,7 +114,7 @@ function InputItem<T>({
|
||||||
<div className="text-sm font-medium text-gray-600 mb-1">{label}</div>
|
<div className="text-sm font-medium text-gray-600 mb-1">{label}</div>
|
||||||
<input
|
<input
|
||||||
type={type}
|
type={type}
|
||||||
{...register(name)}
|
{...register(name, { valueAsNumber: type === "number" })}
|
||||||
className="form-input max-w-lg block w-full shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:max-w-xs sm:text-sm border-gray-300 rounded-md"
|
className="form-input max-w-lg block w-full shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:max-w-xs sm:text-sm border-gray-300 rounded-md"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
@ -194,6 +189,11 @@ const ViewSettings: React.FC<{ register: UseFormRegister<FormFields> }> = ({
|
||||||
name="expY"
|
name="expY"
|
||||||
label="Show y scale exponentially"
|
label="Show y scale exponentially"
|
||||||
/>
|
/>
|
||||||
|
<Checkbox
|
||||||
|
register={register}
|
||||||
|
name="distributionChartActions"
|
||||||
|
label="Show vega chart controls"
|
||||||
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
register={register}
|
register={register}
|
||||||
name="showControls"
|
name="showControls"
|
||||||
|
@ -204,6 +204,36 @@ const ViewSettings: React.FC<{ register: UseFormRegister<FormFields> }> = ({
|
||||||
name="showSummary"
|
name="showSummary"
|
||||||
label="Show summary statistics"
|
label="Show summary statistics"
|
||||||
/>
|
/>
|
||||||
|
<InputItem
|
||||||
|
name="minX"
|
||||||
|
type="number"
|
||||||
|
register={register}
|
||||||
|
label="Min X Value"
|
||||||
|
/>
|
||||||
|
<InputItem
|
||||||
|
name="maxX"
|
||||||
|
type="number"
|
||||||
|
register={register}
|
||||||
|
label="Max X Value"
|
||||||
|
/>
|
||||||
|
<InputItem
|
||||||
|
name="title"
|
||||||
|
type="text"
|
||||||
|
register={register}
|
||||||
|
label="Title"
|
||||||
|
/>
|
||||||
|
<InputItem
|
||||||
|
name="tickFormat"
|
||||||
|
type="text"
|
||||||
|
register={register}
|
||||||
|
label="Tick Format"
|
||||||
|
/>
|
||||||
|
<InputItem
|
||||||
|
name="color"
|
||||||
|
type="color"
|
||||||
|
register={register}
|
||||||
|
label="Color"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</HeadedSection>
|
</HeadedSection>
|
||||||
</div>
|
</div>
|
||||||
|
@ -376,6 +406,12 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
|
||||||
showSummary = false,
|
showSummary = false,
|
||||||
logX = false,
|
logX = false,
|
||||||
expY = false,
|
expY = false,
|
||||||
|
title,
|
||||||
|
minX,
|
||||||
|
maxX,
|
||||||
|
color = "#739ECC",
|
||||||
|
tickFormat = ".9~s",
|
||||||
|
distributionChartActions,
|
||||||
code: controlledCode,
|
code: controlledCode,
|
||||||
onCodeChange,
|
onCodeChange,
|
||||||
onSettingsChange,
|
onSettingsChange,
|
||||||
|
@ -398,6 +434,12 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
|
||||||
showControls,
|
showControls,
|
||||||
logX,
|
logX,
|
||||||
expY,
|
expY,
|
||||||
|
title,
|
||||||
|
minX,
|
||||||
|
maxX,
|
||||||
|
color,
|
||||||
|
tickFormat,
|
||||||
|
distributionChartActions,
|
||||||
showSummary,
|
showSummary,
|
||||||
showEditor,
|
showEditor,
|
||||||
leftSizePercent: 50,
|
leftSizePercent: 50,
|
||||||
|
@ -430,14 +472,7 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
|
||||||
<SquiggleChart
|
<SquiggleChart
|
||||||
code={renderedCode}
|
code={renderedCode}
|
||||||
environment={env}
|
environment={env}
|
||||||
diagramStart={Number(vars.diagramStart)}
|
{...vars}
|
||||||
diagramStop={Number(vars.diagramStop)}
|
|
||||||
diagramCount={Number(vars.diagramCount)}
|
|
||||||
height={vars.chartHeight}
|
|
||||||
showControls={vars.showControls}
|
|
||||||
showSummary={vars.showSummary}
|
|
||||||
logX={vars.logX}
|
|
||||||
expY={vars.expY}
|
|
||||||
bindings={defaultBindings}
|
bindings={defaultBindings}
|
||||||
jsImports={imports}
|
jsImports={imports}
|
||||||
/>
|
/>
|
||||||
|
@ -477,7 +512,7 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
|
||||||
);
|
);
|
||||||
|
|
||||||
const withEditor = (
|
const withEditor = (
|
||||||
<div className="flex mt-1">
|
<div className="flex mt-2">
|
||||||
<div className="w-1/2">{tabs}</div>
|
<div className="w-1/2">{tabs}</div>
|
||||||
<div className="w-1/2 p-2 pl-4">{squiggleChart}</div>
|
<div className="w-1/2 p-2 pl-4">{squiggleChart}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -23,8 +23,8 @@ export const Toggle: React.FC<Props> = ({
|
||||||
layout
|
layout
|
||||||
transition={{ duration: 0.2 }}
|
transition={{ duration: 0.2 }}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
"rounded-full py-1 bg-indigo-500 text-white text-xs font-semibold flex items-center space-x-1",
|
"rounded-md py-0.5 bg-slate-500 text-white text-xs font-semibold flex items-center space-x-1",
|
||||||
status ? "bg-indigo-500" : "bg-gray-400",
|
status ? "bg-slate-500" : "bg-gray-400",
|
||||||
status ? "pl-1 pr-3" : "pl-3 pr-1",
|
status ? "pl-1 pr-3" : "pl-3 pr-1",
|
||||||
!status && "flex-row-reverse space-x-reverse"
|
!status && "flex-row-reverse space-x-reverse"
|
||||||
)}
|
)}
|
||||||
|
|
256
packages/components/src/lib/distributionSpecBuilder.ts
Normal file
256
packages/components/src/lib/distributionSpecBuilder.ts
Normal file
|
@ -0,0 +1,256 @@
|
||||||
|
import { VisualizationSpec } from "react-vega";
|
||||||
|
import type { LogScale, LinearScale, PowScale } from "vega";
|
||||||
|
|
||||||
|
export type DistributionChartSpecOptions = {
|
||||||
|
/** Set the x scale to be logarithmic by deault */
|
||||||
|
logX: boolean;
|
||||||
|
/** Set the y scale to be exponential by deault */
|
||||||
|
expY: boolean;
|
||||||
|
/** The minimum x coordinate shown on the chart */
|
||||||
|
minX?: number;
|
||||||
|
/** The maximum x coordinate shown on the chart */
|
||||||
|
maxX?: number;
|
||||||
|
/** The color of the chart */
|
||||||
|
color?: string;
|
||||||
|
/** The title of the chart */
|
||||||
|
title?: string;
|
||||||
|
/** The formatting of the ticks */
|
||||||
|
format?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export let linearXScale: LinearScale = {
|
||||||
|
name: "xscale",
|
||||||
|
clamp: true,
|
||||||
|
type: "linear",
|
||||||
|
range: "width",
|
||||||
|
zero: false,
|
||||||
|
nice: false,
|
||||||
|
domain: {
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
data: "con",
|
||||||
|
field: "x",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "dis",
|
||||||
|
field: "x",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
export let linearYScale: LinearScale = {
|
||||||
|
name: "yscale",
|
||||||
|
type: "linear",
|
||||||
|
range: "height",
|
||||||
|
zero: true,
|
||||||
|
domain: {
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
data: "con",
|
||||||
|
field: "y",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "dis",
|
||||||
|
field: "y",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export let logXScale: LogScale = {
|
||||||
|
name: "xscale",
|
||||||
|
type: "log",
|
||||||
|
range: "width",
|
||||||
|
zero: false,
|
||||||
|
base: 10,
|
||||||
|
nice: false,
|
||||||
|
clamp: true,
|
||||||
|
domain: {
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
data: "con",
|
||||||
|
field: "x",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "dis",
|
||||||
|
field: "x",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export let expYScale: PowScale = {
|
||||||
|
name: "yscale",
|
||||||
|
type: "pow",
|
||||||
|
exponent: 0.1,
|
||||||
|
range: "height",
|
||||||
|
zero: true,
|
||||||
|
nice: false,
|
||||||
|
domain: {
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
data: "con",
|
||||||
|
field: "y",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "dis",
|
||||||
|
field: "y",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export function buildVegaSpec(
|
||||||
|
specOptions: DistributionChartSpecOptions
|
||||||
|
): VisualizationSpec {
|
||||||
|
let {
|
||||||
|
format = ".9~s",
|
||||||
|
color = "#739ECC",
|
||||||
|
title,
|
||||||
|
minX,
|
||||||
|
maxX,
|
||||||
|
logX,
|
||||||
|
expY,
|
||||||
|
} = specOptions;
|
||||||
|
|
||||||
|
let xScale = logX ? logXScale : linearXScale;
|
||||||
|
if (minX !== undefined && Number.isFinite(minX)) {
|
||||||
|
xScale = { ...xScale, domainMin: minX };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxX !== undefined && Number.isFinite(maxX)) {
|
||||||
|
xScale = { ...xScale, domainMax: maxX };
|
||||||
|
}
|
||||||
|
|
||||||
|
let spec: VisualizationSpec = {
|
||||||
|
$schema: "https://vega.github.io/schema/vega/v5.json",
|
||||||
|
description: "A basic area chart example",
|
||||||
|
width: 500,
|
||||||
|
height: 100,
|
||||||
|
padding: 5,
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
name: "con",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "dis",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
signals: [],
|
||||||
|
scales: [xScale, expY ? expYScale : linearYScale],
|
||||||
|
axes: [
|
||||||
|
{
|
||||||
|
orient: "bottom",
|
||||||
|
scale: "xscale",
|
||||||
|
labelColor: "#727d93",
|
||||||
|
tickColor: "#fff",
|
||||||
|
tickOpacity: 0.0,
|
||||||
|
domainColor: "#fff",
|
||||||
|
domainOpacity: 0.0,
|
||||||
|
format: format,
|
||||||
|
tickCount: 10,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
marks: [
|
||||||
|
{
|
||||||
|
type: "area",
|
||||||
|
from: {
|
||||||
|
data: "con",
|
||||||
|
},
|
||||||
|
encode: {
|
||||||
|
update: {
|
||||||
|
interpolate: { value: "linear" },
|
||||||
|
x: {
|
||||||
|
scale: "xscale",
|
||||||
|
field: "x",
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
scale: "yscale",
|
||||||
|
field: "y",
|
||||||
|
},
|
||||||
|
y2: {
|
||||||
|
scale: "yscale",
|
||||||
|
value: 0,
|
||||||
|
},
|
||||||
|
fill: {
|
||||||
|
value: color,
|
||||||
|
},
|
||||||
|
fillOpacity: {
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "rect",
|
||||||
|
from: {
|
||||||
|
data: "dis",
|
||||||
|
},
|
||||||
|
encode: {
|
||||||
|
enter: {
|
||||||
|
width: {
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
x: {
|
||||||
|
scale: "xscale",
|
||||||
|
field: "x",
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
scale: "yscale",
|
||||||
|
field: "y",
|
||||||
|
},
|
||||||
|
y2: {
|
||||||
|
scale: "yscale",
|
||||||
|
value: 0,
|
||||||
|
},
|
||||||
|
fill: {
|
||||||
|
value: "#2f65a7",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "symbol",
|
||||||
|
from: {
|
||||||
|
data: "dis",
|
||||||
|
},
|
||||||
|
encode: {
|
||||||
|
enter: {
|
||||||
|
shape: {
|
||||||
|
value: "circle",
|
||||||
|
},
|
||||||
|
size: [{ value: 100 }],
|
||||||
|
tooltip: {
|
||||||
|
signal: "{ probability: datum.y, value: datum.x }",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
x: {
|
||||||
|
scale: "xscale",
|
||||||
|
field: "x",
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
scale: "yscale",
|
||||||
|
field: "y",
|
||||||
|
},
|
||||||
|
fill: {
|
||||||
|
value: "#1e4577",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
if (title) {
|
||||||
|
spec = {
|
||||||
|
...spec,
|
||||||
|
title: {
|
||||||
|
text: title,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return spec;
|
||||||
|
}
|
|
@ -3,7 +3,7 @@ import { Canvas, Meta, Story, Props } from "@storybook/addon-docs";
|
||||||
|
|
||||||
<Meta title="Squiggle/SquiggleChart" component={SquiggleChart} />
|
<Meta title="Squiggle/SquiggleChart" component={SquiggleChart} />
|
||||||
|
|
||||||
export const Template = SquiggleChart;
|
export const Template = (props) => <SquiggleChart {...props} />;
|
||||||
/*
|
/*
|
||||||
We have to hardcode a width here, because otherwise some interaction with
|
We have to hardcode a width here, because otherwise some interaction with
|
||||||
Storybook creates an infinite loop with the internal width
|
Storybook creates an infinite loop with the internal width
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
open Jest
|
open Jest
|
||||||
open Expect
|
open Expect
|
||||||
|
|
||||||
let env: DistributionOperation.env = {
|
let env: GenericDist.env = {
|
||||||
sampleCount: 100,
|
sampleCount: 100,
|
||||||
xyPointLength: 100,
|
xyPointLength: 100,
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ describe("sparkline", () => {
|
||||||
expected: DistributionOperation.outputType,
|
expected: DistributionOperation.outputType,
|
||||||
) => {
|
) => {
|
||||||
test(name, () => {
|
test(name, () => {
|
||||||
let result = DistributionOperation.run(~env, FromDist(ToString(ToSparkline(20)), dist))
|
let result = DistributionOperation.run(~env, FromDist(#ToString(ToSparkline(20)), dist))
|
||||||
expect(result)->toEqual(expected)
|
expect(result)->toEqual(expected)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -81,8 +81,8 @@ describe("sparkline", () => {
|
||||||
describe("toPointSet", () => {
|
describe("toPointSet", () => {
|
||||||
test("on symbolic normal distribution", () => {
|
test("on symbolic normal distribution", () => {
|
||||||
let result =
|
let result =
|
||||||
run(FromDist(ToDist(ToPointSet), normalDist5))
|
run(FromDist(#ToDist(ToPointSet), normalDist5))
|
||||||
->outputMap(FromDist(ToFloat(#Mean)))
|
->outputMap(FromDist(#ToFloat(#Mean)))
|
||||||
->toFloat
|
->toFloat
|
||||||
->toExt
|
->toExt
|
||||||
expect(result)->toBeSoCloseTo(5.0, ~digits=0)
|
expect(result)->toBeSoCloseTo(5.0, ~digits=0)
|
||||||
|
@ -90,10 +90,10 @@ describe("toPointSet", () => {
|
||||||
|
|
||||||
test("on sample set", () => {
|
test("on sample set", () => {
|
||||||
let result =
|
let result =
|
||||||
run(FromDist(ToDist(ToPointSet), normalDist5))
|
run(FromDist(#ToDist(ToPointSet), normalDist5))
|
||||||
->outputMap(FromDist(ToDist(ToSampleSet(1000))))
|
->outputMap(FromDist(#ToDist(ToSampleSet(1000))))
|
||||||
->outputMap(FromDist(ToDist(ToPointSet)))
|
->outputMap(FromDist(#ToDist(ToPointSet)))
|
||||||
->outputMap(FromDist(ToFloat(#Mean)))
|
->outputMap(FromDist(#ToFloat(#Mean)))
|
||||||
->toFloat
|
->toFloat
|
||||||
->toExt
|
->toExt
|
||||||
expect(result)->toBeSoCloseTo(5.0, ~digits=-1)
|
expect(result)->toBeSoCloseTo(5.0, ~digits=-1)
|
||||||
|
|
|
@ -19,7 +19,6 @@ exception MixtureFailed
|
||||||
let float1 = 1.0
|
let float1 = 1.0
|
||||||
let float2 = 2.0
|
let float2 = 2.0
|
||||||
let float3 = 3.0
|
let float3 = 3.0
|
||||||
let {mkDelta} = module(TestHelpers)
|
let point1 = TestHelpers.mkDelta(float1)
|
||||||
let point1 = mkDelta(float1)
|
let point2 = TestHelpers.mkDelta(float2)
|
||||||
let point2 = mkDelta(float2)
|
let point3 = TestHelpers.mkDelta(float3)
|
||||||
let point3 = mkDelta(float3)
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ describe("mixture", () => {
|
||||||
let (mean1, mean2) = tup
|
let (mean1, mean2) = tup
|
||||||
let meanValue = {
|
let meanValue = {
|
||||||
run(Mixture([(mkNormal(mean1, 9e-1), 0.5), (mkNormal(mean2, 9e-1), 0.5)]))->outputMap(
|
run(Mixture([(mkNormal(mean1, 9e-1), 0.5), (mkNormal(mean2, 9e-1), 0.5)]))->outputMap(
|
||||||
FromDist(ToFloat(#Mean)),
|
FromDist(#ToFloat(#Mean)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
meanValue->unpackFloat->expect->toBeSoCloseTo((mean1 +. mean2) /. 2.0, ~digits=-1)
|
meanValue->unpackFloat->expect->toBeSoCloseTo((mean1 +. mean2) /. 2.0, ~digits=-1)
|
||||||
|
@ -28,7 +28,7 @@ describe("mixture", () => {
|
||||||
let meanValue = {
|
let meanValue = {
|
||||||
run(
|
run(
|
||||||
Mixture([(mkBeta(alpha, beta), betaWeight), (mkExponential(rate), exponentialWeight)]),
|
Mixture([(mkBeta(alpha, beta), betaWeight), (mkExponential(rate), exponentialWeight)]),
|
||||||
)->outputMap(FromDist(ToFloat(#Mean)))
|
)->outputMap(FromDist(#ToFloat(#Mean)))
|
||||||
}
|
}
|
||||||
let betaMean = 1.0 /. (1.0 +. beta /. alpha)
|
let betaMean = 1.0 /. (1.0 +. beta /. alpha)
|
||||||
let exponentialMean = 1.0 /. rate
|
let exponentialMean = 1.0 /. rate
|
||||||
|
@ -52,7 +52,7 @@ describe("mixture", () => {
|
||||||
(mkUniform(low, high), uniformWeight),
|
(mkUniform(low, high), uniformWeight),
|
||||||
(mkLognormal(mu, sigma), lognormalWeight),
|
(mkLognormal(mu, sigma), lognormalWeight),
|
||||||
]),
|
]),
|
||||||
)->outputMap(FromDist(ToFloat(#Mean)))
|
)->outputMap(FromDist(#ToFloat(#Mean)))
|
||||||
}
|
}
|
||||||
let uniformMean = (low +. high) /. 2.0
|
let uniformMean = (low +. high) /. 2.0
|
||||||
let lognormalMean = mu +. sigma ** 2.0 /. 2.0
|
let lognormalMean = mu +. sigma ** 2.0 /. 2.0
|
||||||
|
|
|
@ -3,6 +3,7 @@ open Expect
|
||||||
open TestHelpers
|
open TestHelpers
|
||||||
open GenericDist_Fixtures
|
open GenericDist_Fixtures
|
||||||
|
|
||||||
|
let klDivergence = DistributionOperation.Constructors.LogScore.distEstimateDistAnswer(~env)
|
||||||
// integral from low to high of 1 / (high - low) log(normal(mean, stdev)(x) / (1 / (high - low))) dx
|
// integral from low to high of 1 / (high - low) log(normal(mean, stdev)(x) / (1 / (high - low))) dx
|
||||||
let klNormalUniform = (mean, stdev, low, high): float =>
|
let klNormalUniform = (mean, stdev, low, high): float =>
|
||||||
-.Js.Math.log((high -. low) /. Js.Math.sqrt(2.0 *. MagicNumbers.Math.pi *. stdev ** 2.0)) +.
|
-.Js.Math.log((high -. low) /. Js.Math.sqrt(2.0 *. MagicNumbers.Math.pi *. stdev ** 2.0)) +.
|
||||||
|
@ -11,8 +12,6 @@ let klNormalUniform = (mean, stdev, low, high): float =>
|
||||||
(mean ** 2.0 -. (high +. low) *. mean +. (low ** 2.0 +. high *. low +. high ** 2.0) /. 3.0)
|
(mean ** 2.0 -. (high +. low) *. mean +. (low ** 2.0 +. high *. low +. high ** 2.0) /. 3.0)
|
||||||
|
|
||||||
describe("klDivergence: continuous -> continuous -> float", () => {
|
describe("klDivergence: continuous -> continuous -> float", () => {
|
||||||
let klDivergence = DistributionOperation.Constructors.klDivergence(~env)
|
|
||||||
|
|
||||||
let testUniform = (lowAnswer, highAnswer, lowPrediction, highPrediction) => {
|
let testUniform = (lowAnswer, highAnswer, lowPrediction, highPrediction) => {
|
||||||
test("of two uniforms is equal to the analytic expression", () => {
|
test("of two uniforms is equal to the analytic expression", () => {
|
||||||
let answer =
|
let answer =
|
||||||
|
@ -58,7 +57,7 @@ describe("klDivergence: continuous -> continuous -> float", () => {
|
||||||
let kl = E.R.liftJoin2(klDivergence, prediction, answer)
|
let kl = E.R.liftJoin2(klDivergence, prediction, answer)
|
||||||
|
|
||||||
switch kl {
|
switch kl {
|
||||||
| Ok(kl') => kl'->expect->toBeSoCloseTo(analyticalKl, ~digits=3)
|
| Ok(kl') => kl'->expect->toBeSoCloseTo(analyticalKl, ~digits=2)
|
||||||
| Error(err) => {
|
| Error(err) => {
|
||||||
Js.Console.log(DistributionTypes.Error.toString(err))
|
Js.Console.log(DistributionTypes.Error.toString(err))
|
||||||
raise(KlFailed)
|
raise(KlFailed)
|
||||||
|
@ -82,7 +81,6 @@ describe("klDivergence: continuous -> continuous -> float", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("klDivergence: discrete -> discrete -> float", () => {
|
describe("klDivergence: discrete -> discrete -> float", () => {
|
||||||
let klDivergence = DistributionOperation.Constructors.klDivergence(~env)
|
|
||||||
let mixture = a => DistributionTypes.DistributionOperation.Mixture(a)
|
let mixture = a => DistributionTypes.DistributionOperation.Mixture(a)
|
||||||
let a' = [(point1, 1e0), (point2, 1e0)]->mixture->run
|
let a' = [(point1, 1e0), (point2, 1e0)]->mixture->run
|
||||||
let b' = [(point1, 1e0), (point2, 1e0), (point3, 1e0)]->mixture->run
|
let b' = [(point1, 1e0), (point2, 1e0), (point3, 1e0)]->mixture->run
|
||||||
|
@ -117,7 +115,6 @@ describe("klDivergence: discrete -> discrete -> float", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("klDivergence: mixed -> mixed -> float", () => {
|
describe("klDivergence: mixed -> mixed -> float", () => {
|
||||||
let klDivergence = DistributionOperation.Constructors.klDivergence(~env)
|
|
||||||
let mixture' = a => DistributionTypes.DistributionOperation.Mixture(a)
|
let mixture' = a => DistributionTypes.DistributionOperation.Mixture(a)
|
||||||
let mixture = a => {
|
let mixture = a => {
|
||||||
let dist' = a->mixture'->run
|
let dist' = a->mixture'->run
|
||||||
|
@ -189,15 +186,15 @@ describe("combineAlongSupportOfSecondArgument0", () => {
|
||||||
uniformMakeR(lowPrediction, highPrediction)->E.R2.errMap(s => DistributionTypes.ArgumentError(
|
uniformMakeR(lowPrediction, highPrediction)->E.R2.errMap(s => DistributionTypes.ArgumentError(
|
||||||
s,
|
s,
|
||||||
))
|
))
|
||||||
let answerWrapped = E.R.fmap(a => run(FromDist(ToDist(ToPointSet), a)), answer)
|
let answerWrapped = E.R.fmap(a => run(FromDist(#ToDist(ToPointSet), a)), answer)
|
||||||
let predictionWrapped = E.R.fmap(a => run(FromDist(ToDist(ToPointSet), a)), prediction)
|
let predictionWrapped = E.R.fmap(a => run(FromDist(#ToDist(ToPointSet), a)), prediction)
|
||||||
|
|
||||||
let interpolator = XYShape.XtoY.continuousInterpolator(#Stepwise, #UseZero)
|
let interpolator = XYShape.XtoY.continuousInterpolator(#Stepwise, #UseZero)
|
||||||
let integrand = PointSetDist_Scoring.KLDivergence.integrand
|
let integrand = PointSetDist_Scoring.WithDistAnswer.integrand
|
||||||
|
|
||||||
let result = switch (answerWrapped, predictionWrapped) {
|
let result = switch (answerWrapped, predictionWrapped) {
|
||||||
| (Ok(Dist(PointSet(Continuous(a)))), Ok(Dist(PointSet(Continuous(b))))) =>
|
| (Ok(Dist(PointSet(Continuous(a)))), Ok(Dist(PointSet(Continuous(b))))) =>
|
||||||
Some(combineAlongSupportOfSecondArgument(integrand, interpolator, a.xyShape, b.xyShape))
|
Some(combineAlongSupportOfSecondArgument(interpolator, integrand, a.xyShape, b.xyShape))
|
||||||
| _ => None
|
| _ => None
|
||||||
}
|
}
|
||||||
result
|
result
|
|
@ -0,0 +1,68 @@
|
||||||
|
open Jest
|
||||||
|
open Expect
|
||||||
|
open TestHelpers
|
||||||
|
open GenericDist_Fixtures
|
||||||
|
exception ScoreFailed
|
||||||
|
|
||||||
|
describe("WithScalarAnswer: discrete -> scalar -> score", () => {
|
||||||
|
let mixture = a => DistributionTypes.DistributionOperation.Mixture(a)
|
||||||
|
let pointA = mkDelta(3.0)
|
||||||
|
let pointB = mkDelta(2.0)
|
||||||
|
let pointC = mkDelta(1.0)
|
||||||
|
let pointD = mkDelta(0.0)
|
||||||
|
|
||||||
|
test("score: agrees with analytical answer when finite", () => {
|
||||||
|
let prediction' = [(pointA, 0.25), (pointB, 0.25), (pointC, 0.25), (pointD, 0.25)]->mixture->run
|
||||||
|
let prediction = switch prediction' {
|
||||||
|
| Dist(PointSet(p)) => p
|
||||||
|
| _ => raise(MixtureFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
let answer = 2.0 // So this is: assigning 100% probability to 2.0
|
||||||
|
let result = PointSetDist_Scoring.WithScalarAnswer.score(~estimate=prediction, ~answer)
|
||||||
|
switch result {
|
||||||
|
| Ok(x) => x->expect->toEqual(-.Js.Math.log(0.25 /. 1.0))
|
||||||
|
| _ => raise(ScoreFailed)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test("score: agrees with analytical answer when finite", () => {
|
||||||
|
let prediction' = [(pointA, 0.75), (pointB, 0.25)]->mixture->run
|
||||||
|
let prediction = switch prediction' {
|
||||||
|
| Dist(PointSet(p)) => p
|
||||||
|
| _ => raise(MixtureFailed)
|
||||||
|
}
|
||||||
|
let answer = 3.0 // So this is: assigning 100% probability to 2.0
|
||||||
|
let result = PointSetDist_Scoring.WithScalarAnswer.score(~estimate=prediction, ~answer)
|
||||||
|
switch result {
|
||||||
|
| Ok(x) => x->expect->toEqual(-.Js.Math.log(0.75 /. 1.0))
|
||||||
|
| _ => raise(ScoreFailed)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test("scoreWithPrior: agrees with analytical answer when finite", () => {
|
||||||
|
let prior' = [(pointA, 0.5), (pointB, 0.5)]->mixture->run
|
||||||
|
let prediction' = [(pointA, 0.75), (pointB, 0.25)]->mixture->run
|
||||||
|
|
||||||
|
let prediction = switch prediction' {
|
||||||
|
| Dist(PointSet(p)) => p
|
||||||
|
| _ => raise(MixtureFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
let prior = switch prior' {
|
||||||
|
| Dist(PointSet(p)) => p
|
||||||
|
| _ => raise(MixtureFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
let answer = 3.0 // So this is: assigning 100% probability to 2.0
|
||||||
|
let result = PointSetDist_Scoring.WithScalarAnswer.scoreWithPrior(
|
||||||
|
~estimate=prediction,
|
||||||
|
~answer,
|
||||||
|
~prior,
|
||||||
|
)
|
||||||
|
switch result {
|
||||||
|
| Ok(x) => x->expect->toEqual(-.Js.Math.log(0.75 /. 1.0) -. -.Js.Math.log(0.5 /. 1.0))
|
||||||
|
| _ => raise(ScoreFailed)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
|
@ -8,34 +8,34 @@ let mkNormal = (mean, stdev) => DistributionTypes.Symbolic(#Normal({mean: mean,
|
||||||
describe("(Symbolic) normalize", () => {
|
describe("(Symbolic) normalize", () => {
|
||||||
testAll("has no impact on normal distributions", list{-1e8, -1e-2, 0.0, 1e-4, 1e16}, mean => {
|
testAll("has no impact on normal distributions", list{-1e8, -1e-2, 0.0, 1e-4, 1e16}, mean => {
|
||||||
let normalValue = mkNormal(mean, 2.0)
|
let normalValue = mkNormal(mean, 2.0)
|
||||||
let normalizedValue = run(FromDist(ToDist(Normalize), normalValue))
|
let normalizedValue = run(FromDist(#ToDist(Normalize), normalValue))
|
||||||
normalizedValue->unpackDist->expect->toEqual(normalValue)
|
normalizedValue->unpackDist->expect->toEqual(normalValue)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("(Symbolic) mean", () => {
|
describe("(Symbolic) mean", () => {
|
||||||
testAll("of normal distributions", list{-1e8, -16.0, -1e-2, 0.0, 1e-4, 32.0, 1e16}, mean => {
|
testAll("of normal distributions", list{-1e8, -16.0, -1e-2, 0.0, 1e-4, 32.0, 1e16}, mean => {
|
||||||
run(FromDist(ToFloat(#Mean), mkNormal(mean, 4.0)))->unpackFloat->expect->toBeCloseTo(mean)
|
run(FromDist(#ToFloat(#Mean), mkNormal(mean, 4.0)))->unpackFloat->expect->toBeCloseTo(mean)
|
||||||
})
|
})
|
||||||
|
|
||||||
Skip.test("of normal(0, -1) (it NaNs out)", () => {
|
Skip.test("of normal(0, -1) (it NaNs out)", () => {
|
||||||
run(FromDist(ToFloat(#Mean), mkNormal(1e1, -1e0)))->unpackFloat->expect->ExpectJs.toBeFalsy
|
run(FromDist(#ToFloat(#Mean), mkNormal(1e1, -1e0)))->unpackFloat->expect->ExpectJs.toBeFalsy
|
||||||
})
|
})
|
||||||
|
|
||||||
test("of normal(0, 1e-8) (it doesn't freak out at tiny stdev)", () => {
|
test("of normal(0, 1e-8) (it doesn't freak out at tiny stdev)", () => {
|
||||||
run(FromDist(ToFloat(#Mean), mkNormal(0.0, 1e-8)))->unpackFloat->expect->toBeCloseTo(0.0)
|
run(FromDist(#ToFloat(#Mean), mkNormal(0.0, 1e-8)))->unpackFloat->expect->toBeCloseTo(0.0)
|
||||||
})
|
})
|
||||||
|
|
||||||
testAll("of exponential distributions", list{1e-7, 2.0, 10.0, 100.0}, rate => {
|
testAll("of exponential distributions", list{1e-7, 2.0, 10.0, 100.0}, rate => {
|
||||||
let meanValue = run(
|
let meanValue = run(
|
||||||
FromDist(ToFloat(#Mean), DistributionTypes.Symbolic(#Exponential({rate: rate}))),
|
FromDist(#ToFloat(#Mean), DistributionTypes.Symbolic(#Exponential({rate: rate}))),
|
||||||
)
|
)
|
||||||
meanValue->unpackFloat->expect->toBeCloseTo(1.0 /. rate) // https://en.wikipedia.org/wiki/Exponential_distribution#Mean,_variance,_moments,_and_median
|
meanValue->unpackFloat->expect->toBeCloseTo(1.0 /. rate) // https://en.wikipedia.org/wiki/Exponential_distribution#Mean,_variance,_moments,_and_median
|
||||||
})
|
})
|
||||||
|
|
||||||
test("of a cauchy distribution", () => {
|
test("of a cauchy distribution", () => {
|
||||||
let meanValue = run(
|
let meanValue = run(
|
||||||
FromDist(ToFloat(#Mean), DistributionTypes.Symbolic(#Cauchy({local: 1.0, scale: 1.0}))),
|
FromDist(#ToFloat(#Mean), DistributionTypes.Symbolic(#Cauchy({local: 1.0, scale: 1.0}))),
|
||||||
)
|
)
|
||||||
meanValue->unpackFloat->expect->toBeSoCloseTo(1.0098094001641797, ~digits=5)
|
meanValue->unpackFloat->expect->toBeSoCloseTo(1.0098094001641797, ~digits=5)
|
||||||
//-> toBe(GenDistError(Other("Cauchy distributions may have no mean value.")))
|
//-> toBe(GenDistError(Other("Cauchy distributions may have no mean value.")))
|
||||||
|
@ -48,7 +48,7 @@ describe("(Symbolic) mean", () => {
|
||||||
let (low, medium, high) = tup
|
let (low, medium, high) = tup
|
||||||
let meanValue = run(
|
let meanValue = run(
|
||||||
FromDist(
|
FromDist(
|
||||||
ToFloat(#Mean),
|
#ToFloat(#Mean),
|
||||||
DistributionTypes.Symbolic(#Triangular({low: low, medium: medium, high: high})),
|
DistributionTypes.Symbolic(#Triangular({low: low, medium: medium, high: high})),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -63,7 +63,7 @@ describe("(Symbolic) mean", () => {
|
||||||
tup => {
|
tup => {
|
||||||
let (alpha, beta) = tup
|
let (alpha, beta) = tup
|
||||||
let meanValue = run(
|
let meanValue = run(
|
||||||
FromDist(ToFloat(#Mean), DistributionTypes.Symbolic(#Beta({alpha: alpha, beta: beta}))),
|
FromDist(#ToFloat(#Mean), DistributionTypes.Symbolic(#Beta({alpha: alpha, beta: beta}))),
|
||||||
)
|
)
|
||||||
meanValue->unpackFloat->expect->toBeCloseTo(1.0 /. (1.0 +. beta /. alpha)) // https://en.wikipedia.org/wiki/Beta_distribution#Mean
|
meanValue->unpackFloat->expect->toBeCloseTo(1.0 /. (1.0 +. beta /. alpha)) // https://en.wikipedia.org/wiki/Beta_distribution#Mean
|
||||||
},
|
},
|
||||||
|
@ -72,7 +72,7 @@ describe("(Symbolic) mean", () => {
|
||||||
// TODO: When we have our theory of validators we won't want this to be NaN but to be an error.
|
// TODO: When we have our theory of validators we won't want this to be NaN but to be an error.
|
||||||
test("of beta(0, 0)", () => {
|
test("of beta(0, 0)", () => {
|
||||||
let meanValue = run(
|
let meanValue = run(
|
||||||
FromDist(ToFloat(#Mean), DistributionTypes.Symbolic(#Beta({alpha: 0.0, beta: 0.0}))),
|
FromDist(#ToFloat(#Mean), DistributionTypes.Symbolic(#Beta({alpha: 0.0, beta: 0.0}))),
|
||||||
)
|
)
|
||||||
meanValue->unpackFloat->expect->ExpectJs.toBeFalsy
|
meanValue->unpackFloat->expect->ExpectJs.toBeFalsy
|
||||||
})
|
})
|
||||||
|
@ -85,7 +85,7 @@ describe("(Symbolic) mean", () => {
|
||||||
let betaDistribution = SymbolicDist.Beta.fromMeanAndStdev(mean, stdev)
|
let betaDistribution = SymbolicDist.Beta.fromMeanAndStdev(mean, stdev)
|
||||||
let meanValue =
|
let meanValue =
|
||||||
betaDistribution->E.R2.fmap(d =>
|
betaDistribution->E.R2.fmap(d =>
|
||||||
run(FromDist(ToFloat(#Mean), d->DistributionTypes.Symbolic))
|
run(FromDist(#ToFloat(#Mean), d->DistributionTypes.Symbolic))
|
||||||
)
|
)
|
||||||
switch meanValue {
|
switch meanValue {
|
||||||
| Ok(value) => value->unpackFloat->expect->toBeCloseTo(mean)
|
| Ok(value) => value->unpackFloat->expect->toBeCloseTo(mean)
|
||||||
|
@ -100,7 +100,7 @@ describe("(Symbolic) mean", () => {
|
||||||
tup => {
|
tup => {
|
||||||
let (mu, sigma) = tup
|
let (mu, sigma) = tup
|
||||||
let meanValue = run(
|
let meanValue = run(
|
||||||
FromDist(ToFloat(#Mean), DistributionTypes.Symbolic(#Lognormal({mu: mu, sigma: sigma}))),
|
FromDist(#ToFloat(#Mean), DistributionTypes.Symbolic(#Lognormal({mu: mu, sigma: sigma}))),
|
||||||
)
|
)
|
||||||
meanValue->unpackFloat->expect->toBeCloseTo(Js.Math.exp(mu +. sigma ** 2.0 /. 2.0)) // https://brilliant.org/wiki/log-normal-distribution/
|
meanValue->unpackFloat->expect->toBeCloseTo(Js.Math.exp(mu +. sigma ** 2.0 /. 2.0)) // https://brilliant.org/wiki/log-normal-distribution/
|
||||||
},
|
},
|
||||||
|
@ -112,14 +112,14 @@ describe("(Symbolic) mean", () => {
|
||||||
tup => {
|
tup => {
|
||||||
let (low, high) = tup
|
let (low, high) = tup
|
||||||
let meanValue = run(
|
let meanValue = run(
|
||||||
FromDist(ToFloat(#Mean), DistributionTypes.Symbolic(#Uniform({low: low, high: high}))),
|
FromDist(#ToFloat(#Mean), DistributionTypes.Symbolic(#Uniform({low: low, high: high}))),
|
||||||
)
|
)
|
||||||
meanValue->unpackFloat->expect->toBeCloseTo((low +. high) /. 2.0) // https://en.wikipedia.org/wiki/Continuous_uniform_distribution#Moments
|
meanValue->unpackFloat->expect->toBeCloseTo((low +. high) /. 2.0) // https://en.wikipedia.org/wiki/Continuous_uniform_distribution#Moments
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
test("of a float", () => {
|
test("of a float", () => {
|
||||||
let meanValue = run(FromDist(ToFloat(#Mean), DistributionTypes.Symbolic(#Float(7.7))))
|
let meanValue = run(FromDist(#ToFloat(#Mean), DistributionTypes.Symbolic(#Float(7.7))))
|
||||||
meanValue->unpackFloat->expect->toBeCloseTo(7.7)
|
meanValue->unpackFloat->expect->toBeCloseTo(7.7)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -21,6 +21,10 @@ describe("builtin", () => {
|
||||||
"addOne(t)=t+1; toList(mapSamples(fromSamples([1,2,3,4,5,6]), addOne))",
|
"addOne(t)=t+1; toList(mapSamples(fromSamples([1,2,3,4,5,6]), addOne))",
|
||||||
"Ok([2,3,4,5,6,7])",
|
"Ok([2,3,4,5,6,7])",
|
||||||
)
|
)
|
||||||
|
testEval(
|
||||||
|
"toList(mapSamplesN([fromSamples([1,2,3,4,5,6]), fromSamples([6, 5, 4, 3, 2, 1])], {|x| x[0] > x[1] ? x[0] : x[1]}))",
|
||||||
|
"Ok([6,5,4,4,5,6])",
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("builtin exception", () => {
|
describe("builtin exception", () => {
|
||||||
|
|
|
@ -2,15 +2,15 @@
|
||||||
module ErrorValue = Reducer_ErrorValue
|
module ErrorValue = Reducer_ErrorValue
|
||||||
module ExternalExpressionValue = ReducerInterface.ExternalExpressionValue
|
module ExternalExpressionValue = ReducerInterface.ExternalExpressionValue
|
||||||
module InternalExpressionValue = ReducerInterface.InternalExpressionValue
|
module InternalExpressionValue = ReducerInterface.InternalExpressionValue
|
||||||
module Module = Reducer_Module
|
module Bindings = Reducer_Bindings
|
||||||
|
|
||||||
let removeDefaultsInternal = (iev: InternalExpressionValue.t) => {
|
let removeDefaultsInternal = (iev: InternalExpressionValue.t) => {
|
||||||
switch iev {
|
switch iev {
|
||||||
| InternalExpressionValue.IEvModule(nameSpace) =>
|
| InternalExpressionValue.IEvBindings(nameSpace) =>
|
||||||
Module.removeOther(
|
Bindings.removeOther(
|
||||||
nameSpace,
|
nameSpace,
|
||||||
ReducerInterface.StdLib.internalStdLib,
|
ReducerInterface.StdLib.internalStdLib,
|
||||||
)->InternalExpressionValue.IEvModule
|
)->InternalExpressionValue.IEvBindings
|
||||||
| value => value
|
| value => value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,100 +0,0 @@
|
||||||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
|
||||||
module ExpressionT = Reducer_Expression_T
|
|
||||||
module Module = Reducer_Module
|
|
||||||
module Bindings = Reducer_Module
|
|
||||||
module ErrorValue = Reducer_ErrorValue
|
|
||||||
|
|
||||||
open Jest
|
|
||||||
open Expect
|
|
||||||
|
|
||||||
// ----------------------
|
|
||||||
// --- Start of Module File
|
|
||||||
// ----------------------
|
|
||||||
|
|
||||||
module FooImplementation = {
|
|
||||||
// As this is a Rescript module, functions can use other functions in this module
|
|
||||||
// and in other stdLib modules implemented this way.
|
|
||||||
// Embedding function definitions in to switch statements is a bad practice
|
|
||||||
// - to reduce line count or to
|
|
||||||
let fooNumber = 0.0
|
|
||||||
let fooString = "Foo String"
|
|
||||||
let fooBool = true
|
|
||||||
let makeFoo = (a: string, b: string, _environment): string => `I am ${a}-foo and I am ${b}-foo`
|
|
||||||
let makeBar = (a: float, b: float, _environment): string =>
|
|
||||||
`I am ${a->Js.Float.toString}-bar and I am ${b->Js.Float.toString}-bar`
|
|
||||||
}
|
|
||||||
|
|
||||||
// There is a potential for type modules to define lift functions
|
|
||||||
// for their own type to get rid of switch statements.
|
|
||||||
module FooFFI = {
|
|
||||||
let makeFoo: ExpressionT.optionFfiFn = (args: array<InternalExpressionValue.t>, environment) => {
|
|
||||||
switch args {
|
|
||||||
| [IEvString(a), IEvString(b)] => FooImplementation.makeFoo(a, b, environment)->IEvString->Some
|
|
||||||
| _ => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let makeBar: ExpressionT.optionFfiFn = (args: array<InternalExpressionValue.t>, environment) =>
|
|
||||||
switch args {
|
|
||||||
| [IEvNumber(a), IEvNumber(b)] => FooImplementation.makeBar(a, b, environment)->IEvString->Some
|
|
||||||
| _ => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let fooModule: Module.t =
|
|
||||||
Module.emptyStdLib
|
|
||||||
->Module.defineNumber("fooNumber", FooImplementation.fooNumber)
|
|
||||||
->Module.defineString("fooString", FooImplementation.fooString)
|
|
||||||
->Module.defineBool("fooBool", FooImplementation.fooBool)
|
|
||||||
->Module.defineFunction("makeFoo", FooFFI.makeFoo)
|
|
||||||
->Module.defineFunction("makeBar", FooFFI.makeBar)
|
|
||||||
|
|
||||||
let makeBindings = (prevBindings: Bindings.t): Bindings.t =>
|
|
||||||
prevBindings->Module.defineModule("Foo", fooModule)
|
|
||||||
|
|
||||||
// ----------------------
|
|
||||||
// --- End of Module File
|
|
||||||
// ----------------------
|
|
||||||
|
|
||||||
let stdLibWithFoo = Bindings.emptyBindings->makeBindings
|
|
||||||
let evalWithFoo = sourceCode =>
|
|
||||||
Reducer_Expression.parse(sourceCode)->Belt.Result.flatMap(expr =>
|
|
||||||
Reducer_Expression.reduceExpression(
|
|
||||||
expr,
|
|
||||||
stdLibWithFoo,
|
|
||||||
InternalExpressionValue.defaultEnvironment,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
let evalToStringResultWithFoo = sourceCode =>
|
|
||||||
evalWithFoo(sourceCode)->InternalExpressionValue.toStringResult
|
|
||||||
|
|
||||||
describe("Module", () => {
|
|
||||||
test("fooNumber", () => {
|
|
||||||
let result = evalToStringResultWithFoo("Foo.fooNumber")
|
|
||||||
expect(result)->toEqual("Ok(0)")
|
|
||||||
})
|
|
||||||
test("fooString", () => {
|
|
||||||
let result = evalToStringResultWithFoo("Foo.fooString")
|
|
||||||
expect(result)->toEqual("Ok('Foo String')")
|
|
||||||
})
|
|
||||||
test("fooBool", () => {
|
|
||||||
let result = evalToStringResultWithFoo("Foo.fooBool")
|
|
||||||
expect(result)->toEqual("Ok(true)")
|
|
||||||
})
|
|
||||||
test("fooBool", () => {
|
|
||||||
let result = evalToStringResultWithFoo("Foo.fooBool")
|
|
||||||
expect(result)->toEqual("Ok(true)")
|
|
||||||
})
|
|
||||||
test("makeFoo", () => {
|
|
||||||
let result = evalToStringResultWithFoo("Foo.makeFoo('a', 'b')")
|
|
||||||
expect(result)->toEqual("Ok('I am a-foo and I am b-foo')")
|
|
||||||
})
|
|
||||||
test("makeFoo wrong arguments", () => {
|
|
||||||
let result = evalToStringResultWithFoo("Foo.makeFoo(1, 2)")
|
|
||||||
// Notice the error with types
|
|
||||||
expect(result)->toEqual("Error(Function not found: makeFoo(Number,Number))")
|
|
||||||
})
|
|
||||||
test("makeBar", () => {
|
|
||||||
let result = evalToStringResultWithFoo("Foo.makeBar(1, 2)")
|
|
||||||
expect(result)->toEqual("Ok('I am 1-bar and I am 2-bar')")
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -236,7 +236,8 @@ describe("Peggy parse", () => {
|
||||||
testParse("1m+2cm", "{(::add (::fromUnit_m 1) (::fromUnit_cm 2))}")
|
testParse("1m+2cm", "{(::add (::fromUnit_m 1) (::fromUnit_cm 2))}")
|
||||||
})
|
})
|
||||||
describe("Module", () => {
|
describe("Module", () => {
|
||||||
testParse("Math.pi", "{(::$_atIndex_$ @Math 'pi')}")
|
testParse("x", "{:x}")
|
||||||
|
testParse("Math.pi", "{:Math.pi}")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ describe("Peggy parse type", () => {
|
||||||
"{(::$_typeOf_$ :f (::$_typeFunction_$ (::$_constructArray_$ (#number #number #number))))}",
|
"{(::$_typeOf_$ :f (::$_typeFunction_$ (::$_constructArray_$ (#number #number #number))))}",
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
describe("high priority modifier", () => {
|
describe("high priority contract", () => {
|
||||||
testParse(
|
testParse(
|
||||||
"answer: number<-min<-max(100)|string",
|
"answer: number<-min<-max(100)|string",
|
||||||
"{(::$_typeOf_$ :answer (::$_typeOr_$ (::$_constructArray_$ ((::$_typeModifier_max_$ (::$_typeModifier_min_$ #number) 100) #string))))}",
|
"{(::$_typeOf_$ :answer (::$_typeOr_$ (::$_constructArray_$ ((::$_typeModifier_max_$ (::$_typeModifier_min_$ #number) 100) #string))))}",
|
||||||
|
@ -30,7 +30,7 @@ describe("Peggy parse type", () => {
|
||||||
"{(::$_typeOf_$ :answer (::$_typeModifier_memberOf_$ #number (::$_constructArray_$ (1 3 5))))}",
|
"{(::$_typeOf_$ :answer (::$_typeModifier_memberOf_$ #number (::$_constructArray_$ (1 3 5))))}",
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
describe("low priority modifier", () => {
|
describe("low priority contract", () => {
|
||||||
testParse(
|
testParse(
|
||||||
"answer: number | string $ opaque",
|
"answer: number | string $ opaque",
|
||||||
"{(::$_typeOf_$ :answer (::$_typeModifier_opaque_$ (::$_typeOr_$ (::$_constructArray_$ (#number #string)))))}",
|
"{(::$_typeOf_$ :answer (::$_typeModifier_opaque_$ (::$_typeOr_$ (::$_constructArray_$ (#number #string)))))}",
|
||||||
|
@ -63,14 +63,14 @@ describe("Peggy parse type", () => {
|
||||||
"{(::$_typeOf_$ :weekend (::$_typeOr_$ (::$_constructArray_$ ((::$_typeConstructor_$ #Saturday (::$_constructArray_$ ())) (::$_typeConstructor_$ #Sunday (::$_constructArray_$ ()))))))}",
|
"{(::$_typeOf_$ :weekend (::$_typeOr_$ (::$_constructArray_$ ((::$_typeConstructor_$ #Saturday (::$_constructArray_$ ())) (::$_typeConstructor_$ #Sunday (::$_constructArray_$ ()))))))}",
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
describe("type paranthesis", () => {
|
describe("type parenthesis", () => {
|
||||||
//$ is introduced to avoid paranthesis
|
//$ is introduced to avoid parenthesis
|
||||||
testParse(
|
testParse(
|
||||||
"answer: (number|string)<-opaque",
|
"answer: (number|string)<-opaque",
|
||||||
"{(::$_typeOf_$ :answer (::$_typeModifier_opaque_$ (::$_typeOr_$ (::$_constructArray_$ (#number #string)))))}",
|
"{(::$_typeOf_$ :answer (::$_typeModifier_opaque_$ (::$_typeOr_$ (::$_constructArray_$ (#number #string)))))}",
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
describe("squiggle expressions in type modifiers", () => {
|
describe("squiggle expressions in type contracts", () => {
|
||||||
testParse(
|
testParse(
|
||||||
"odds1 = [1,3,5]; odds2 = [7, 9]; type odds = number<-memberOf(concat(odds1, odds2))",
|
"odds1 = [1,3,5]; odds2 = [7, 9]; type odds = number<-memberOf(concat(odds1, odds2))",
|
||||||
"{:odds1 = {(::$_constructArray_$ (1 3 5))}; :odds2 = {(::$_constructArray_$ (7 9))}; (::$_typeAlias_$ #odds (::$_typeModifier_memberOf_$ #number (::concat :odds1 :odds2)))}",
|
"{:odds1 = {(::$_constructArray_$ (1 3 5))}; :odds2 = {(::$_constructArray_$ (7 9))}; (::$_typeAlias_$ #odds (::$_typeModifier_memberOf_$ #number (::concat :odds1 :odds2)))}",
|
||||||
|
|
|
@ -4,6 +4,7 @@ module ExpressionValue = ReducerInterface.InternalExpressionValue
|
||||||
module Parse = Reducer_Peggy_Parse
|
module Parse = Reducer_Peggy_Parse
|
||||||
module Result = Belt.Result
|
module Result = Belt.Result
|
||||||
module ToExpression = Reducer_Peggy_ToExpression
|
module ToExpression = Reducer_Peggy_ToExpression
|
||||||
|
module Bindings = Reducer_Bindings
|
||||||
|
|
||||||
open Jest
|
open Jest
|
||||||
open Expect
|
open Expect
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
|
module Bindings = Reducer_Bindings
|
||||||
|
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||||
|
|
||||||
open Jest
|
open Jest
|
||||||
open Reducer_Peggy_TestHelpers
|
open Reducer_Peggy_TestHelpers
|
||||||
|
|
||||||
describe("Peggy to Expression", () => {
|
describe("Peggy to Expression", () => {
|
||||||
describe("literals operators parenthesis", () => {
|
describe("literals operators parenthesis", () => {
|
||||||
// Note that there is always an outer block. Otherwise, external bindings are ignrored at the first statement
|
// Note that there is always an outer block. Otherwise, external bindings are ignored at the first statement
|
||||||
testToExpression("1", "{1}", ~v="1", ())
|
testToExpression("1", "{1}", ~v="1", ())
|
||||||
testToExpression("'hello'", "{'hello'}", ~v="'hello'", ())
|
testToExpression("'hello'", "{'hello'}", ~v="'hello'", ())
|
||||||
testToExpression("true", "{true}", ~v="true", ())
|
testToExpression("true", "{true}", ~v="true", ())
|
||||||
|
@ -183,6 +186,14 @@ describe("Peggy to Expression", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("module", () => {
|
describe("module", () => {
|
||||||
testToExpression("Math.pi", "{(:$_atIndex_$ :Math 'pi')}", ~v="3.141592653589793", ())
|
// testToExpression("Math.pi", "{:Math.pi}", ~v="3.141592653589793", ())
|
||||||
|
// Only.test("stdlibrary", () => {
|
||||||
|
// ReducerInterface_StdLib.internalStdLib
|
||||||
|
// ->IEvBindings
|
||||||
|
// ->InternalExpressionValue.toString
|
||||||
|
// ->expect
|
||||||
|
// ->toBe("")
|
||||||
|
// })
|
||||||
|
testToExpression("Math.pi", "{:Math.pi}", ~v="3.141592653589793", ())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -40,7 +40,7 @@ describe("Peggy Types to Expression", () => {
|
||||||
(),
|
(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
describe("high priority modifier", () => {
|
describe("high priority contract", () => {
|
||||||
testToExpression(
|
testToExpression(
|
||||||
"answer: number<-min(1)<-max(100)|string",
|
"answer: number<-min(1)<-max(100)|string",
|
||||||
"{(:$_typeOf_$ :answer (:$_typeOr_$ (:$_constructArray_$ ((:$_typeModifier_max_$ (:$_typeModifier_min_$ #number 1) 100) #string))))}",
|
"{(:$_typeOf_$ :answer (:$_typeOr_$ (:$_constructArray_$ ((:$_typeModifier_max_$ (:$_typeModifier_min_$ #number 1) 100) #string))))}",
|
||||||
|
@ -78,7 +78,7 @@ describe("Peggy Types to Expression", () => {
|
||||||
(),
|
(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
describe("low priority modifier", () => {
|
describe("low priority contract", () => {
|
||||||
testToExpression(
|
testToExpression(
|
||||||
"answer: number | string $ opaque",
|
"answer: number | string $ opaque",
|
||||||
"{(:$_typeOf_$ :answer (:$_typeModifier_opaque_$ (:$_typeOr_$ (:$_constructArray_$ (#number #string)))))}",
|
"{(:$_typeOf_$ :answer (:$_typeModifier_opaque_$ (:$_typeOr_$ (:$_constructArray_$ (#number #string)))))}",
|
||||||
|
@ -86,7 +86,7 @@ describe("Peggy Types to Expression", () => {
|
||||||
(),
|
(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
describe("squiggle expressions in type modifiers", () => {
|
describe("squiggle expressions in type contracts", () => {
|
||||||
testToExpression(
|
testToExpression(
|
||||||
"odds1 = [1,3,5]; odds2 = [7, 9]; type odds = number<-memberOf(concat(odds1, odds2))",
|
"odds1 = [1,3,5]; odds2 = [7, 9]; type odds = number<-memberOf(concat(odds1, odds2))",
|
||||||
"{(:$_let_$ :odds1 {(:$_constructArray_$ (1 3 5))}); (:$_let_$ :odds2 {(:$_constructArray_$ (7 9))}); (:$_typeAlias_$ #odds (:$_typeModifier_memberOf_$ #number (:concat :odds1 :odds2)))}",
|
"{(:$_let_$ :odds1 {(:$_constructArray_$ (1 3 5))}); (:$_let_$ :odds2 {(:$_constructArray_$ (7 9))}); (:$_typeAlias_$ #odds (:$_typeModifier_memberOf_$ #number (:concat :odds1 :odds2)))}",
|
||||||
|
|
|
@ -8,7 +8,7 @@ module InternalExpressionValue = ReducerInterface.InternalExpressionValue
|
||||||
module ExpressionWithContext = Reducer_ExpressionWithContext
|
module ExpressionWithContext = Reducer_ExpressionWithContext
|
||||||
module Macro = Reducer_Expression_Macro
|
module Macro = Reducer_Expression_Macro
|
||||||
module T = Reducer_Expression_T
|
module T = Reducer_Expression_T
|
||||||
module Module = Reducer_Module
|
module Bindings = Reducer_Bindings
|
||||||
|
|
||||||
let testMacro_ = (
|
let testMacro_ = (
|
||||||
tester,
|
tester,
|
||||||
|
@ -16,7 +16,7 @@ let testMacro_ = (
|
||||||
expr: T.expression,
|
expr: T.expression,
|
||||||
expectedCode: string,
|
expectedCode: string,
|
||||||
) => {
|
) => {
|
||||||
let bindings = Module.fromArray(bindArray)
|
let bindings = Bindings.fromArray(bindArray)
|
||||||
tester(expr->T.toString, () =>
|
tester(expr->T.toString, () =>
|
||||||
expr
|
expr
|
||||||
->Macro.expandMacroCall(
|
->Macro.expandMacroCall(
|
||||||
|
@ -36,7 +36,7 @@ let testMacroEval_ = (
|
||||||
expr: T.expression,
|
expr: T.expression,
|
||||||
expectedValue: string,
|
expectedValue: string,
|
||||||
) => {
|
) => {
|
||||||
let bindings = Module.fromArray(bindArray)
|
let bindings = Bindings.fromArray(bindArray)
|
||||||
tester(expr->T.toString, () =>
|
tester(expr->T.toString, () =>
|
||||||
expr
|
expr
|
||||||
->Macro.doMacroCall(
|
->Macro.doMacroCall(
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
module Expression = Reducer_Expression
|
||||||
|
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||||
|
module Bindings = Reducer_Bindings
|
||||||
|
module T = Reducer_Type_T
|
||||||
|
module TypeCompile = Reducer_Type_Compile
|
||||||
|
|
||||||
|
open Jest
|
||||||
|
open Expect
|
||||||
|
|
||||||
|
let myIevEval = (aTypeSourceCode: string) =>
|
||||||
|
TypeCompile.ievFromTypeExpression(aTypeSourceCode, Expression.reduceExpression)
|
||||||
|
let myIevEvalToString = (aTypeSourceCode: string) =>
|
||||||
|
myIevEval(aTypeSourceCode)->InternalExpressionValue.toStringResult
|
||||||
|
|
||||||
|
let myIevExpectEqual = (aTypeSourceCode, answer) =>
|
||||||
|
expect(myIevEvalToString(aTypeSourceCode))->toEqual(answer)
|
||||||
|
|
||||||
|
let myIevTest = (test, aTypeSourceCode, answer) =>
|
||||||
|
test(aTypeSourceCode, () => myIevExpectEqual(aTypeSourceCode, answer))
|
||||||
|
|
||||||
|
let myTypeEval = (aTypeSourceCode: string) =>
|
||||||
|
TypeCompile.fromTypeExpression(aTypeSourceCode, Expression.reduceExpression)
|
||||||
|
let myTypeEvalToString = (aTypeSourceCode: string) => myTypeEval(aTypeSourceCode)->T.toStringResult
|
||||||
|
|
||||||
|
let myTypeExpectEqual = (aTypeSourceCode, answer) =>
|
||||||
|
expect(myTypeEvalToString(aTypeSourceCode))->toEqual(answer)
|
||||||
|
|
||||||
|
let myTypeTest = (test, aTypeSourceCode, answer) =>
|
||||||
|
test(aTypeSourceCode, () => myTypeExpectEqual(aTypeSourceCode, answer))
|
||||||
|
|
||||||
|
// | ItTypeIdentifier(string)
|
||||||
|
myTypeTest(test, "number", "number")
|
||||||
|
myTypeTest(test, "(number)", "number")
|
||||||
|
// | ItModifiedType({modifiedType: iType})
|
||||||
|
myIevTest(test, "number<-min(0)", "Ok({min: 0,typeIdentifier: #number,typeTag: 'typeIdentifier'})")
|
||||||
|
myTypeTest(test, "number<-min(0)", "number<-min(0)")
|
||||||
|
// | ItTypeOr({typeOr: array<iType>})
|
||||||
|
myTypeTest(test, "number | string", "(number | string)")
|
||||||
|
// | ItTypeFunction({inputs: array<iType>, output: iType})
|
||||||
|
myTypeTest(test, "number => number => number", "(number => number => number)")
|
||||||
|
// | ItTypeArray({element: iType})
|
||||||
|
myIevTest(test, "[number]", "Ok({element: #number,typeTag: 'typeArray'})")
|
||||||
|
myTypeTest(test, "[number]", "[number]")
|
||||||
|
// | ItTypeTuple({elements: array<iType>})
|
||||||
|
myTypeTest(test, "[number, string]", "[number, string]")
|
||||||
|
// | ItTypeRecord({properties: Belt.Map.String.t<iType>})
|
||||||
|
myIevTest(
|
||||||
|
test,
|
||||||
|
"{age: number, name: string}",
|
||||||
|
"Ok({properties: {age: #number,name: #string},typeTag: 'typeRecord'})",
|
||||||
|
)
|
||||||
|
myTypeTest(test, "{age: number, name: string}", "{age: number, name: string}")
|
|
@ -0,0 +1,41 @@
|
||||||
|
module Expression = Reducer_Expression
|
||||||
|
module ExpressionT = Reducer_Expression_T
|
||||||
|
module ErrorValue = Reducer_ErrorValue
|
||||||
|
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||||
|
module Bindings = Reducer_Bindings
|
||||||
|
module T = Reducer_Type_T
|
||||||
|
module TypeChecker = Reducer_Type_TypeChecker
|
||||||
|
|
||||||
|
open Jest
|
||||||
|
open Expect
|
||||||
|
|
||||||
|
let checkArgumentsSourceCode = (aTypeSourceCode: string, sourceCode: string): result<
|
||||||
|
'v,
|
||||||
|
ErrorValue.t,
|
||||||
|
> => {
|
||||||
|
let reducerFn = Expression.reduceExpression
|
||||||
|
let rResult =
|
||||||
|
Reducer.parse(sourceCode)->Belt.Result.flatMap(expr =>
|
||||||
|
reducerFn(expr, Bindings.emptyBindings, InternalExpressionValue.defaultEnvironment)
|
||||||
|
)
|
||||||
|
rResult->Belt.Result.flatMap(result =>
|
||||||
|
switch result {
|
||||||
|
| IEvArray(args) => TypeChecker.checkArguments(aTypeSourceCode, args, reducerFn)
|
||||||
|
| _ => Js.Exn.raiseError("Arguments has to be an array")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let myCheckArguments = (aTypeSourceCode: string, sourceCode: string): string =>
|
||||||
|
switch checkArgumentsSourceCode(aTypeSourceCode, sourceCode) {
|
||||||
|
| Ok(_) => "Ok"
|
||||||
|
| Error(error) => ErrorValue.errorToString(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
let myCheckArgumentsExpectEqual = (aTypeSourceCode, sourceCode, answer) =>
|
||||||
|
expect(myCheckArguments(aTypeSourceCode, sourceCode))->toEqual(answer)
|
||||||
|
|
||||||
|
let myCheckArgumentsTest = (test, aTypeSourceCode, sourceCode, answer) =>
|
||||||
|
test(aTypeSourceCode, () => myCheckArgumentsExpectEqual(aTypeSourceCode, sourceCode, answer))
|
||||||
|
|
||||||
|
myCheckArgumentsTest(test, "number=>number=>number", "[1,2]", "Ok")
|
|
@ -0,0 +1,70 @@
|
||||||
|
module Expression = Reducer_Expression
|
||||||
|
module ExpressionT = Reducer_Expression_T
|
||||||
|
module ErrorValue = Reducer_ErrorValue
|
||||||
|
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||||
|
module Bindings = Reducer_Bindings
|
||||||
|
module T = Reducer_Type_T
|
||||||
|
module TypeChecker = Reducer_Type_TypeChecker
|
||||||
|
|
||||||
|
open Jest
|
||||||
|
open Expect
|
||||||
|
|
||||||
|
// In development, you are expected to use TypeChecker.isTypeOf(aTypeSourceCode, result, reducerFn).
|
||||||
|
// isTypeOfSourceCode is written to use strings instead of expression values.
|
||||||
|
|
||||||
|
let isTypeOfSourceCode = (aTypeSourceCode: string, sourceCode: string): result<
|
||||||
|
'v,
|
||||||
|
ErrorValue.t,
|
||||||
|
> => {
|
||||||
|
let reducerFn = Expression.reduceExpression
|
||||||
|
let rResult =
|
||||||
|
Reducer.parse(sourceCode)->Belt.Result.flatMap(expr =>
|
||||||
|
reducerFn(expr, Bindings.emptyBindings, InternalExpressionValue.defaultEnvironment)
|
||||||
|
)
|
||||||
|
rResult->Belt.Result.flatMap(result => TypeChecker.isTypeOf(aTypeSourceCode, result, reducerFn))
|
||||||
|
}
|
||||||
|
|
||||||
|
let myTypeCheck = (aTypeSourceCode: string, sourceCode: string): string =>
|
||||||
|
switch isTypeOfSourceCode(aTypeSourceCode, sourceCode) {
|
||||||
|
| Ok(_) => "Ok"
|
||||||
|
| Error(error) => ErrorValue.errorToString(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
let myTypeCheckExpectEqual = (aTypeSourceCode, sourceCode, answer) =>
|
||||||
|
expect(myTypeCheck(aTypeSourceCode, sourceCode))->toEqual(answer)
|
||||||
|
|
||||||
|
let myTypeCheckTest = (test, aTypeSourceCode, sourceCode, answer) =>
|
||||||
|
test(aTypeSourceCode, () => myTypeCheckExpectEqual(aTypeSourceCode, sourceCode, answer))
|
||||||
|
|
||||||
|
myTypeCheckTest(test, "number", "1", "Ok")
|
||||||
|
myTypeCheckTest(test, "number", "'2'", "Expected type: number but got: '2'")
|
||||||
|
myTypeCheckTest(test, "string", "3", "Expected type: string but got: 3")
|
||||||
|
myTypeCheckTest(test, "string", "'a'", "Ok")
|
||||||
|
myTypeCheckTest(test, "[number]", "[1,2,3]", "Ok")
|
||||||
|
myTypeCheckTest(test, "[number]", "['a','a','a']", "Expected type: number but got: 'a'")
|
||||||
|
myTypeCheckTest(test, "[number]", "[1,'a',3]", "Expected type: number but got: 'a'")
|
||||||
|
myTypeCheckTest(test, "[number, string]", "[1,'a']", "Ok")
|
||||||
|
myTypeCheckTest(test, "[number, string]", "[1, 2]", "Expected type: string but got: 2")
|
||||||
|
myTypeCheckTest(
|
||||||
|
test,
|
||||||
|
"[number, string, string]",
|
||||||
|
"[1,'a']",
|
||||||
|
"Expected type: [number, string, string] but got: [1,'a']",
|
||||||
|
)
|
||||||
|
myTypeCheckTest(
|
||||||
|
test,
|
||||||
|
"[number, string]",
|
||||||
|
"[1,'a', 3]",
|
||||||
|
"Expected type: [number, string] but got: [1,'a',3]",
|
||||||
|
)
|
||||||
|
myTypeCheckTest(test, "{age: number, name: string}", "{age: 1, name: 'a'}", "Ok")
|
||||||
|
myTypeCheckTest(
|
||||||
|
test,
|
||||||
|
"{age: number, name: string}",
|
||||||
|
"{age: 1, name: 'a', job: 'IT'}",
|
||||||
|
"Expected type: {age: number, name: string} but got: {age: 1,job: 'IT',name: 'a'}",
|
||||||
|
)
|
||||||
|
myTypeCheckTest(test, "number | string", "1", "Ok")
|
||||||
|
myTypeCheckTest(test, "date | string", "1", "Expected type: (date | string) but got: 1")
|
||||||
|
myTypeCheckTest(test, "number<-min(10)", "10", "Ok")
|
||||||
|
myTypeCheckTest(test, "number<-min(10)", "0", "Expected type: number<-min(10) but got: 0")
|
|
@ -10,5 +10,5 @@ describe("Evaluate ternary operator", () => {
|
||||||
testEvalToBe("false ? 'YES' : 'NO'", "Ok('NO')")
|
testEvalToBe("false ? 'YES' : 'NO'", "Ok('NO')")
|
||||||
testEvalToBe("2 > 1 ? 'YES' : 'NO'", "Ok('YES')")
|
testEvalToBe("2 > 1 ? 'YES' : 'NO'", "Ok('YES')")
|
||||||
testEvalToBe("2 <= 1 ? 'YES' : 'NO'", "Ok('NO')")
|
testEvalToBe("2 <= 1 ? 'YES' : 'NO'", "Ok('NO')")
|
||||||
testEvalToBe("1+1 ? 'YES' : 'NO'", "Error(Expected type: Boolean)")
|
testEvalToBe("1+1 ? 'YES' : 'NO'", "Error(Expected type: Boolean but got: )")
|
||||||
})
|
})
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
open Jest
|
||||||
|
open Expect
|
||||||
|
open Reducer_TestHelpers
|
||||||
|
|
||||||
|
let expectEvalToBeOk = (expr: string) =>
|
||||||
|
Reducer.evaluate(expr)->Reducer_Helpers.rRemoveDefaultsExternal->E.R.isOk->expect->toBe(true)
|
||||||
|
|
||||||
|
let registry = FunctionRegistry_Library.registry
|
||||||
|
let examples = E.A.to_list(FunctionRegistry_Core.Registry.allExamples(registry))
|
||||||
|
|
||||||
|
describe("FunctionRegistry Library", () => {
|
||||||
|
describe("Regular tests", () => {
|
||||||
|
testEvalToBe("List.make(3, 'HI')", "Ok(['HI','HI','HI'])")
|
||||||
|
testEvalToBe("make(3, 'HI')", "Error(Function not found: make(Number,String))")
|
||||||
|
testEvalToBe("List.upTo(1,3)", "Ok([1,2,3])")
|
||||||
|
testEvalToBe("List.first([3,5,8])", "Ok(3)")
|
||||||
|
testEvalToBe("List.last([3,5,8])", "Ok(8)")
|
||||||
|
testEvalToBe("List.reverse([3,5,8])", "Ok([8,5,3])")
|
||||||
|
testEvalToBe("Dist.normal(5,2)", "Ok(Normal(5,2))")
|
||||||
|
testEvalToBe("normal(5,2)", "Ok(Normal(5,2))")
|
||||||
|
testEvalToBe("normal({mean:5,stdev:2})", "Ok(Normal(5,2))")
|
||||||
|
testEvalToBe("-2 to 4", "Ok(Normal(1,1.8238704957353074))")
|
||||||
|
testEvalToBe("pointMass(5)", "Ok(PointMass(5))")
|
||||||
|
testEvalToBe("Number.floor(5.5)", "Ok(5)")
|
||||||
|
testEvalToBe("Number.ceil(5.5)", "Ok(6)")
|
||||||
|
testEvalToBe("floor(5.5)", "Ok(5)")
|
||||||
|
testEvalToBe("ceil(5.5)", "Ok(6)")
|
||||||
|
testEvalToBe("Number.abs(5.5)", "Ok(5.5)")
|
||||||
|
testEvalToBe("abs(5.5)", "Ok(5.5)")
|
||||||
|
testEvalToBe("Number.exp(10)", "Ok(22026.465794806718)")
|
||||||
|
testEvalToBe("Number.log10(10)", "Ok(1)")
|
||||||
|
testEvalToBe("Number.log2(10)", "Ok(3.321928094887362)")
|
||||||
|
testEvalToBe("Number.sum([2,5,3])", "Ok(10)")
|
||||||
|
testEvalToBe("sum([2,5,3])", "Ok(10)")
|
||||||
|
testEvalToBe("Number.product([2,5,3])", "Ok(30)")
|
||||||
|
testEvalToBe("Number.min([2,5,3])", "Ok(2)")
|
||||||
|
testEvalToBe("Number.max([2,5,3])", "Ok(5)")
|
||||||
|
testEvalToBe("Number.mean([0,5,10])", "Ok(5)")
|
||||||
|
testEvalToBe("Number.geomean([1,5,18])", "Ok(4.481404746557164)")
|
||||||
|
testEvalToBe("Number.stdev([0,5,10,15])", "Ok(5.5901699437494745)")
|
||||||
|
testEvalToBe("Number.variance([0,5,10,15])", "Ok(31.25)")
|
||||||
|
testEvalToBe("Number.sort([10,0,15,5])", "Ok([0,5,10,15])")
|
||||||
|
testEvalToBe("Number.cumsum([1,5,3])", "Ok([1,6,9])")
|
||||||
|
testEvalToBe("Number.cumprod([1,5,3])", "Ok([1,5,15])")
|
||||||
|
testEvalToBe("Number.diff([1,5,3])", "Ok([4,-2])")
|
||||||
|
testEvalToBe(
|
||||||
|
"Dist.logScore({estimate: normal(5,2), answer: normal(5.2,1), prior: normal(5.5,3)})",
|
||||||
|
"Ok(-0.33591375663884876)",
|
||||||
|
)
|
||||||
|
testEvalToBe(
|
||||||
|
"Dist.logScore({estimate: normal(5,2), answer: normal(5.2,1)})",
|
||||||
|
"Ok(0.32244107041564646)",
|
||||||
|
)
|
||||||
|
testEvalToBe("Dist.logScore({estimate: normal(5,2), answer: 4.5})", "Ok(1.6433360626394853)")
|
||||||
|
testEvalToBe("Dist.klDivergence(normal(5,2), normal(5,1.5))", "Ok(0.06874342818671068)")
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("Fn auto-testing", () => {
|
||||||
|
testAll("tests of validity", examples, r => {
|
||||||
|
expectEvalToBeOk(r)
|
||||||
|
})
|
||||||
|
|
||||||
|
testAll(
|
||||||
|
"tests of type",
|
||||||
|
E.A.to_list(
|
||||||
|
FunctionRegistry_Core.Registry.allExamplesWithFns(registry)->E.A2.filter(((fn, _)) =>
|
||||||
|
E.O.isSome(fn.output)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
((fn, example)) => {
|
||||||
|
let responseType =
|
||||||
|
example
|
||||||
|
->Reducer.evaluate
|
||||||
|
->E.R2.fmap(ReducerInterface_InternalExpressionValue.externalValueToValueType)
|
||||||
|
let expectedOutputType = fn.output |> E.O.toExn("")
|
||||||
|
expect(responseType)->toEqual(Ok(expectedOutputType))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
|
@ -29,7 +29,7 @@ let {toFloat, toDist, toString, toError, fmap} = module(DistributionOperation.Ou
|
||||||
|
|
||||||
let fnImage = (theFn, inps) => Js.Array.map(theFn, inps)
|
let fnImage = (theFn, inps) => Js.Array.map(theFn, inps)
|
||||||
|
|
||||||
let env: DistributionOperation.env = {
|
let env: GenericDist.env = {
|
||||||
sampleCount: MagicNumbers.Environment.defaultSampleCount,
|
sampleCount: MagicNumbers.Environment.defaultSampleCount,
|
||||||
xyPointLength: MagicNumbers.Environment.defaultXYPointLength,
|
xyPointLength: MagicNumbers.Environment.defaultXYPointLength,
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@
|
||||||
"chalk": "^5.0.1",
|
"chalk": "^5.0.1",
|
||||||
"codecov": "^3.8.3",
|
"codecov": "^3.8.3",
|
||||||
"fast-check": "^3.0.1",
|
"fast-check": "^3.0.1",
|
||||||
"gentype": "^4.4.0",
|
"gentype": "^4.5.0",
|
||||||
"jest": "^27.5.1",
|
"jest": "^27.5.1",
|
||||||
"moduleserve": "^0.9.1",
|
"moduleserve": "^0.9.1",
|
||||||
"nyc": "^15.1.0",
|
"nyc": "^15.1.0",
|
||||||
|
@ -65,7 +65,7 @@
|
||||||
"rescript-fast-check": "^1.1.1",
|
"rescript-fast-check": "^1.1.1",
|
||||||
"ts-jest": "^27.1.4",
|
"ts-jest": "^27.1.4",
|
||||||
"ts-loader": "^9.3.0",
|
"ts-loader": "^9.3.0",
|
||||||
"ts-node": "^10.8.1",
|
"ts-node": "^10.9.1",
|
||||||
"typescript": "^4.7.4",
|
"typescript": "^4.7.4",
|
||||||
"webpack": "^5.73.0",
|
"webpack": "^5.73.0",
|
||||||
"webpack-cli": "^4.10.0"
|
"webpack-cli": "^4.10.0"
|
||||||
|
|
|
@ -4,12 +4,9 @@ type error = DistributionTypes.error
|
||||||
|
|
||||||
// TODO: It could be great to use a cache for some calculations (basically, do memoization). Also, better analytics/tracking could go a long way.
|
// TODO: It could be great to use a cache for some calculations (basically, do memoization). Also, better analytics/tracking could go a long way.
|
||||||
|
|
||||||
type env = {
|
type env = GenericDist.env
|
||||||
sampleCount: int,
|
|
||||||
xyPointLength: int,
|
|
||||||
}
|
|
||||||
|
|
||||||
let defaultEnv = {
|
let defaultEnv: env = {
|
||||||
sampleCount: MagicNumbers.Environment.defaultSampleCount,
|
sampleCount: MagicNumbers.Environment.defaultSampleCount,
|
||||||
xyPointLength: MagicNumbers.Environment.defaultXYPointLength,
|
xyPointLength: MagicNumbers.Environment.defaultXYPointLength,
|
||||||
}
|
}
|
||||||
|
@ -93,7 +90,7 @@ module OutputLocal = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let rec run = (~env, functionCallInfo: functionCallInfo): outputType => {
|
let rec run = (~env: env, functionCallInfo: functionCallInfo): outputType => {
|
||||||
let {sampleCount, xyPointLength} = env
|
let {sampleCount, xyPointLength} = env
|
||||||
|
|
||||||
let reCall = (~env=env, ~functionCallInfo=functionCallInfo, ()) => {
|
let reCall = (~env=env, ~functionCallInfo=functionCallInfo, ()) => {
|
||||||
|
@ -101,14 +98,14 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => {
|
||||||
}
|
}
|
||||||
|
|
||||||
let toPointSetFn = r => {
|
let toPointSetFn = r => {
|
||||||
switch reCall(~functionCallInfo=FromDist(ToDist(ToPointSet), r), ()) {
|
switch reCall(~functionCallInfo=FromDist(#ToDist(ToPointSet), r), ()) {
|
||||||
| Dist(PointSet(p)) => Ok(p)
|
| Dist(PointSet(p)) => Ok(p)
|
||||||
| e => Error(OutputLocal.toErrorOrUnreachable(e))
|
| e => Error(OutputLocal.toErrorOrUnreachable(e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let toSampleSetFn = r => {
|
let toSampleSetFn = r => {
|
||||||
switch reCall(~functionCallInfo=FromDist(ToDist(ToSampleSet(sampleCount)), r), ()) {
|
switch reCall(~functionCallInfo=FromDist(#ToDist(ToSampleSet(sampleCount)), r), ()) {
|
||||||
| Dist(SampleSet(p)) => Ok(p)
|
| Dist(SampleSet(p)) => Ok(p)
|
||||||
| e => Error(OutputLocal.toErrorOrUnreachable(e))
|
| e => Error(OutputLocal.toErrorOrUnreachable(e))
|
||||||
}
|
}
|
||||||
|
@ -116,13 +113,13 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => {
|
||||||
|
|
||||||
let scaleMultiply = (r, weight) =>
|
let scaleMultiply = (r, weight) =>
|
||||||
reCall(
|
reCall(
|
||||||
~functionCallInfo=FromDist(ToDistCombination(Pointwise, #Multiply, #Float(weight)), r),
|
~functionCallInfo=FromDist(#ToDistCombination(Pointwise, #Multiply, #Float(weight)), r),
|
||||||
(),
|
(),
|
||||||
)->OutputLocal.toDistR
|
)->OutputLocal.toDistR
|
||||||
|
|
||||||
let pointwiseAdd = (r1, r2) =>
|
let pointwiseAdd = (r1, r2) =>
|
||||||
reCall(
|
reCall(
|
||||||
~functionCallInfo=FromDist(ToDistCombination(Pointwise, #Add, #Dist(r2)), r1),
|
~functionCallInfo=FromDist(#ToDistCombination(Pointwise, #Add, #Dist(r2)), r1),
|
||||||
(),
|
(),
|
||||||
)->OutputLocal.toDistR
|
)->OutputLocal.toDistR
|
||||||
|
|
||||||
|
@ -131,49 +128,40 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => {
|
||||||
dist: genericDist,
|
dist: genericDist,
|
||||||
): outputType => {
|
): outputType => {
|
||||||
let response = switch subFnName {
|
let response = switch subFnName {
|
||||||
| ToFloat(distToFloatOperation) =>
|
| #ToFloat(distToFloatOperation) =>
|
||||||
GenericDist.toFloatOperation(dist, ~toPointSetFn, ~distToFloatOperation)
|
GenericDist.toFloatOperation(dist, ~toPointSetFn, ~distToFloatOperation)
|
||||||
->E.R2.fmap(r => Float(r))
|
->E.R2.fmap(r => Float(r))
|
||||||
->OutputLocal.fromResult
|
->OutputLocal.fromResult
|
||||||
| ToString(ToString) => dist->GenericDist.toString->String
|
| #ToString(ToString) => dist->GenericDist.toString->String
|
||||||
| ToString(ToSparkline(bucketCount)) =>
|
| #ToString(ToSparkline(bucketCount)) =>
|
||||||
GenericDist.toSparkline(dist, ~sampleCount, ~bucketCount, ())
|
GenericDist.toSparkline(dist, ~sampleCount, ~bucketCount, ())
|
||||||
->E.R2.fmap(r => String(r))
|
->E.R2.fmap(r => String(r))
|
||||||
->OutputLocal.fromResult
|
->OutputLocal.fromResult
|
||||||
| ToDist(Inspect) => {
|
| #ToDist(Inspect) => {
|
||||||
Js.log2("Console log requested: ", dist)
|
Js.log2("Console log requested: ", dist)
|
||||||
Dist(dist)
|
Dist(dist)
|
||||||
}
|
}
|
||||||
| ToDist(Normalize) => dist->GenericDist.normalize->Dist
|
| #ToDist(Normalize) => dist->GenericDist.normalize->Dist
|
||||||
| ToScore(KLDivergence(t2)) =>
|
| #ToScore(LogScore(answer, prior)) =>
|
||||||
GenericDist.Score.klDivergence(dist, t2, ~toPointSetFn)
|
GenericDist.Score.logScore(~estimate=dist, ~answer, ~prior, ~env)
|
||||||
->E.R2.fmap(r => Float(r))
|
->E.R2.fmap(s => Float(s))
|
||||||
->OutputLocal.fromResult
|
->OutputLocal.fromResult
|
||||||
| ToScore(LogScore(answer, prior)) =>
|
| #ToBool(IsNormalized) => dist->GenericDist.isNormalized->Bool
|
||||||
GenericDist.Score.logScoreWithPointResolution(
|
| #ToDist(Truncate(leftCutoff, rightCutoff)) =>
|
||||||
~prediction=dist,
|
|
||||||
~answer,
|
|
||||||
~prior,
|
|
||||||
~toPointSetFn,
|
|
||||||
)
|
|
||||||
->E.R2.fmap(r => Float(r))
|
|
||||||
->OutputLocal.fromResult
|
|
||||||
| ToBool(IsNormalized) => dist->GenericDist.isNormalized->Bool
|
|
||||||
| ToDist(Truncate(leftCutoff, rightCutoff)) =>
|
|
||||||
GenericDist.truncate(~toPointSetFn, ~leftCutoff, ~rightCutoff, dist, ())
|
GenericDist.truncate(~toPointSetFn, ~leftCutoff, ~rightCutoff, dist, ())
|
||||||
->E.R2.fmap(r => Dist(r))
|
->E.R2.fmap(r => Dist(r))
|
||||||
->OutputLocal.fromResult
|
->OutputLocal.fromResult
|
||||||
| ToDist(ToSampleSet(n)) =>
|
| #ToDist(ToSampleSet(n)) =>
|
||||||
dist
|
dist
|
||||||
->GenericDist.toSampleSetDist(n)
|
->GenericDist.toSampleSetDist(n)
|
||||||
->E.R2.fmap(r => Dist(SampleSet(r)))
|
->E.R2.fmap(r => Dist(SampleSet(r)))
|
||||||
->OutputLocal.fromResult
|
->OutputLocal.fromResult
|
||||||
| ToDist(ToPointSet) =>
|
| #ToDist(ToPointSet) =>
|
||||||
dist
|
dist
|
||||||
->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(#LogarithmWithThreshold(eps), f)) =>
|
| #ToDist(Scale(#LogarithmWithThreshold(eps), f)) =>
|
||||||
dist
|
dist
|
||||||
->GenericDist.pointwiseCombinationFloat(
|
->GenericDist.pointwiseCombinationFloat(
|
||||||
~toPointSetFn,
|
~toPointSetFn,
|
||||||
|
@ -182,23 +170,23 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => {
|
||||||
)
|
)
|
||||||
->E.R2.fmap(r => Dist(r))
|
->E.R2.fmap(r => Dist(r))
|
||||||
->OutputLocal.fromResult
|
->OutputLocal.fromResult
|
||||||
| ToDist(Scale(#Multiply, f)) =>
|
| #ToDist(Scale(#Multiply, f)) =>
|
||||||
dist
|
dist
|
||||||
->GenericDist.pointwiseCombinationFloat(~toPointSetFn, ~algebraicCombination=#Multiply, ~f)
|
->GenericDist.pointwiseCombinationFloat(~toPointSetFn, ~algebraicCombination=#Multiply, ~f)
|
||||||
->E.R2.fmap(r => Dist(r))
|
->E.R2.fmap(r => Dist(r))
|
||||||
->OutputLocal.fromResult
|
->OutputLocal.fromResult
|
||||||
| ToDist(Scale(#Logarithm, f)) =>
|
| #ToDist(Scale(#Logarithm, f)) =>
|
||||||
dist
|
dist
|
||||||
->GenericDist.pointwiseCombinationFloat(~toPointSetFn, ~algebraicCombination=#Logarithm, ~f)
|
->GenericDist.pointwiseCombinationFloat(~toPointSetFn, ~algebraicCombination=#Logarithm, ~f)
|
||||||
->E.R2.fmap(r => Dist(r))
|
->E.R2.fmap(r => Dist(r))
|
||||||
->OutputLocal.fromResult
|
->OutputLocal.fromResult
|
||||||
| ToDist(Scale(#Power, f)) =>
|
| #ToDist(Scale(#Power, f)) =>
|
||||||
dist
|
dist
|
||||||
->GenericDist.pointwiseCombinationFloat(~toPointSetFn, ~algebraicCombination=#Power, ~f)
|
->GenericDist.pointwiseCombinationFloat(~toPointSetFn, ~algebraicCombination=#Power, ~f)
|
||||||
->E.R2.fmap(r => Dist(r))
|
->E.R2.fmap(r => Dist(r))
|
||||||
->OutputLocal.fromResult
|
->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
|
||||||
->GenericDist.algebraicCombination(
|
->GenericDist.algebraicCombination(
|
||||||
~strategy,
|
~strategy,
|
||||||
|
@ -209,12 +197,12 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => {
|
||||||
)
|
)
|
||||||
->E.R2.fmap(r => Dist(r))
|
->E.R2.fmap(r => Dist(r))
|
||||||
->OutputLocal.fromResult
|
->OutputLocal.fromResult
|
||||||
| ToDistCombination(Pointwise, algebraicCombination, #Dist(t2)) =>
|
| #ToDistCombination(Pointwise, algebraicCombination, #Dist(t2)) =>
|
||||||
dist
|
dist
|
||||||
->GenericDist.pointwiseCombination(~toPointSetFn, ~algebraicCombination, ~t2)
|
->GenericDist.pointwiseCombination(~toPointSetFn, ~algebraicCombination, ~t2)
|
||||||
->E.R2.fmap(r => Dist(r))
|
->E.R2.fmap(r => Dist(r))
|
||||||
->OutputLocal.fromResult
|
->OutputLocal.fromResult
|
||||||
| ToDistCombination(Pointwise, algebraicCombination, #Float(f)) =>
|
| #ToDistCombination(Pointwise, algebraicCombination, #Float(f)) =>
|
||||||
dist
|
dist
|
||||||
->GenericDist.pointwiseCombinationFloat(~toPointSetFn, ~algebraicCombination, ~f)
|
->GenericDist.pointwiseCombinationFloat(~toPointSetFn, ~algebraicCombination, ~f)
|
||||||
->E.R2.fmap(r => Dist(r))
|
->E.R2.fmap(r => Dist(r))
|
||||||
|
@ -225,8 +213,7 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => {
|
||||||
|
|
||||||
switch functionCallInfo {
|
switch functionCallInfo {
|
||||||
| FromDist(subFnName, dist) => fromDistFn(subFnName, dist)
|
| FromDist(subFnName, dist) => fromDistFn(subFnName, dist)
|
||||||
| FromFloat(subFnName, float) =>
|
| FromFloat(subFnName, x) => reCall(~functionCallInfo=FromFloat(subFnName, x), ())
|
||||||
reCall(~functionCallInfo=FromDist(subFnName, GenericDist.fromFloat(float)), ())
|
|
||||||
| Mixture(dists) =>
|
| Mixture(dists) =>
|
||||||
dists
|
dists
|
||||||
->GenericDist.mixture(~scaleMultiplyFn=scaleMultiply, ~pointwiseAddFn=pointwiseAdd)
|
->GenericDist.mixture(~scaleMultiplyFn=scaleMultiply, ~pointwiseAddFn=pointwiseAdd)
|
||||||
|
@ -278,13 +265,16 @@ module Constructors = {
|
||||||
let pdf = (~env, dist, f) => C.pdf(dist, f)->run(~env)->toFloatR
|
let pdf = (~env, dist, f) => C.pdf(dist, f)->run(~env)->toFloatR
|
||||||
let normalize = (~env, dist) => C.normalize(dist)->run(~env)->toDistR
|
let normalize = (~env, dist) => C.normalize(dist)->run(~env)->toDistR
|
||||||
let isNormalized = (~env, dist) => C.isNormalized(dist)->run(~env)->toBoolR
|
let isNormalized = (~env, dist) => C.isNormalized(dist)->run(~env)->toBoolR
|
||||||
let klDivergence = (~env, dist1, dist2) => C.klDivergence(dist1, dist2)->run(~env)->toFloatR
|
module LogScore = {
|
||||||
let logScoreWithPointResolution = (
|
let distEstimateDistAnswer = (~env, estimate, answer) =>
|
||||||
~env,
|
C.LogScore.distEstimateDistAnswer(estimate, answer)->run(~env)->toFloatR
|
||||||
~prediction: DistributionTypes.genericDist,
|
let distEstimateDistAnswerWithPrior = (~env, estimate, answer, prior) =>
|
||||||
~answer: float,
|
C.LogScore.distEstimateDistAnswerWithPrior(estimate, answer, prior)->run(~env)->toFloatR
|
||||||
~prior: option<DistributionTypes.genericDist>,
|
let distEstimateScalarAnswer = (~env, estimate, answer) =>
|
||||||
) => C.logScoreWithPointResolution(~prediction, ~answer, ~prior)->run(~env)->toFloatR
|
C.LogScore.distEstimateScalarAnswer(estimate, answer)->run(~env)->toFloatR
|
||||||
|
let distEstimateScalarAnswerWithPrior = (~env, estimate, answer, prior) =>
|
||||||
|
C.LogScore.distEstimateScalarAnswerWithPrior(estimate, answer, prior)->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 fromSamples = (~env, xs) => C.fromSamples(xs)->run(~env)->toDistR
|
||||||
|
|
|
@ -1,11 +1,5 @@
|
||||||
@genType
|
@genType
|
||||||
type env = {
|
let defaultEnv: GenericDist.env
|
||||||
sampleCount: int,
|
|
||||||
xyPointLength: int,
|
|
||||||
}
|
|
||||||
|
|
||||||
@genType
|
|
||||||
let defaultEnv: env
|
|
||||||
|
|
||||||
open DistributionTypes
|
open DistributionTypes
|
||||||
|
|
||||||
|
@ -19,15 +13,18 @@ type outputType =
|
||||||
| GenDistError(error)
|
| GenDistError(error)
|
||||||
|
|
||||||
@genType
|
@genType
|
||||||
let run: (~env: env, DistributionTypes.DistributionOperation.genericFunctionCallInfo) => outputType
|
let run: (
|
||||||
|
~env: GenericDist.env,
|
||||||
|
DistributionTypes.DistributionOperation.genericFunctionCallInfo,
|
||||||
|
) => outputType
|
||||||
let runFromDist: (
|
let runFromDist: (
|
||||||
~env: env,
|
~env: GenericDist.env,
|
||||||
~functionCallInfo: DistributionTypes.DistributionOperation.fromDist,
|
~functionCallInfo: DistributionTypes.DistributionOperation.fromDist,
|
||||||
genericDist,
|
genericDist,
|
||||||
) => outputType
|
) => outputType
|
||||||
let runFromFloat: (
|
let runFromFloat: (
|
||||||
~env: env,
|
~env: GenericDist.env,
|
||||||
~functionCallInfo: DistributionTypes.DistributionOperation.fromDist,
|
~functionCallInfo: DistributionTypes.DistributionOperation.fromFloat,
|
||||||
float,
|
float,
|
||||||
) => outputType
|
) => outputType
|
||||||
|
|
||||||
|
@ -42,79 +39,147 @@ module Output: {
|
||||||
let toBool: t => option<bool>
|
let toBool: t => option<bool>
|
||||||
let toBoolR: t => result<bool, error>
|
let toBoolR: t => result<bool, error>
|
||||||
let toError: t => option<error>
|
let toError: t => option<error>
|
||||||
let fmap: (~env: env, t, DistributionTypes.DistributionOperation.singleParamaterFunction) => t
|
let fmap: (
|
||||||
|
~env: GenericDist.env,
|
||||||
|
t,
|
||||||
|
DistributionTypes.DistributionOperation.singleParamaterFunction,
|
||||||
|
) => t
|
||||||
}
|
}
|
||||||
|
|
||||||
module Constructors: {
|
module Constructors: {
|
||||||
@genType
|
@genType
|
||||||
let mean: (~env: env, genericDist) => result<float, error>
|
let mean: (~env: GenericDist.env, genericDist) => result<float, error>
|
||||||
@genType
|
@genType
|
||||||
let stdev: (~env: env, genericDist) => result<float, error>
|
let stdev: (~env: GenericDist.env, genericDist) => result<float, error>
|
||||||
@genType
|
@genType
|
||||||
let variance: (~env: env, genericDist) => result<float, error>
|
let variance: (~env: GenericDist.env, genericDist) => result<float, error>
|
||||||
@genType
|
@genType
|
||||||
let sample: (~env: env, genericDist) => result<float, error>
|
let sample: (~env: GenericDist.env, genericDist) => result<float, error>
|
||||||
@genType
|
@genType
|
||||||
let cdf: (~env: env, genericDist, float) => result<float, error>
|
let cdf: (~env: GenericDist.env, genericDist, float) => result<float, error>
|
||||||
@genType
|
@genType
|
||||||
let inv: (~env: env, genericDist, float) => result<float, error>
|
let inv: (~env: GenericDist.env, genericDist, float) => result<float, error>
|
||||||
@genType
|
@genType
|
||||||
let pdf: (~env: env, genericDist, float) => result<float, error>
|
let pdf: (~env: GenericDist.env, genericDist, float) => result<float, error>
|
||||||
@genType
|
@genType
|
||||||
let normalize: (~env: env, genericDist) => result<genericDist, error>
|
let normalize: (~env: GenericDist.env, genericDist) => result<genericDist, error>
|
||||||
@genType
|
@genType
|
||||||
let isNormalized: (~env: env, genericDist) => result<bool, error>
|
let isNormalized: (~env: GenericDist.env, genericDist) => result<bool, error>
|
||||||
|
module LogScore: {
|
||||||
@genType
|
@genType
|
||||||
let klDivergence: (~env: env, genericDist, genericDist) => result<float, error>
|
let distEstimateDistAnswer: (
|
||||||
@genType
|
~env: GenericDist.env,
|
||||||
let logScoreWithPointResolution: (
|
genericDist,
|
||||||
~env: env,
|
genericDist,
|
||||||
~prediction: genericDist,
|
|
||||||
~answer: float,
|
|
||||||
~prior: option<genericDist>,
|
|
||||||
) => result<float, error>
|
) => result<float, error>
|
||||||
@genType
|
@genType
|
||||||
let toPointSet: (~env: env, genericDist) => result<genericDist, error>
|
let distEstimateDistAnswerWithPrior: (
|
||||||
|
~env: GenericDist.env,
|
||||||
|
genericDist,
|
||||||
|
genericDist,
|
||||||
|
genericDist,
|
||||||
|
) => result<float, error>
|
||||||
@genType
|
@genType
|
||||||
let toSampleSet: (~env: env, genericDist, int) => result<genericDist, error>
|
let distEstimateScalarAnswer: (
|
||||||
|
~env: GenericDist.env,
|
||||||
|
genericDist,
|
||||||
|
float,
|
||||||
|
) => result<float, error>
|
||||||
@genType
|
@genType
|
||||||
let fromSamples: (~env: env, SampleSetDist.t) => result<genericDist, error>
|
let distEstimateScalarAnswerWithPrior: (
|
||||||
@genType
|
~env: GenericDist.env,
|
||||||
let truncate: (~env: env, genericDist, option<float>, option<float>) => result<genericDist, error>
|
genericDist,
|
||||||
@genType
|
float,
|
||||||
let inspect: (~env: env, genericDist) => result<genericDist, error>
|
genericDist,
|
||||||
@genType
|
) => result<float, error>
|
||||||
let toString: (~env: env, genericDist) => result<string, error>
|
}
|
||||||
@genType
|
@genType
|
||||||
let toSparkline: (~env: env, genericDist, int) => result<string, error>
|
let toPointSet: (~env: GenericDist.env, genericDist) => result<genericDist, error>
|
||||||
@genType
|
@genType
|
||||||
let algebraicAdd: (~env: env, genericDist, genericDist) => result<genericDist, error>
|
let toSampleSet: (~env: GenericDist.env, genericDist, int) => result<genericDist, error>
|
||||||
@genType
|
@genType
|
||||||
let algebraicMultiply: (~env: env, genericDist, genericDist) => result<genericDist, error>
|
let fromSamples: (~env: GenericDist.env, SampleSetDist.t) => result<genericDist, error>
|
||||||
@genType
|
@genType
|
||||||
let algebraicDivide: (~env: env, genericDist, genericDist) => result<genericDist, error>
|
let truncate: (
|
||||||
@genType
|
~env: GenericDist.env,
|
||||||
let algebraicSubtract: (~env: env, genericDist, genericDist) => result<genericDist, error>
|
genericDist,
|
||||||
@genType
|
option<float>,
|
||||||
let algebraicLogarithm: (~env: env, genericDist, genericDist) => result<genericDist, error>
|
option<float>,
|
||||||
@genType
|
) => result<genericDist, error>
|
||||||
let algebraicPower: (~env: env, genericDist, genericDist) => result<genericDist, error>
|
@genType
|
||||||
@genType
|
let inspect: (~env: GenericDist.env, genericDist) => result<genericDist, error>
|
||||||
let scaleLogarithm: (~env: env, genericDist, float) => result<genericDist, error>
|
@genType
|
||||||
@genType
|
let toString: (~env: GenericDist.env, genericDist) => result<string, error>
|
||||||
let scaleMultiply: (~env: env, genericDist, float) => result<genericDist, error>
|
@genType
|
||||||
@genType
|
let toSparkline: (~env: GenericDist.env, genericDist, int) => result<string, error>
|
||||||
let scalePower: (~env: env, genericDist, float) => result<genericDist, error>
|
@genType
|
||||||
@genType
|
let algebraicAdd: (~env: GenericDist.env, genericDist, genericDist) => result<genericDist, error>
|
||||||
let pointwiseAdd: (~env: env, genericDist, genericDist) => result<genericDist, error>
|
@genType
|
||||||
@genType
|
let algebraicMultiply: (
|
||||||
let pointwiseMultiply: (~env: env, genericDist, genericDist) => result<genericDist, error>
|
~env: GenericDist.env,
|
||||||
@genType
|
genericDist,
|
||||||
let pointwiseDivide: (~env: env, genericDist, genericDist) => result<genericDist, error>
|
genericDist,
|
||||||
@genType
|
) => result<genericDist, error>
|
||||||
let pointwiseSubtract: (~env: env, genericDist, genericDist) => result<genericDist, error>
|
@genType
|
||||||
@genType
|
let algebraicDivide: (
|
||||||
let pointwiseLogarithm: (~env: env, genericDist, genericDist) => result<genericDist, error>
|
~env: GenericDist.env,
|
||||||
@genType
|
genericDist,
|
||||||
let pointwisePower: (~env: env, genericDist, genericDist) => result<genericDist, error>
|
genericDist,
|
||||||
|
) => result<genericDist, error>
|
||||||
|
@genType
|
||||||
|
let algebraicSubtract: (
|
||||||
|
~env: GenericDist.env,
|
||||||
|
genericDist,
|
||||||
|
genericDist,
|
||||||
|
) => result<genericDist, error>
|
||||||
|
@genType
|
||||||
|
let algebraicLogarithm: (
|
||||||
|
~env: GenericDist.env,
|
||||||
|
genericDist,
|
||||||
|
genericDist,
|
||||||
|
) => result<genericDist, error>
|
||||||
|
@genType
|
||||||
|
let algebraicPower: (
|
||||||
|
~env: GenericDist.env,
|
||||||
|
genericDist,
|
||||||
|
genericDist,
|
||||||
|
) => result<genericDist, error>
|
||||||
|
@genType
|
||||||
|
let scaleLogarithm: (~env: GenericDist.env, genericDist, float) => result<genericDist, error>
|
||||||
|
@genType
|
||||||
|
let scaleMultiply: (~env: GenericDist.env, genericDist, float) => result<genericDist, error>
|
||||||
|
@genType
|
||||||
|
let scalePower: (~env: GenericDist.env, genericDist, float) => result<genericDist, error>
|
||||||
|
@genType
|
||||||
|
let pointwiseAdd: (~env: GenericDist.env, genericDist, genericDist) => result<genericDist, error>
|
||||||
|
@genType
|
||||||
|
let pointwiseMultiply: (
|
||||||
|
~env: GenericDist.env,
|
||||||
|
genericDist,
|
||||||
|
genericDist,
|
||||||
|
) => result<genericDist, error>
|
||||||
|
@genType
|
||||||
|
let pointwiseDivide: (
|
||||||
|
~env: GenericDist.env,
|
||||||
|
genericDist,
|
||||||
|
genericDist,
|
||||||
|
) => result<genericDist, error>
|
||||||
|
@genType
|
||||||
|
let pointwiseSubtract: (
|
||||||
|
~env: GenericDist.env,
|
||||||
|
genericDist,
|
||||||
|
genericDist,
|
||||||
|
) => result<genericDist, error>
|
||||||
|
@genType
|
||||||
|
let pointwiseLogarithm: (
|
||||||
|
~env: GenericDist.env,
|
||||||
|
genericDist,
|
||||||
|
genericDist,
|
||||||
|
) => result<genericDist, error>
|
||||||
|
@genType
|
||||||
|
let pointwisePower: (
|
||||||
|
~env: GenericDist.env,
|
||||||
|
genericDist,
|
||||||
|
genericDist,
|
||||||
|
) => result<genericDist, error>
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,61 +98,86 @@ module DistributionOperation = {
|
||||||
| ToString
|
| ToString
|
||||||
| ToSparkline(int)
|
| ToSparkline(int)
|
||||||
|
|
||||||
type toScore = KLDivergence(genericDist) | LogScore(float, option<genericDist>)
|
type genericDistOrScalar = Score_Dist(genericDist) | Score_Scalar(float)
|
||||||
|
|
||||||
type fromDist =
|
type toScore = LogScore(genericDistOrScalar, option<genericDist>)
|
||||||
| ToFloat(toFloat)
|
|
||||||
| ToDist(toDist)
|
type fromFloat = [
|
||||||
| ToScore(toScore)
|
| #ToFloat(toFloat)
|
||||||
| ToDistCombination(direction, Operation.Algebraic.t, [#Dist(genericDist) | #Float(float)])
|
| #ToDist(toDist)
|
||||||
| ToString(toString)
|
| #ToDistCombination(direction, Operation.Algebraic.t, [#Dist(genericDist) | #Float(float)])
|
||||||
| ToBool(toBool)
|
| #ToString(toString)
|
||||||
|
| #ToBool(toBool)
|
||||||
|
]
|
||||||
|
|
||||||
|
type fromDist = [
|
||||||
|
| fromFloat
|
||||||
|
| #ToScore(toScore)
|
||||||
|
]
|
||||||
|
|
||||||
type singleParamaterFunction =
|
type singleParamaterFunction =
|
||||||
| FromDist(fromDist)
|
| FromDist(fromDist)
|
||||||
| FromFloat(fromDist)
|
| FromFloat(fromFloat)
|
||||||
|
|
||||||
type genericFunctionCallInfo =
|
type genericFunctionCallInfo =
|
||||||
| FromDist(fromDist, genericDist)
|
| FromDist(fromDist, genericDist)
|
||||||
| FromFloat(fromDist, float)
|
| FromFloat(fromFloat, float)
|
||||||
| FromSamples(array<float>)
|
| FromSamples(array<float>)
|
||||||
| Mixture(array<(genericDist, float)>)
|
| Mixture(array<(genericDist, float)>)
|
||||||
|
|
||||||
let distCallToString = (distFunction: fromDist): string =>
|
let floatCallToString = (floatFunction: fromFloat): string =>
|
||||||
switch distFunction {
|
switch floatFunction {
|
||||||
| ToFloat(#Cdf(r)) => `cdf(${E.Float.toFixed(r)})`
|
| #ToFloat(#Cdf(r)) => `cdf(${E.Float.toFixed(r)})`
|
||||||
| ToFloat(#Inv(r)) => `inv(${E.Float.toFixed(r)})`
|
| #ToFloat(#Inv(r)) => `inv(${E.Float.toFixed(r)})`
|
||||||
| ToFloat(#Mean) => `mean`
|
| #ToFloat(#Mean) => `mean`
|
||||||
| ToFloat(#Min) => `min`
|
| #ToFloat(#Min) => `min`
|
||||||
| ToFloat(#Max) => `max`
|
| #ToFloat(#Max) => `max`
|
||||||
| ToFloat(#Stdev) => `stdev`
|
| #ToFloat(#Stdev) => `stdev`
|
||||||
| ToFloat(#Variance) => `variance`
|
| #ToFloat(#Variance) => `variance`
|
||||||
| ToFloat(#Mode) => `mode`
|
| #ToFloat(#Mode) => `mode`
|
||||||
| ToFloat(#Pdf(r)) => `pdf(${E.Float.toFixed(r)})`
|
| #ToFloat(#Pdf(r)) => `pdf(${E.Float.toFixed(r)})`
|
||||||
| ToFloat(#Sample) => `sample`
|
| #ToFloat(#Sample) => `sample`
|
||||||
| ToFloat(#IntegralSum) => `integralSum`
|
| #ToFloat(#IntegralSum) => `integralSum`
|
||||||
| ToScore(KLDivergence(_)) => `klDivergence`
|
| #ToDist(Normalize) => `normalize`
|
||||||
| ToScore(LogScore(x, _)) => `logScore against ${E.Float.toFixed(x)}`
|
| #ToDist(ToPointSet) => `toPointSet`
|
||||||
| ToDist(Normalize) => `normalize`
|
| #ToDist(ToSampleSet(r)) => `toSampleSet(${E.I.toString(r)})`
|
||||||
| ToDist(ToPointSet) => `toPointSet`
|
| #ToDist(Truncate(_, _)) => `truncate`
|
||||||
| ToDist(ToSampleSet(r)) => `toSampleSet(${E.I.toString(r)})`
|
| #ToDist(Inspect) => `inspect`
|
||||||
| ToDist(Truncate(_, _)) => `truncate`
|
| #ToDist(Scale(#Power, r)) => `scalePower(${E.Float.toFixed(r)})`
|
||||||
| ToDist(Inspect) => `inspect`
|
| #ToDist(Scale(#Multiply, r)) => `scaleMultiply(${E.Float.toFixed(r)})`
|
||||||
| ToDist(Scale(#Power, r)) => `scalePower(${E.Float.toFixed(r)})`
|
| #ToDist(Scale(#Logarithm, r)) => `scaleLog(${E.Float.toFixed(r)})`
|
||||||
| ToDist(Scale(#Multiply, r)) => `scaleMultiply(${E.Float.toFixed(r)})`
|
| #ToDist(Scale(#LogarithmWithThreshold(eps), r)) =>
|
||||||
| ToDist(Scale(#Logarithm, r)) => `scaleLog(${E.Float.toFixed(r)})`
|
|
||||||
| ToDist(Scale(#LogarithmWithThreshold(eps), r)) =>
|
|
||||||
`scaleLogWithThreshold(${E.Float.toFixed(r)}, epsilon=${E.Float.toFixed(eps)})`
|
`scaleLogWithThreshold(${E.Float.toFixed(r)}, epsilon=${E.Float.toFixed(eps)})`
|
||||||
| ToString(ToString) => `toString`
|
| #ToString(ToString) => `toString`
|
||||||
| ToString(ToSparkline(n)) => `sparkline(${E.I.toString(n)})`
|
| #ToString(ToSparkline(n)) => `sparkline(${E.I.toString(n)})`
|
||||||
| ToBool(IsNormalized) => `isNormalized`
|
| #ToBool(IsNormalized) => `isNormalized`
|
||||||
| ToDistCombination(Algebraic(_), _, _) => `algebraic`
|
| #ToDistCombination(Algebraic(_), _, _) => `algebraic`
|
||||||
| ToDistCombination(Pointwise, _, _) => `pointwise`
|
| #ToDistCombination(Pointwise, _, _) => `pointwise`
|
||||||
|
}
|
||||||
|
|
||||||
|
let distCallToString = (
|
||||||
|
distFunction: [
|
||||||
|
| #ToFloat(toFloat)
|
||||||
|
| #ToDist(toDist)
|
||||||
|
| #ToDistCombination(direction, Operation.Algebraic.t, [#Dist(genericDist) | #Float(float)])
|
||||||
|
| #ToString(toString)
|
||||||
|
| #ToBool(toBool)
|
||||||
|
| #ToScore(toScore)
|
||||||
|
],
|
||||||
|
): string =>
|
||||||
|
switch distFunction {
|
||||||
|
| #ToScore(_) => `logScore`
|
||||||
|
| #ToFloat(x) => floatCallToString(#ToFloat(x))
|
||||||
|
| #ToDist(x) => floatCallToString(#ToDist(x))
|
||||||
|
| #ToString(x) => floatCallToString(#ToString(x))
|
||||||
|
| #ToBool(x) => floatCallToString(#ToBool(x))
|
||||||
|
| #ToDistCombination(x, y, z) => floatCallToString(#ToDistCombination(x, y, z))
|
||||||
}
|
}
|
||||||
|
|
||||||
let toString = (d: genericFunctionCallInfo): string =>
|
let toString = (d: genericFunctionCallInfo): string =>
|
||||||
switch d {
|
switch d {
|
||||||
| FromDist(f, _) | FromFloat(f, _) => distCallToString(f)
|
| FromDist(f, _) => distCallToString(f)
|
||||||
|
| FromFloat(f, _) => floatCallToString(f)
|
||||||
| Mixture(_) => `mixture`
|
| Mixture(_) => `mixture`
|
||||||
| FromSamples(_) => `fromSamples`
|
| FromSamples(_) => `fromSamples`
|
||||||
}
|
}
|
||||||
|
@ -162,80 +187,93 @@ module Constructors = {
|
||||||
|
|
||||||
module UsingDists = {
|
module UsingDists = {
|
||||||
@genType
|
@genType
|
||||||
let mean = (dist): t => FromDist(ToFloat(#Mean), dist)
|
let mean = (dist): t => FromDist(#ToFloat(#Mean), dist)
|
||||||
let stdev = (dist): t => FromDist(ToFloat(#Stdev), dist)
|
let stdev = (dist): t => FromDist(#ToFloat(#Stdev), dist)
|
||||||
let variance = (dist): t => FromDist(ToFloat(#Variance), dist)
|
let variance = (dist): t => FromDist(#ToFloat(#Variance), dist)
|
||||||
let sample = (dist): t => FromDist(ToFloat(#Sample), dist)
|
let sample = (dist): t => FromDist(#ToFloat(#Sample), dist)
|
||||||
let cdf = (dist, x): t => FromDist(ToFloat(#Cdf(x)), dist)
|
let cdf = (dist, x): t => FromDist(#ToFloat(#Cdf(x)), dist)
|
||||||
let inv = (dist, x): t => FromDist(ToFloat(#Inv(x)), dist)
|
let inv = (dist, x): t => FromDist(#ToFloat(#Inv(x)), dist)
|
||||||
let pdf = (dist, x): t => FromDist(ToFloat(#Pdf(x)), dist)
|
let pdf = (dist, x): t => FromDist(#ToFloat(#Pdf(x)), dist)
|
||||||
let normalize = (dist): t => FromDist(ToDist(Normalize), dist)
|
let normalize = (dist): t => FromDist(#ToDist(Normalize), dist)
|
||||||
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 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 klDivergence = (dist1, dist2): t => FromDist(ToScore(KLDivergence(dist2)), dist1)
|
module LogScore = {
|
||||||
let logScoreWithPointResolution = (~prediction, ~answer, ~prior): t => FromDist(
|
let distEstimateDistAnswer = (estimate, answer): t => FromDist(
|
||||||
ToScore(LogScore(answer, prior)),
|
#ToScore(LogScore(Score_Dist(answer), None)),
|
||||||
prediction,
|
estimate,
|
||||||
)
|
)
|
||||||
let scaleMultiply = (dist, n): t => FromDist(ToDist(Scale(#Multiply, n)), dist)
|
let distEstimateDistAnswerWithPrior = (estimate, answer, prior): t => FromDist(
|
||||||
let scalePower = (dist, n): t => FromDist(ToDist(Scale(#Power, n)), dist)
|
#ToScore(LogScore(Score_Dist(answer), Some(prior))),
|
||||||
let scaleLogarithm = (dist, n): t => FromDist(ToDist(Scale(#Logarithm, n)), dist)
|
estimate,
|
||||||
|
)
|
||||||
|
let distEstimateScalarAnswer = (estimate, answer): t => FromDist(
|
||||||
|
#ToScore(LogScore(Score_Scalar(answer), None)),
|
||||||
|
estimate,
|
||||||
|
)
|
||||||
|
let distEstimateScalarAnswerWithPrior = (estimate, answer, prior): t => FromDist(
|
||||||
|
#ToScore(LogScore(Score_Scalar(answer), Some(prior))),
|
||||||
|
estimate,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
let scaleMultiply = (dist, n): t => FromDist(#ToDist(Scale(#Multiply, n)), dist)
|
||||||
|
let scalePower = (dist, n): t => FromDist(#ToDist(Scale(#Power, n)), dist)
|
||||||
|
let scaleLogarithm = (dist, n): t => FromDist(#ToDist(Scale(#Logarithm, n)), dist)
|
||||||
let scaleLogarithmWithThreshold = (dist, n, eps): t => FromDist(
|
let scaleLogarithmWithThreshold = (dist, n, eps): t => FromDist(
|
||||||
ToDist(Scale(#LogarithmWithThreshold(eps), n)),
|
#ToDist(Scale(#LogarithmWithThreshold(eps), n)),
|
||||||
dist,
|
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(
|
||||||
ToDistCombination(Algebraic(AsDefault), #Add, #Dist(dist2)),
|
#ToDistCombination(Algebraic(AsDefault), #Add, #Dist(dist2)),
|
||||||
dist1,
|
dist1,
|
||||||
)
|
)
|
||||||
let algebraicMultiply = (dist1, dist2): t => FromDist(
|
let algebraicMultiply = (dist1, dist2): t => FromDist(
|
||||||
ToDistCombination(Algebraic(AsDefault), #Multiply, #Dist(dist2)),
|
#ToDistCombination(Algebraic(AsDefault), #Multiply, #Dist(dist2)),
|
||||||
dist1,
|
dist1,
|
||||||
)
|
)
|
||||||
let algebraicDivide = (dist1, dist2): t => FromDist(
|
let algebraicDivide = (dist1, dist2): t => FromDist(
|
||||||
ToDistCombination(Algebraic(AsDefault), #Divide, #Dist(dist2)),
|
#ToDistCombination(Algebraic(AsDefault), #Divide, #Dist(dist2)),
|
||||||
dist1,
|
dist1,
|
||||||
)
|
)
|
||||||
let algebraicSubtract = (dist1, dist2): t => FromDist(
|
let algebraicSubtract = (dist1, dist2): t => FromDist(
|
||||||
ToDistCombination(Algebraic(AsDefault), #Subtract, #Dist(dist2)),
|
#ToDistCombination(Algebraic(AsDefault), #Subtract, #Dist(dist2)),
|
||||||
dist1,
|
dist1,
|
||||||
)
|
)
|
||||||
let algebraicLogarithm = (dist1, dist2): t => FromDist(
|
let algebraicLogarithm = (dist1, dist2): t => FromDist(
|
||||||
ToDistCombination(Algebraic(AsDefault), #Logarithm, #Dist(dist2)),
|
#ToDistCombination(Algebraic(AsDefault), #Logarithm, #Dist(dist2)),
|
||||||
dist1,
|
dist1,
|
||||||
)
|
)
|
||||||
let algebraicPower = (dist1, dist2): t => FromDist(
|
let algebraicPower = (dist1, dist2): t => FromDist(
|
||||||
ToDistCombination(Algebraic(AsDefault), #Power, #Dist(dist2)),
|
#ToDistCombination(Algebraic(AsDefault), #Power, #Dist(dist2)),
|
||||||
dist1,
|
dist1,
|
||||||
)
|
)
|
||||||
let pointwiseAdd = (dist1, dist2): t => FromDist(
|
let pointwiseAdd = (dist1, dist2): t => FromDist(
|
||||||
ToDistCombination(Pointwise, #Add, #Dist(dist2)),
|
#ToDistCombination(Pointwise, #Add, #Dist(dist2)),
|
||||||
dist1,
|
dist1,
|
||||||
)
|
)
|
||||||
let pointwiseMultiply = (dist1, dist2): t => FromDist(
|
let pointwiseMultiply = (dist1, dist2): t => FromDist(
|
||||||
ToDistCombination(Pointwise, #Multiply, #Dist(dist2)),
|
#ToDistCombination(Pointwise, #Multiply, #Dist(dist2)),
|
||||||
dist1,
|
dist1,
|
||||||
)
|
)
|
||||||
let pointwiseDivide = (dist1, dist2): t => FromDist(
|
let pointwiseDivide = (dist1, dist2): t => FromDist(
|
||||||
ToDistCombination(Pointwise, #Divide, #Dist(dist2)),
|
#ToDistCombination(Pointwise, #Divide, #Dist(dist2)),
|
||||||
dist1,
|
dist1,
|
||||||
)
|
)
|
||||||
let pointwiseSubtract = (dist1, dist2): t => FromDist(
|
let pointwiseSubtract = (dist1, dist2): t => FromDist(
|
||||||
ToDistCombination(Pointwise, #Subtract, #Dist(dist2)),
|
#ToDistCombination(Pointwise, #Subtract, #Dist(dist2)),
|
||||||
dist1,
|
dist1,
|
||||||
)
|
)
|
||||||
let pointwiseLogarithm = (dist1, dist2): t => FromDist(
|
let pointwiseLogarithm = (dist1, dist2): t => FromDist(
|
||||||
ToDistCombination(Pointwise, #Logarithm, #Dist(dist2)),
|
#ToDistCombination(Pointwise, #Logarithm, #Dist(dist2)),
|
||||||
dist1,
|
dist1,
|
||||||
)
|
)
|
||||||
let pointwisePower = (dist1, dist2): t => FromDist(
|
let pointwisePower = (dist1, dist2): t => FromDist(
|
||||||
ToDistCombination(Pointwise, #Power, #Dist(dist2)),
|
#ToDistCombination(Pointwise, #Power, #Dist(dist2)),
|
||||||
dist1,
|
dist1,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,11 @@ type toSampleSetFn = t => result<SampleSetDist.t, error>
|
||||||
type scaleMultiplyFn = (t, float) => result<t, error>
|
type scaleMultiplyFn = (t, float) => result<t, error>
|
||||||
type pointwiseAddFn = (t, t) => result<t, error>
|
type pointwiseAddFn = (t, t) => result<t, error>
|
||||||
|
|
||||||
|
type env = {
|
||||||
|
sampleCount: int,
|
||||||
|
xyPointLength: int,
|
||||||
|
}
|
||||||
|
|
||||||
let isPointSet = (t: t) =>
|
let isPointSet = (t: t) =>
|
||||||
switch t {
|
switch t {
|
||||||
| PointSet(_) => true
|
| PointSet(_) => true
|
||||||
|
@ -61,46 +66,6 @@ let integralEndY = (t: t): float =>
|
||||||
|
|
||||||
let isNormalized = (t: t): bool => Js.Math.abs_float(integralEndY(t) -. 1.0) < 1e-7
|
let isNormalized = (t: t): bool => Js.Math.abs_float(integralEndY(t) -. 1.0) < 1e-7
|
||||||
|
|
||||||
module Score = {
|
|
||||||
let klDivergence = (prediction, answer, ~toPointSetFn: toPointSetFn): result<float, error> => {
|
|
||||||
let pointSets = E.R.merge(toPointSetFn(prediction), toPointSetFn(answer))
|
|
||||||
pointSets |> E.R2.bind(((predi, ans)) =>
|
|
||||||
PointSetDist.T.klDivergence(predi, ans)->E.R2.errMap(x => DistributionTypes.OperationError(x))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
let logScoreWithPointResolution = (
|
|
||||||
~prediction: DistributionTypes.genericDist,
|
|
||||||
~answer: float,
|
|
||||||
~prior: option<DistributionTypes.genericDist>,
|
|
||||||
~toPointSetFn: toPointSetFn,
|
|
||||||
): result<float, error> => {
|
|
||||||
switch prior {
|
|
||||||
| Some(prior') =>
|
|
||||||
E.R.merge(toPointSetFn(prior'), toPointSetFn(prediction))->E.R.bind(((
|
|
||||||
prior'',
|
|
||||||
prediction'',
|
|
||||||
)) =>
|
|
||||||
PointSetDist.T.logScoreWithPointResolution(
|
|
||||||
~prediction=prediction'',
|
|
||||||
~answer,
|
|
||||||
~prior=prior''->Some,
|
|
||||||
)->E.R2.errMap(x => DistributionTypes.OperationError(x))
|
|
||||||
)
|
|
||||||
| None =>
|
|
||||||
prediction
|
|
||||||
->toPointSetFn
|
|
||||||
->E.R.bind(x =>
|
|
||||||
PointSetDist.T.logScoreWithPointResolution(
|
|
||||||
~prediction=x,
|
|
||||||
~answer,
|
|
||||||
~prior=None,
|
|
||||||
)->E.R2.errMap(x => DistributionTypes.OperationError(x))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let toFloatOperation = (
|
let toFloatOperation = (
|
||||||
t,
|
t,
|
||||||
~toPointSetFn: toPointSetFn,
|
~toPointSetFn: toPointSetFn,
|
||||||
|
@ -171,6 +136,70 @@ let toPointSet = (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module Score = {
|
||||||
|
type genericDistOrScalar = DistributionTypes.DistributionOperation.genericDistOrScalar
|
||||||
|
|
||||||
|
let argsMake = (~esti: t, ~answ: genericDistOrScalar, ~prior: option<t>, ~env: env): result<
|
||||||
|
PointSetDist_Scoring.scoreArgs,
|
||||||
|
error,
|
||||||
|
> => {
|
||||||
|
let toPointSetFn = t =>
|
||||||
|
toPointSet(
|
||||||
|
t,
|
||||||
|
~xyPointLength=env.xyPointLength,
|
||||||
|
~sampleCount=env.sampleCount,
|
||||||
|
~xSelection=#ByWeight,
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
let prior': option<result<PointSetTypes.pointSetDist, error>> = switch prior {
|
||||||
|
| None => None
|
||||||
|
| Some(d) => toPointSetFn(d)->Some
|
||||||
|
}
|
||||||
|
let twoDists = (~toPointSetFn, esti': t, answ': t): result<
|
||||||
|
(PointSetTypes.pointSetDist, PointSetTypes.pointSetDist),
|
||||||
|
error,
|
||||||
|
> => E.R.merge(toPointSetFn(esti'), toPointSetFn(answ'))
|
||||||
|
switch (esti, answ, prior') {
|
||||||
|
| (esti', Score_Dist(answ'), None) =>
|
||||||
|
twoDists(~toPointSetFn, esti', answ')->E.R2.fmap(((esti'', answ'')) =>
|
||||||
|
{estimate: esti'', answer: answ'', prior: None}->PointSetDist_Scoring.DistAnswer
|
||||||
|
)
|
||||||
|
| (esti', Score_Dist(answ'), Some(Ok(prior''))) =>
|
||||||
|
twoDists(~toPointSetFn, esti', answ')->E.R2.fmap(((esti'', answ'')) =>
|
||||||
|
{
|
||||||
|
estimate: esti'',
|
||||||
|
answer: answ'',
|
||||||
|
prior: Some(prior''),
|
||||||
|
}->PointSetDist_Scoring.DistAnswer
|
||||||
|
)
|
||||||
|
| (esti', Score_Scalar(answ'), None) =>
|
||||||
|
toPointSetFn(esti')->E.R2.fmap(esti'' =>
|
||||||
|
{
|
||||||
|
estimate: esti'',
|
||||||
|
answer: answ',
|
||||||
|
prior: None,
|
||||||
|
}->PointSetDist_Scoring.ScalarAnswer
|
||||||
|
)
|
||||||
|
| (esti', Score_Scalar(answ'), Some(Ok(prior''))) =>
|
||||||
|
toPointSetFn(esti')->E.R2.fmap(esti'' =>
|
||||||
|
{
|
||||||
|
estimate: esti'',
|
||||||
|
answer: answ',
|
||||||
|
prior: Some(prior''),
|
||||||
|
}->PointSetDist_Scoring.ScalarAnswer
|
||||||
|
)
|
||||||
|
| (_, _, Some(Error(err))) => err->Error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let logScore = (~estimate: t, ~answer: genericDistOrScalar, ~prior: option<t>, ~env: env): result<
|
||||||
|
float,
|
||||||
|
error,
|
||||||
|
> =>
|
||||||
|
argsMake(~esti=estimate, ~answ=answer, ~prior, ~env)->E.R.bind(x =>
|
||||||
|
x->PointSetDist.logScore->E.R2.errMap(y => DistributionTypes.OperationError(y))
|
||||||
|
)
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
PointSetDist.toSparkline calls "downsampleEquallyOverX", which downsamples it to n=bucketCount.
|
PointSetDist.toSparkline calls "downsampleEquallyOverX", which downsamples it to n=bucketCount.
|
||||||
It first needs a pointSetDist, so we convert to a pointSetDist. In this process we want the
|
It first needs a pointSetDist, so we convert to a pointSetDist. In this process we want the
|
||||||
|
|
|
@ -5,6 +5,9 @@ type toSampleSetFn = t => result<SampleSetDist.t, error>
|
||||||
type scaleMultiplyFn = (t, float) => result<t, error>
|
type scaleMultiplyFn = (t, float) => result<t, error>
|
||||||
type pointwiseAddFn = (t, t) => result<t, error>
|
type pointwiseAddFn = (t, t) => result<t, error>
|
||||||
|
|
||||||
|
@genType
|
||||||
|
type env = {sampleCount: int, xyPointLength: int}
|
||||||
|
|
||||||
let sampleN: (t, int) => array<float>
|
let sampleN: (t, int) => array<float>
|
||||||
let sample: t => float
|
let sample: t => float
|
||||||
|
|
||||||
|
@ -25,12 +28,11 @@ let toFloatOperation: (
|
||||||
) => result<float, error>
|
) => result<float, error>
|
||||||
|
|
||||||
module Score: {
|
module Score: {
|
||||||
let klDivergence: (t, t, ~toPointSetFn: toPointSetFn) => result<float, error>
|
let logScore: (
|
||||||
let logScoreWithPointResolution: (
|
~estimate: t,
|
||||||
~prediction: t,
|
~answer: DistributionTypes.DistributionOperation.genericDistOrScalar,
|
||||||
~answer: float,
|
|
||||||
~prior: option<t>,
|
~prior: option<t>,
|
||||||
~toPointSetFn: toPointSetFn,
|
~env: env,
|
||||||
) => result<float, error>
|
) => result<float, error>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -120,7 +120,7 @@ let combinePointwise = (
|
||||||
|
|
||||||
let interpolator = XYShape.XtoY.continuousInterpolator(t1.interpolation, extrapolation)
|
let interpolator = XYShape.XtoY.continuousInterpolator(t1.interpolation, extrapolation)
|
||||||
|
|
||||||
combiner(fn, interpolator, t1.xyShape, t2.xyShape)->E.R2.fmap(x =>
|
combiner(interpolator, fn, t1.xyShape, t2.xyShape)->E.R2.fmap(x =>
|
||||||
make(~integralSumCache=combinedIntegralSum, x)
|
make(~integralSumCache=combinedIntegralSum, x)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -270,20 +270,6 @@ module T = Dist({
|
||||||
}
|
}
|
||||||
let variance = (t: t): float =>
|
let variance = (t: t): float =>
|
||||||
XYShape.Analysis.getVarianceDangerously(t, mean, Analysis.getMeanOfSquares)
|
XYShape.Analysis.getVarianceDangerously(t, mean, Analysis.getMeanOfSquares)
|
||||||
|
|
||||||
let klDivergence = (prediction: t, answer: t) => {
|
|
||||||
let newShape = XYShape.PointwiseCombination.combineAlongSupportOfSecondArgument(
|
|
||||||
PointSetDist_Scoring.KLDivergence.integrand,
|
|
||||||
prediction.xyShape,
|
|
||||||
answer.xyShape,
|
|
||||||
)
|
|
||||||
newShape->E.R2.fmap(x => x->make->integralEndY)
|
|
||||||
}
|
|
||||||
let logScoreWithPointResolution = (~prediction: t, ~answer: float, ~prior: option<t>) => {
|
|
||||||
let priorPdf = prior->E.O2.fmap((shape, x) => XYShape.XtoY.linear(x, shape.xyShape))
|
|
||||||
let predictionPdf = x => XYShape.XtoY.linear(x, prediction.xyShape)
|
|
||||||
PointSetDist_Scoring.LogScoreWithPointResolution.score(~priorPdf, ~predictionPdf, ~answer)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
let isNormalized = (t: t): bool => {
|
let isNormalized = (t: t): bool => {
|
||||||
|
|
|
@ -49,7 +49,7 @@ let combinePointwise = (
|
||||||
// TODO: does it ever make sense to pointwise combine the integrals here?
|
// TODO: does it ever make sense to pointwise combine the integrals here?
|
||||||
// It could be done for pointwise additions, but is that ever needed?
|
// It could be done for pointwise additions, but is that ever needed?
|
||||||
|
|
||||||
combiner(fn, XYShape.XtoY.discreteInterpolator, t1.xyShape, t2.xyShape)->E.R2.fmap(make)
|
combiner(XYShape.XtoY.discreteInterpolator, fn, t1.xyShape, t2.xyShape)->E.R2.fmap(make)
|
||||||
}
|
}
|
||||||
|
|
||||||
let reduce = (
|
let reduce = (
|
||||||
|
@ -222,15 +222,4 @@ module T = Dist({
|
||||||
let getMeanOfSquares = t => t |> shapeMap(XYShape.T.square) |> mean
|
let getMeanOfSquares = t => t |> shapeMap(XYShape.T.square) |> mean
|
||||||
XYShape.Analysis.getVarianceDangerously(t, mean, getMeanOfSquares)
|
XYShape.Analysis.getVarianceDangerously(t, mean, getMeanOfSquares)
|
||||||
}
|
}
|
||||||
|
|
||||||
let klDivergence = (prediction: t, answer: t) => {
|
|
||||||
combinePointwise(
|
|
||||||
~fn=PointSetDist_Scoring.KLDivergence.integrand,
|
|
||||||
prediction,
|
|
||||||
answer,
|
|
||||||
)->E.R2.fmap(integralEndY)
|
|
||||||
}
|
|
||||||
let logScoreWithPointResolution = (~prediction: t, ~answer: float, ~prior: option<t>) => {
|
|
||||||
Error(Operation.NotYetImplemented)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -33,12 +33,6 @@ module type dist = {
|
||||||
|
|
||||||
let mean: t => float
|
let mean: t => float
|
||||||
let variance: t => float
|
let variance: t => float
|
||||||
let klDivergence: (t, t) => result<float, Operation.Error.t>
|
|
||||||
let logScoreWithPointResolution: (
|
|
||||||
~prediction: t,
|
|
||||||
~answer: float,
|
|
||||||
~prior: option<t>,
|
|
||||||
) => result<float, Operation.Error.t>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module Dist = (T: dist) => {
|
module Dist = (T: dist) => {
|
||||||
|
@ -61,9 +55,6 @@ module Dist = (T: dist) => {
|
||||||
let mean = T.mean
|
let mean = T.mean
|
||||||
let variance = T.variance
|
let variance = T.variance
|
||||||
let integralEndY = T.integralEndY
|
let integralEndY = T.integralEndY
|
||||||
let klDivergence = T.klDivergence
|
|
||||||
let logScoreWithPointResolution = T.logScoreWithPointResolution
|
|
||||||
|
|
||||||
let updateIntegralCache = T.updateIntegralCache
|
let updateIntegralCache = T.updateIntegralCache
|
||||||
|
|
||||||
module Integral = {
|
module Integral = {
|
||||||
|
|
|
@ -302,15 +302,6 @@ module T = Dist({
|
||||||
| _ => XYShape.Analysis.getVarianceDangerously(t, mean, getMeanOfSquares)
|
| _ => XYShape.Analysis.getVarianceDangerously(t, mean, getMeanOfSquares)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let klDivergence = (prediction: t, answer: t) => {
|
|
||||||
let klDiscretePart = Discrete.T.klDivergence(prediction.discrete, answer.discrete)
|
|
||||||
let klContinuousPart = Continuous.T.klDivergence(prediction.continuous, answer.continuous)
|
|
||||||
E.R.merge(klDiscretePart, klContinuousPart)->E.R2.fmap(t => fst(t) +. snd(t))
|
|
||||||
}
|
|
||||||
let logScoreWithPointResolution = (~prediction: t, ~answer: float, ~prior: option<t>) => {
|
|
||||||
Error(Operation.NotYetImplemented)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
let combineAlgebraically = (op: Operation.convolutionOperation, t1: t, t2: t): t => {
|
let combineAlgebraically = (op: Operation.convolutionOperation, t1: t, t2: t): t => {
|
||||||
|
|
|
@ -66,6 +66,7 @@ let combineAlgebraically = (op: Operation.convolutionOperation, t1: t, t2: t): t
|
||||||
}
|
}
|
||||||
|
|
||||||
let combinePointwise = (
|
let combinePointwise = (
|
||||||
|
~combiner=XYShape.PointwiseCombination.combine,
|
||||||
~integralSumCachesFn: (float, float) => option<float>=(_, _) => None,
|
~integralSumCachesFn: (float, float) => option<float>=(_, _) => None,
|
||||||
~integralCachesFn: (
|
~integralCachesFn: (
|
||||||
PointSetTypes.continuousShape,
|
PointSetTypes.continuousShape,
|
||||||
|
@ -78,6 +79,7 @@ let combinePointwise = (
|
||||||
switch (t1, t2) {
|
switch (t1, t2) {
|
||||||
| (Continuous(m1), Continuous(m2)) =>
|
| (Continuous(m1), Continuous(m2)) =>
|
||||||
Continuous.combinePointwise(
|
Continuous.combinePointwise(
|
||||||
|
~combiner,
|
||||||
~integralSumCachesFn,
|
~integralSumCachesFn,
|
||||||
fn,
|
fn,
|
||||||
m1,
|
m1,
|
||||||
|
@ -85,6 +87,7 @@ let combinePointwise = (
|
||||||
)->E.R2.fmap(x => PointSetTypes.Continuous(x))
|
)->E.R2.fmap(x => PointSetTypes.Continuous(x))
|
||||||
| (Discrete(m1), Discrete(m2)) =>
|
| (Discrete(m1), Discrete(m2)) =>
|
||||||
Discrete.combinePointwise(
|
Discrete.combinePointwise(
|
||||||
|
~combiner,
|
||||||
~integralSumCachesFn,
|
~integralSumCachesFn,
|
||||||
~fn,
|
~fn,
|
||||||
m1,
|
m1,
|
||||||
|
@ -195,25 +198,16 @@ module T = Dist({
|
||||||
| Discrete(m) => Discrete.T.variance(m)
|
| Discrete(m) => Discrete.T.variance(m)
|
||||||
| Continuous(m) => Continuous.T.variance(m)
|
| Continuous(m) => Continuous.T.variance(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
let klDivergence = (prediction: t, answer: t) =>
|
|
||||||
switch (prediction, answer) {
|
|
||||||
| (Continuous(t1), Continuous(t2)) => Continuous.T.klDivergence(t1, t2)
|
|
||||||
| (Discrete(t1), Discrete(t2)) => Discrete.T.klDivergence(t1, t2)
|
|
||||||
| (m1, m2) => Mixed.T.klDivergence(m1->toMixed, m2->toMixed)
|
|
||||||
}
|
|
||||||
|
|
||||||
let logScoreWithPointResolution = (~prediction: t, ~answer: float, ~prior: option<t>) => {
|
|
||||||
switch (prior, prediction) {
|
|
||||||
| (Some(Continuous(t1)), Continuous(t2)) =>
|
|
||||||
Continuous.T.logScoreWithPointResolution(~prediction=t2, ~answer, ~prior=t1->Some)
|
|
||||||
| (None, Continuous(t2)) =>
|
|
||||||
Continuous.T.logScoreWithPointResolution(~prediction=t2, ~answer, ~prior=None)
|
|
||||||
| _ => Error(Operation.NotYetImplemented)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let logScore = (args: PointSetDist_Scoring.scoreArgs): result<float, Operation.Error.t> =>
|
||||||
|
PointSetDist_Scoring.logScore(
|
||||||
|
args,
|
||||||
|
~combineFn=combinePointwise,
|
||||||
|
~integrateFn=T.Integral.sum,
|
||||||
|
~toMixedFn=toMixed,
|
||||||
|
)
|
||||||
|
|
||||||
let pdf = (f: float, t: t) => {
|
let pdf = (f: float, t: t) => {
|
||||||
let mixedPoint: PointSetTypes.mixedPoint = T.xToY(f, t)
|
let mixedPoint: PointSetTypes.mixedPoint = T.xToY(f, t)
|
||||||
mixedPoint.continuous +. mixedPoint.discrete
|
mixedPoint.continuous +. mixedPoint.discrete
|
||||||
|
|
|
@ -1,46 +1,149 @@
|
||||||
module KLDivergence = {
|
type pointSetDist = PointSetTypes.pointSetDist
|
||||||
|
|
||||||
|
type scalar = float
|
||||||
|
type score = float
|
||||||
|
type abstractScoreArgs<'a, 'b> = {estimate: 'a, answer: 'b, prior: option<'a>}
|
||||||
|
type scoreArgs =
|
||||||
|
| DistAnswer(abstractScoreArgs<pointSetDist, pointSetDist>)
|
||||||
|
| ScalarAnswer(abstractScoreArgs<pointSetDist, scalar>)
|
||||||
|
|
||||||
let logFn = Js.Math.log // base e
|
let logFn = Js.Math.log // base e
|
||||||
let integrand = (predictionElement: float, answerElement: float): result<
|
let minusScaledLogOfQuotient = (~esti, ~answ): result<float, Operation.Error.t> => {
|
||||||
|
let quot = esti /. answ
|
||||||
|
quot < 0.0 ? Error(Operation.ComplexNumberError) : Ok(-.answ *. logFn(quot))
|
||||||
|
}
|
||||||
|
|
||||||
|
module WithDistAnswer = {
|
||||||
|
// The Kullback-Leibler divergence
|
||||||
|
let integrand = (estimateElement: float, answerElement: float): result<
|
||||||
float,
|
float,
|
||||||
Operation.Error.t,
|
Operation.Error.t,
|
||||||
> =>
|
> =>
|
||||||
// We decided that negative infinity, not an error at answerElement = 0.0, is a desirable value.
|
// We decided that 0.0, not an error at answerElement = 0.0, is a desirable value.
|
||||||
if answerElement == 0.0 {
|
if answerElement == 0.0 {
|
||||||
Ok(0.0)
|
Ok(0.0)
|
||||||
} else if predictionElement == 0.0 {
|
} else if estimateElement == 0.0 {
|
||||||
Ok(infinity)
|
Ok(infinity)
|
||||||
} else {
|
} else {
|
||||||
let quot = predictionElement /. answerElement
|
minusScaledLogOfQuotient(~esti=estimateElement, ~answ=answerElement)
|
||||||
quot < 0.0 ? Error(Operation.ComplexNumberError) : Ok(-.answerElement *. logFn(quot))
|
}
|
||||||
|
|
||||||
|
let sum = (
|
||||||
|
~estimate: pointSetDist,
|
||||||
|
~answer: pointSetDist,
|
||||||
|
~combineFn,
|
||||||
|
~integrateFn,
|
||||||
|
~toMixedFn,
|
||||||
|
): result<score, Operation.Error.t> => {
|
||||||
|
let combineAndIntegrate = (estimate, answer) =>
|
||||||
|
combineFn(integrand, estimate, answer)->E.R2.fmap(integrateFn)
|
||||||
|
|
||||||
|
let getMixedSums = (estimate: pointSetDist, answer: pointSetDist) => {
|
||||||
|
let esti = estimate->toMixedFn
|
||||||
|
let answ = answer->toMixedFn
|
||||||
|
switch (
|
||||||
|
Mixed.T.toContinuous(esti),
|
||||||
|
Mixed.T.toDiscrete(esti),
|
||||||
|
Mixed.T.toContinuous(answ),
|
||||||
|
Mixed.T.toDiscrete(answ),
|
||||||
|
) {
|
||||||
|
| (
|
||||||
|
Some(estiContinuousPart),
|
||||||
|
Some(estiDiscretePart),
|
||||||
|
Some(answContinuousPart),
|
||||||
|
Some(answDiscretePart),
|
||||||
|
) =>
|
||||||
|
E.R.merge(
|
||||||
|
combineAndIntegrate(
|
||||||
|
PointSetTypes.Discrete(estiDiscretePart),
|
||||||
|
PointSetTypes.Discrete(answDiscretePart),
|
||||||
|
),
|
||||||
|
combineAndIntegrate(Continuous(estiContinuousPart), Continuous(answContinuousPart)),
|
||||||
|
)
|
||||||
|
| (_, _, _, _) => `unreachable state`->Operation.Other->Error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module LogScoreWithPointResolution = {
|
switch (estimate, answer) {
|
||||||
let logFn = Js.Math.log
|
| (Continuous(_), Continuous(_))
|
||||||
let score = (
|
| (Discrete(_), Discrete(_)) =>
|
||||||
~priorPdf: option<float => float>,
|
combineAndIntegrate(estimate, answer)
|
||||||
~predictionPdf: float => float,
|
| (_, _) =>
|
||||||
~answer: float,
|
getMixedSums(estimate, answer)->E.R2.fmap(((discretePart, continuousPart)) =>
|
||||||
): result<float, Operation.Error.t> => {
|
discretePart +. continuousPart
|
||||||
let numerator = answer->predictionPdf
|
)
|
||||||
if numerator < 0.0 {
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let sumWithPrior = (
|
||||||
|
~estimate: pointSetDist,
|
||||||
|
~answer: pointSetDist,
|
||||||
|
~prior: pointSetDist,
|
||||||
|
~combineFn,
|
||||||
|
~integrateFn,
|
||||||
|
~toMixedFn,
|
||||||
|
): result<score, Operation.Error.t> => {
|
||||||
|
let kl1 = sum(~estimate, ~answer, ~combineFn, ~integrateFn, ~toMixedFn)
|
||||||
|
let kl2 = sum(~estimate=prior, ~answer, ~combineFn, ~integrateFn, ~toMixedFn)
|
||||||
|
E.R.merge(kl1, kl2)->E.R2.fmap(((kl1', kl2')) => kl1' -. kl2')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module WithScalarAnswer = {
|
||||||
|
let sum = (mp: PointSetTypes.MixedPoint.t): float => mp.continuous +. mp.discrete
|
||||||
|
let score = (~estimate: pointSetDist, ~answer: scalar): result<score, Operation.Error.t> => {
|
||||||
|
let _score = (~estimatePdf: float => option<float>, ~answer: float): result<
|
||||||
|
score,
|
||||||
|
Operation.Error.t,
|
||||||
|
> => {
|
||||||
|
let density = answer->estimatePdf
|
||||||
|
switch density {
|
||||||
|
| None => Operation.PdfInvalidError->Error
|
||||||
|
| Some(density') =>
|
||||||
|
if density' < 0.0 {
|
||||||
Operation.PdfInvalidError->Error
|
Operation.PdfInvalidError->Error
|
||||||
} else if numerator == 0.0 {
|
} else if density' == 0.0 {
|
||||||
infinity->Ok
|
infinity->Ok
|
||||||
} else {
|
} else {
|
||||||
-.(
|
density'->logFn->(x => -.x)->Ok
|
||||||
switch priorPdf {
|
|
||||||
| None => numerator->logFn
|
|
||||||
| Some(f) => {
|
|
||||||
let priorDensityOfAnswer = f(answer)
|
|
||||||
if priorDensityOfAnswer == 0.0 {
|
|
||||||
neg_infinity
|
|
||||||
} else {
|
|
||||||
(numerator /. priorDensityOfAnswer)->logFn
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)->Ok
|
|
||||||
|
let estimatePdf = x =>
|
||||||
|
switch estimate {
|
||||||
|
| Continuous(esti) => Continuous.T.xToY(x, esti)->sum->Some
|
||||||
|
| Discrete(esti) => Discrete.T.xToY(x, esti)->sum->Some
|
||||||
|
| Mixed(_) => None
|
||||||
|
}
|
||||||
|
_score(~estimatePdf, ~answer)
|
||||||
|
}
|
||||||
|
|
||||||
|
let scoreWithPrior = (~estimate: pointSetDist, ~answer: scalar, ~prior: pointSetDist): result<
|
||||||
|
score,
|
||||||
|
Operation.Error.t,
|
||||||
|
> => {
|
||||||
|
E.R.merge(score(~estimate, ~answer), score(~estimate=prior, ~answer))->E.R2.fmap(((s1, s2)) =>
|
||||||
|
s1 -. s2
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let twoGenericDistsToTwoPointSetDists = (~toPointSetFn, estimate, answer): result<
|
||||||
|
(pointSetDist, pointSetDist),
|
||||||
|
'e,
|
||||||
|
> => E.R.merge(toPointSetFn(estimate, ()), toPointSetFn(answer, ()))
|
||||||
|
|
||||||
|
let logScore = (args: scoreArgs, ~combineFn, ~integrateFn, ~toMixedFn): result<
|
||||||
|
score,
|
||||||
|
Operation.Error.t,
|
||||||
|
> =>
|
||||||
|
switch args {
|
||||||
|
| DistAnswer({estimate, answer, prior: None}) =>
|
||||||
|
WithDistAnswer.sum(~estimate, ~answer, ~integrateFn, ~combineFn, ~toMixedFn)
|
||||||
|
| DistAnswer({estimate, answer, prior: Some(prior)}) =>
|
||||||
|
WithDistAnswer.sumWithPrior(~estimate, ~answer, ~prior, ~integrateFn, ~combineFn, ~toMixedFn)
|
||||||
|
| ScalarAnswer({estimate, answer, prior: None}) => WithScalarAnswer.score(~estimate, ~answer)
|
||||||
|
| ScalarAnswer({estimate, answer, prior: Some(prior)}) =>
|
||||||
|
WithScalarAnswer.scoreWithPrior(~estimate, ~answer, ~prior)
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,6 +117,11 @@ let map3 = (
|
||||||
): result<t, sampleSetError> =>
|
): result<t, sampleSetError> =>
|
||||||
E.A.zip3(get(t1), get(t2), get(t3))->E.A2.fmap(E.Tuple3.toFnCall(fn))->_fromSampleResultArray
|
E.A.zip3(get(t1), get(t2), get(t3))->E.A2.fmap(E.Tuple3.toFnCall(fn))->_fromSampleResultArray
|
||||||
|
|
||||||
|
let mapN = (~fn: array<float> => result<float, Operation.Error.t>, ~t1: array<t>): result<
|
||||||
|
t,
|
||||||
|
sampleSetError,
|
||||||
|
> => E.A.transpose(E.A.fmap(get, t1))->E.A2.fmap(fn)->_fromSampleResultArray
|
||||||
|
|
||||||
let mean = t => T.get(t)->E.A.Floats.mean
|
let mean = t => T.get(t)->E.A.Floats.mean
|
||||||
let geomean = t => T.get(t)->E.A.Floats.geomean
|
let geomean = t => T.get(t)->E.A.Floats.geomean
|
||||||
let mode = t => T.get(t)->E.A.Floats.mode
|
let mode = t => T.get(t)->E.A.Floats.mode
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
type internalExpressionValue = ReducerInterface_InternalExpressionValue.t
|
type internalExpressionValue = ReducerInterface_InternalExpressionValue.t
|
||||||
|
type internalExpressionValueType = ReducerInterface_InternalExpressionValue.internalExpressionValueType
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Function Registry "Type". A type, without any other information.
|
Function Registry "Type". A type, without any other information.
|
||||||
|
@ -8,6 +9,7 @@ type rec frType =
|
||||||
| FRTypeNumber
|
| FRTypeNumber
|
||||||
| FRTypeNumeric
|
| FRTypeNumeric
|
||||||
| FRTypeDistOrNumber
|
| FRTypeDistOrNumber
|
||||||
|
| FRTypeDist
|
||||||
| FRTypeLambda
|
| FRTypeLambda
|
||||||
| FRTypeRecord(frTypeRecord)
|
| FRTypeRecord(frTypeRecord)
|
||||||
| FRTypeDict(frType)
|
| FRTypeDict(frType)
|
||||||
|
@ -41,18 +43,26 @@ and frValueDistOrNumber = FRValueNumber(float) | FRValueDist(DistributionTypes.g
|
||||||
type fnDefinition = {
|
type fnDefinition = {
|
||||||
name: string,
|
name: string,
|
||||||
inputs: array<frType>,
|
inputs: array<frType>,
|
||||||
run: (array<frValue>, DistributionOperation.env) => result<internalExpressionValue, string>,
|
run: (
|
||||||
|
array<internalExpressionValue>,
|
||||||
|
array<frValue>,
|
||||||
|
GenericDist.env,
|
||||||
|
) => result<internalExpressionValue, string>,
|
||||||
}
|
}
|
||||||
|
|
||||||
type function = {
|
type function = {
|
||||||
name: string,
|
name: string,
|
||||||
definitions: array<fnDefinition>,
|
definitions: array<fnDefinition>,
|
||||||
examples: option<string>,
|
requiresNamespace: bool,
|
||||||
|
nameSpace: string,
|
||||||
|
output: option<internalExpressionValueType>,
|
||||||
|
examples: array<string>,
|
||||||
description: option<string>,
|
description: option<string>,
|
||||||
isExperimental: bool,
|
isExperimental: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
type registry = array<function>
|
type fnNameDict = Js.Dict.t<array<function>>
|
||||||
|
type registry = {functions: array<function>, fnNameDict: fnNameDict}
|
||||||
|
|
||||||
module FRType = {
|
module FRType = {
|
||||||
type t = frType
|
type t = frType
|
||||||
|
@ -60,6 +70,7 @@ module FRType = {
|
||||||
switch t {
|
switch t {
|
||||||
| FRTypeNumber => "number"
|
| FRTypeNumber => "number"
|
||||||
| FRTypeNumeric => "numeric"
|
| FRTypeNumeric => "numeric"
|
||||||
|
| FRTypeDist => "distribution"
|
||||||
| FRTypeDistOrNumber => "distribution|number"
|
| FRTypeDistOrNumber => "distribution|number"
|
||||||
| FRTypeRecord(r) => {
|
| FRTypeRecord(r) => {
|
||||||
let input = ((name, frType): frTypeRecordParam) => `${name}: ${toString(frType)}`
|
let input = ((name, frType): frTypeRecordParam) => `${name}: ${toString(frType)}`
|
||||||
|
@ -98,6 +109,7 @@ module FRType = {
|
||||||
| (FRTypeDistOrNumber, IEvDistribution(Symbolic(#Float(f)))) =>
|
| (FRTypeDistOrNumber, IEvDistribution(Symbolic(#Float(f)))) =>
|
||||||
Some(FRValueDistOrNumber(FRValueNumber(f)))
|
Some(FRValueDistOrNumber(FRValueNumber(f)))
|
||||||
| (FRTypeDistOrNumber, IEvDistribution(f)) => Some(FRValueDistOrNumber(FRValueDist(f)))
|
| (FRTypeDistOrNumber, IEvDistribution(f)) => Some(FRValueDistOrNumber(FRValueDist(f)))
|
||||||
|
| (FRTypeDist, IEvDistribution(f)) => Some(FRValueDist(f))
|
||||||
| (FRTypeNumeric, IEvNumber(f)) => Some(FRValueNumber(f))
|
| (FRTypeNumeric, IEvNumber(f)) => Some(FRValueNumber(f))
|
||||||
| (FRTypeNumeric, IEvDistribution(Symbolic(#Float(f)))) => Some(FRValueNumber(f))
|
| (FRTypeNumeric, IEvDistribution(Symbolic(#Float(f)))) => Some(FRValueNumber(f))
|
||||||
| (FRTypeLambda, IEvLambda(f)) => Some(FRValueLambda(f))
|
| (FRTypeLambda, IEvLambda(f)) => Some(FRValueLambda(f))
|
||||||
|
@ -262,7 +274,7 @@ module Matcher = {
|
||||||
|
|
||||||
module Registry = {
|
module Registry = {
|
||||||
let _findExactMatches = (r: registry, fnName: string, args: array<internalExpressionValue>) => {
|
let _findExactMatches = (r: registry, fnName: string, args: array<internalExpressionValue>) => {
|
||||||
let functionMatchPairs = r->E.A2.fmap(l => (l, Function.match(l, fnName, args)))
|
let functionMatchPairs = r.functions->E.A2.fmap(l => (l, Function.match(l, fnName, args)))
|
||||||
let fullMatch = functionMatchPairs->E.A.getBy(((_, match)) => Match.isFullMatch(match))
|
let fullMatch = functionMatchPairs->E.A.getBy(((_, match)) => Match.isFullMatch(match))
|
||||||
fullMatch->E.O.bind(((fn, match)) =>
|
fullMatch->E.O.bind(((fn, match)) =>
|
||||||
switch match {
|
switch match {
|
||||||
|
@ -273,7 +285,7 @@ module Matcher = {
|
||||||
}
|
}
|
||||||
|
|
||||||
let _findNameMatches = (r: registry, fnName: string, args: array<internalExpressionValue>) => {
|
let _findNameMatches = (r: registry, fnName: string, args: array<internalExpressionValue>) => {
|
||||||
let functionMatchPairs = r->E.A2.fmap(l => (l, Function.match(l, fnName, args)))
|
let functionMatchPairs = r.functions->E.A2.fmap(l => (l, Function.match(l, fnName, args)))
|
||||||
let getNameMatches =
|
let getNameMatches =
|
||||||
functionMatchPairs
|
functionMatchPairs
|
||||||
->E.A2.fmap(((fn, match)) => Match.isNameMatchOnly(match) ? Some((fn, match)) : None)
|
->E.A2.fmap(((fn, match)) => Match.isNameMatchOnly(match) ? Some((fn, match)) : None)
|
||||||
|
@ -292,10 +304,13 @@ module Matcher = {
|
||||||
}
|
}
|
||||||
|
|
||||||
let findMatches = (r: registry, fnName: string, args: array<internalExpressionValue>) => {
|
let findMatches = (r: registry, fnName: string, args: array<internalExpressionValue>) => {
|
||||||
switch _findExactMatches(r, fnName, args) {
|
let fnNameInParts = Js.String.split(".", fnName)
|
||||||
|
let fnToSearch = E.A.get(fnNameInParts, 1) |> E.O.default(fnNameInParts[0])
|
||||||
|
|
||||||
|
switch _findExactMatches(r, fnToSearch, args) {
|
||||||
| Some(r) => Match.FullMatch(r)
|
| Some(r) => Match.FullMatch(r)
|
||||||
| None =>
|
| None =>
|
||||||
switch _findNameMatches(r, fnName, args) {
|
switch _findNameMatches(r, fnToSearch, args) {
|
||||||
| Some(r) => Match.SameNameDifferentArguments(r)
|
| Some(r) => Match.SameNameDifferentArguments(r)
|
||||||
| None => Match.DifferentName
|
| None => Match.DifferentName
|
||||||
}
|
}
|
||||||
|
@ -305,7 +320,7 @@ module Matcher = {
|
||||||
let matchToDef = (registry: registry, {fnName, inputIndex}: RegistryMatch.match): option<
|
let matchToDef = (registry: registry, {fnName, inputIndex}: RegistryMatch.match): option<
|
||||||
fnDefinition,
|
fnDefinition,
|
||||||
> =>
|
> =>
|
||||||
registry
|
registry.functions
|
||||||
->E.A.getBy(fn => fn.name === fnName)
|
->E.A.getBy(fn => fn.name === fnName)
|
||||||
->E.O.bind(fn => E.A.get(fn.definitions, inputIndex))
|
->E.O.bind(fn => E.A.get(fn.definitions, inputIndex))
|
||||||
}
|
}
|
||||||
|
@ -319,15 +334,23 @@ module FnDefinition = {
|
||||||
t.name ++ `(${inputs})`
|
t.name ++ `(${inputs})`
|
||||||
}
|
}
|
||||||
|
|
||||||
let run = (t: t, args: array<internalExpressionValue>, env: DistributionOperation.env) => {
|
let isMatch = (t: t, args: array<internalExpressionValue>) => {
|
||||||
let argValues = FRType.matchWithExpressionValueArray(t.inputs, args)
|
let argValues = FRType.matchWithExpressionValueArray(t.inputs, args)
|
||||||
switch argValues {
|
switch argValues {
|
||||||
| Some(values) => t.run(values, env)
|
| Some(_) => true
|
||||||
|
| None => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let run = (t: t, args: array<internalExpressionValue>, env: GenericDist.env) => {
|
||||||
|
let argValues = FRType.matchWithExpressionValueArray(t.inputs, args)
|
||||||
|
switch argValues {
|
||||||
|
| Some(values) => t.run(args, values, env)
|
||||||
| None => Error("Incorrect Types")
|
| None => Error("Incorrect Types")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let make = (~name, ~inputs, ~run): t => {
|
let make = (~name, ~inputs, ~run, ()): t => {
|
||||||
name: name,
|
name: name,
|
||||||
inputs: inputs,
|
inputs: inputs,
|
||||||
run: run,
|
run: run,
|
||||||
|
@ -340,16 +363,29 @@ module Function = {
|
||||||
type functionJson = {
|
type functionJson = {
|
||||||
name: string,
|
name: string,
|
||||||
definitions: array<string>,
|
definitions: array<string>,
|
||||||
examples: option<string>,
|
examples: array<string>,
|
||||||
description: option<string>,
|
description: option<string>,
|
||||||
isExperimental: bool,
|
isExperimental: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
let make = (~name, ~definitions, ~examples=?, ~description=?, ~isExperimental=false, ()): t => {
|
let make = (
|
||||||
|
~name,
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~definitions,
|
||||||
|
~examples=?,
|
||||||
|
~output=?,
|
||||||
|
~description=?,
|
||||||
|
~isExperimental=false,
|
||||||
|
(),
|
||||||
|
): t => {
|
||||||
name: name,
|
name: name,
|
||||||
|
nameSpace: nameSpace,
|
||||||
definitions: definitions,
|
definitions: definitions,
|
||||||
examples: examples,
|
output: output,
|
||||||
|
examples: examples |> E.O.default([]),
|
||||||
isExperimental: isExperimental,
|
isExperimental: isExperimental,
|
||||||
|
requiresNamespace: requiresNamespace,
|
||||||
description: description,
|
description: description,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -362,22 +398,64 @@ module Function = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module NameSpace = {
|
||||||
|
type t = {name: string, functions: array<function>}
|
||||||
|
let definitions = (t: t) => t.functions->E.A2.fmap(f => f.definitions)->E.A.concatMany
|
||||||
|
let uniqueFnNames = (t: t) => definitions(t)->E.A2.fmap(r => r.name)->E.A.uniq
|
||||||
|
let nameToDefinitions = (t: t, name: string) => definitions(t)->E.A2.filter(r => r.name == name)
|
||||||
|
}
|
||||||
|
|
||||||
module Registry = {
|
module Registry = {
|
||||||
let toJson = (r: registry) => r->E.A2.fmap(Function.toJson)
|
let toJson = (r: registry) => r.functions->E.A2.fmap(Function.toJson)
|
||||||
|
let allExamples = (r: registry) => r.functions->E.A2.fmap(r => r.examples)->E.A.concatMany
|
||||||
|
let allExamplesWithFns = (r: registry) =>
|
||||||
|
r.functions->E.A2.fmap(fn => fn.examples->E.A2.fmap(example => (fn, example)))->E.A.concatMany
|
||||||
|
|
||||||
|
let _buildFnNameDict = (r: array<function>): fnNameDict => {
|
||||||
|
let allDefinitionsWithFns =
|
||||||
|
r
|
||||||
|
->E.A2.fmap(fn => fn.definitions->E.A2.fmap(definitions => (fn, definitions)))
|
||||||
|
->E.A.concatMany
|
||||||
|
let functionsWithFnNames =
|
||||||
|
allDefinitionsWithFns
|
||||||
|
->E.A2.fmap(((fn, def)) => {
|
||||||
|
let nameWithNamespace = `${fn.nameSpace}.${def.name}`
|
||||||
|
let nameWithoutNamespace = def.name
|
||||||
|
fn.requiresNamespace
|
||||||
|
? [(nameWithNamespace, fn)]
|
||||||
|
: [(nameWithNamespace, fn), (nameWithoutNamespace, fn)]
|
||||||
|
})
|
||||||
|
->E.A.concatMany
|
||||||
|
let uniqueNames = functionsWithFnNames->E.A2.fmap(((name, _)) => name)->E.A.uniq
|
||||||
|
let cacheAsArray: array<(string, array<function>)> = uniqueNames->E.A2.fmap(uniqueName => {
|
||||||
|
let relevantItems =
|
||||||
|
E.A2.filter(functionsWithFnNames, ((defName, _)) => defName == uniqueName)->E.A2.fmap(
|
||||||
|
E.Tuple2.second,
|
||||||
|
)
|
||||||
|
(uniqueName, relevantItems)
|
||||||
|
})
|
||||||
|
cacheAsArray->Js.Dict.fromArray
|
||||||
|
}
|
||||||
|
|
||||||
|
let make = (fns: array<function>): registry => {
|
||||||
|
let dict = _buildFnNameDict(fns)
|
||||||
|
{functions: fns, fnNameDict: dict}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
There's a (potential+minor) bug here: If a function definition is called outside of the calls
|
There's a (potential+minor) bug here: If a function definition is called outside of the calls
|
||||||
to the registry, then it's possible that there could be a match after the registry is
|
to the registry, then it's possible that there could be a match after the registry is
|
||||||
called. However, for now, we could just call the registry last.
|
called. However, for now, we could just call the registry last.
|
||||||
*/
|
*/
|
||||||
let matchAndRun = (
|
let _matchAndRun = (
|
||||||
~registry: registry,
|
~registry: registry,
|
||||||
~fnName: string,
|
~fnName: string,
|
||||||
~args: array<internalExpressionValue>,
|
~args: array<internalExpressionValue>,
|
||||||
~env: DistributionOperation.env,
|
~env: GenericDist.env,
|
||||||
) => {
|
) => {
|
||||||
|
let relevantFunctions = Js.Dict.get(registry.fnNameDict, fnName) |> E.O.default([])
|
||||||
|
let modified = {functions: relevantFunctions, fnNameDict: registry.fnNameDict}
|
||||||
let matchToDef = m => Matcher.Registry.matchToDef(registry, m)
|
let matchToDef = m => Matcher.Registry.matchToDef(registry, m)
|
||||||
//Js.log(toSimple(registry))
|
|
||||||
let showNameMatchDefinitions = matches => {
|
let showNameMatchDefinitions = matches => {
|
||||||
let defs =
|
let defs =
|
||||||
matches
|
matches
|
||||||
|
@ -388,10 +466,21 @@ module Registry = {
|
||||||
->E.A2.joinWith("; ")
|
->E.A2.joinWith("; ")
|
||||||
`There are function matches for ${fnName}(), but with different arguments: ${defs}`
|
`There are function matches for ${fnName}(), but with different arguments: ${defs}`
|
||||||
}
|
}
|
||||||
switch Matcher.Registry.findMatches(registry, fnName, args) {
|
|
||||||
|
switch Matcher.Registry.findMatches(modified, fnName, args) {
|
||||||
| Matcher.Match.FullMatch(match) => match->matchToDef->E.O2.fmap(FnDefinition.run(_, args, env))
|
| Matcher.Match.FullMatch(match) => match->matchToDef->E.O2.fmap(FnDefinition.run(_, args, env))
|
||||||
| SameNameDifferentArguments(m) => Some(Error(showNameMatchDefinitions(m)))
|
| SameNameDifferentArguments(m) => Some(Error(showNameMatchDefinitions(m)))
|
||||||
| _ => None
|
| _ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let dispatch = (
|
||||||
|
registry,
|
||||||
|
(fnName, args): ReducerInterface_InternalExpressionValue.functionCall,
|
||||||
|
env,
|
||||||
|
) => {
|
||||||
|
_matchAndRun(~registry, ~fnName, ~args, ~env)->E.O2.fmap(
|
||||||
|
E.R2.errMap(_, s => Reducer_ErrorValue.RETodo(s)),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,12 @@ module Prepare = {
|
||||||
| _ => Error(impossibleError)
|
| _ => Error(impossibleError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let threeArgs = (inputs: ts): result<ts, err> =>
|
||||||
|
switch inputs {
|
||||||
|
| [FRValueRecord([(_, n1), (_, n2), (_, n3)])] => Ok([n1, n2, n3])
|
||||||
|
| _ => Error(impossibleError)
|
||||||
|
}
|
||||||
|
|
||||||
let toArgs = (inputs: ts): result<ts, err> =>
|
let toArgs = (inputs: ts): result<ts, err> =>
|
||||||
switch inputs {
|
switch inputs {
|
||||||
| [FRValueRecord(args)] => args->E.A2.fmap(((_, b)) => b)->Ok
|
| [FRValueRecord(args)] => args->E.A2.fmap(((_, b)) => b)->Ok
|
||||||
|
@ -57,6 +63,16 @@ module Prepare = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let twoDist = (values: ts): result<
|
||||||
|
(DistributionTypes.genericDist, DistributionTypes.genericDist),
|
||||||
|
err,
|
||||||
|
> => {
|
||||||
|
switch values {
|
||||||
|
| [FRValueDist(a1), FRValueDist(a2)] => Ok(a1, a2)
|
||||||
|
| _ => Error(impossibleError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let twoNumbers = (values: ts): result<(float, float), err> => {
|
let twoNumbers = (values: ts): result<(float, float), err> => {
|
||||||
switch values {
|
switch values {
|
||||||
| [FRValueNumber(a1), FRValueNumber(a2)] => Ok(a1, a2)
|
| [FRValueNumber(a1), FRValueNumber(a2)] => Ok(a1, a2)
|
||||||
|
@ -81,6 +97,11 @@ module Prepare = {
|
||||||
module Record = {
|
module Record = {
|
||||||
let twoDistOrNumber = (values: ts): result<(frValueDistOrNumber, frValueDistOrNumber), err> =>
|
let twoDistOrNumber = (values: ts): result<(frValueDistOrNumber, frValueDistOrNumber), err> =>
|
||||||
values->ToValueArray.Record.twoArgs->E.R.bind(twoDistOrNumber)
|
values->ToValueArray.Record.twoArgs->E.R.bind(twoDistOrNumber)
|
||||||
|
|
||||||
|
let twoDist = (values: ts): result<
|
||||||
|
(DistributionTypes.genericDist, DistributionTypes.genericDist),
|
||||||
|
err,
|
||||||
|
> => values->ToValueArray.Record.twoArgs->E.R.bind(twoDist)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,8 +149,7 @@ module Prepare = {
|
||||||
module Process = {
|
module Process = {
|
||||||
module DistOrNumberToDist = {
|
module DistOrNumberToDist = {
|
||||||
module Helpers = {
|
module Helpers = {
|
||||||
let toSampleSet = (r, env: DistributionOperation.env) =>
|
let toSampleSet = (r, env: GenericDist.env) => GenericDist.toSampleSetDist(r, env.sampleCount)
|
||||||
GenericDist.toSampleSetDist(r, env.sampleCount)
|
|
||||||
|
|
||||||
let mapFnResult = r =>
|
let mapFnResult = r =>
|
||||||
switch r {
|
switch r {
|
||||||
|
@ -166,7 +186,7 @@ module Process = {
|
||||||
let oneValue = (
|
let oneValue = (
|
||||||
~fn: float => result<DistributionTypes.genericDist, string>,
|
~fn: float => result<DistributionTypes.genericDist, string>,
|
||||||
~value: frValueDistOrNumber,
|
~value: frValueDistOrNumber,
|
||||||
~env: DistributionOperation.env,
|
~env: GenericDist.env,
|
||||||
): result<DistributionTypes.genericDist, string> => {
|
): result<DistributionTypes.genericDist, string> => {
|
||||||
switch value {
|
switch value {
|
||||||
| FRValueNumber(a1) => fn(a1)
|
| FRValueNumber(a1) => fn(a1)
|
||||||
|
@ -179,7 +199,7 @@ module Process = {
|
||||||
let twoValues = (
|
let twoValues = (
|
||||||
~fn: ((float, float)) => result<DistributionTypes.genericDist, string>,
|
~fn: ((float, float)) => result<DistributionTypes.genericDist, string>,
|
||||||
~values: (frValueDistOrNumber, frValueDistOrNumber),
|
~values: (frValueDistOrNumber, frValueDistOrNumber),
|
||||||
~env: DistributionOperation.env,
|
~env: GenericDist.env,
|
||||||
): result<DistributionTypes.genericDist, string> => {
|
): result<DistributionTypes.genericDist, string> => {
|
||||||
switch values {
|
switch values {
|
||||||
| (FRValueNumber(a1), FRValueNumber(a2)) => fn((a1, a2))
|
| (FRValueNumber(a1), FRValueNumber(a2)) => fn((a1, a2))
|
||||||
|
@ -193,72 +213,3 @@ module Process = {
|
||||||
twoValues(~fn=Helpers.wrapSymbolic(fn), ~values)
|
twoValues(~fn=Helpers.wrapSymbolic(fn), ~values)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module TwoArgDist = {
|
|
||||||
let process = (~fn, ~env, r) =>
|
|
||||||
r
|
|
||||||
->E.R.bind(Process.DistOrNumberToDist.twoValuesUsingSymbolicDist(~fn, ~values=_, ~env))
|
|
||||||
->E.R2.fmap(Wrappers.evDistribution)
|
|
||||||
|
|
||||||
let make = (name, fn) => {
|
|
||||||
FnDefinition.make(~name, ~inputs=[FRTypeDistOrNumber, FRTypeDistOrNumber], ~run=(inputs, env) =>
|
|
||||||
inputs->Prepare.ToValueTuple.twoDistOrNumber->process(~fn, ~env)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
let makeRecordP5P95 = (name, fn) => {
|
|
||||||
FnDefinition.make(
|
|
||||||
~name,
|
|
||||||
~inputs=[FRTypeRecord([("p5", FRTypeDistOrNumber), ("p95", FRTypeDistOrNumber)])],
|
|
||||||
~run=(inputs, env) => inputs->Prepare.ToValueTuple.Record.twoDistOrNumber->process(~fn, ~env),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
let makeRecordMeanStdev = (name, fn) => {
|
|
||||||
FnDefinition.make(
|
|
||||||
~name,
|
|
||||||
~inputs=[FRTypeRecord([("mean", FRTypeDistOrNumber), ("stdev", FRTypeDistOrNumber)])],
|
|
||||||
~run=(inputs, env) => inputs->Prepare.ToValueTuple.Record.twoDistOrNumber->process(~fn, ~env),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module OneArgDist = {
|
|
||||||
let process = (~fn, ~env, r) =>
|
|
||||||
r
|
|
||||||
->E.R.bind(Process.DistOrNumberToDist.oneValueUsingSymbolicDist(~fn, ~value=_, ~env))
|
|
||||||
->E.R2.fmap(Wrappers.evDistribution)
|
|
||||||
|
|
||||||
let make = (name, fn) =>
|
|
||||||
FnDefinition.make(~name, ~inputs=[FRTypeDistOrNumber], ~run=(inputs, env) =>
|
|
||||||
inputs->Prepare.ToValueTuple.oneDistOrNumber->process(~fn, ~env)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
module ArrayNumberDist = {
|
|
||||||
let make = (name, fn) => {
|
|
||||||
FnDefinition.make(~name, ~inputs=[FRTypeArray(FRTypeNumber)], ~run=(inputs, _) =>
|
|
||||||
Prepare.ToTypedArray.numbers(inputs)
|
|
||||||
->E.R.bind(r => E.A.length(r) === 0 ? Error("List is empty") : Ok(r))
|
|
||||||
->E.R.bind(fn)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
let make2 = (name, fn) => {
|
|
||||||
FnDefinition.make(~name, ~inputs=[FRTypeArray(FRTypeAny)], ~run=(inputs, _) =>
|
|
||||||
Prepare.ToTypedArray.numbers(inputs)
|
|
||||||
->E.R.bind(r => E.A.length(r) === 0 ? Error("List is empty") : Ok(r))
|
|
||||||
->E.R.bind(fn)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module NumberToNumber = {
|
|
||||||
let make = (name, fn) =>
|
|
||||||
FnDefinition.make(~name, ~inputs=[FRTypeNumber], ~run=(inputs, _) => {
|
|
||||||
inputs
|
|
||||||
->getOrError(0)
|
|
||||||
->E.R.bind(Prepare.oneNumber)
|
|
||||||
->E.R2.fmap(fn)
|
|
||||||
->E.R2.fmap(Wrappers.evNumber)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,512 +1,12 @@
|
||||||
open FunctionRegistry_Core
|
let fnList = Belt.Array.concatMany([
|
||||||
open FunctionRegistry_Helpers
|
FR_Dict.library,
|
||||||
|
FR_Dist.library,
|
||||||
let twoArgs = E.Tuple2.toFnCall
|
FR_Fn.library,
|
||||||
|
FR_List.library,
|
||||||
module Declaration = {
|
FR_Number.library,
|
||||||
let frType = FRTypeRecord([
|
FR_Pointset.library,
|
||||||
("fn", FRTypeLambda),
|
FR_Scoring.library,
|
||||||
("inputs", FRTypeArray(FRTypeRecord([("min", FRTypeNumber), ("max", FRTypeNumber)]))),
|
|
||||||
])
|
])
|
||||||
|
|
||||||
let fromExpressionValue = (e: frValue): result<internalExpressionValue, string> => {
|
let registry = FunctionRegistry_Core.Registry.make(fnList)
|
||||||
switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.twoArgs([e]) {
|
let dispatch = FunctionRegistry_Core.Registry.dispatch(registry)
|
||||||
| Ok([FRValueLambda(lambda), FRValueArray(inputs)]) => {
|
|
||||||
open FunctionRegistry_Helpers.Prepare
|
|
||||||
let getMinMax = arg =>
|
|
||||||
ToValueArray.Record.toArgs([arg])
|
|
||||||
->E.R.bind(ToValueTuple.twoNumbers)
|
|
||||||
->E.R2.fmap(((min, max)) => Declaration.ContinuousFloatArg.make(min, max))
|
|
||||||
inputs
|
|
||||||
->E.A2.fmap(getMinMax)
|
|
||||||
->E.A.R.firstErrorOrOpen
|
|
||||||
->E.R2.fmap(args => ReducerInterface_InternalExpressionValue.IEvDeclaration(
|
|
||||||
Declaration.make(lambda, args),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
| Error(r) => Error(r)
|
|
||||||
| Ok(_) => Error(FunctionRegistry_Helpers.impossibleError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let inputsTodist = (inputs: array<FunctionRegistry_Core.frValue>, makeDist) => {
|
|
||||||
let array = inputs->getOrError(0)->E.R.bind(Prepare.ToValueArray.Array.openA)
|
|
||||||
let xyCoords =
|
|
||||||
array->E.R.bind(xyCoords =>
|
|
||||||
xyCoords
|
|
||||||
->E.A2.fmap(xyCoord =>
|
|
||||||
[xyCoord]->Prepare.ToValueArray.Record.twoArgs->E.R.bind(Prepare.ToValueTuple.twoNumbers)
|
|
||||||
)
|
|
||||||
->E.A.R.firstErrorOrOpen
|
|
||||||
)
|
|
||||||
let expressionValue =
|
|
||||||
xyCoords
|
|
||||||
->E.R.bind(r => r->XYShape.T.makeFromZipped->E.R2.errMap(XYShape.Error.toString))
|
|
||||||
->E.R2.fmap(r => ReducerInterface_InternalExpressionValue.IEvDistribution(
|
|
||||||
PointSet(makeDist(r)),
|
|
||||||
))
|
|
||||||
expressionValue
|
|
||||||
}
|
|
||||||
|
|
||||||
let registry = [
|
|
||||||
Function.make(
|
|
||||||
~name="toContinuousPointSet",
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(
|
|
||||||
~name="toContinuousPointSet",
|
|
||||||
~inputs=[FRTypeArray(FRTypeRecord([("x", FRTypeNumeric), ("y", FRTypeNumeric)]))],
|
|
||||||
~run=(inputs, _) => inputsTodist(inputs, r => Continuous(Continuous.make(r))),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="toDiscretePointSet",
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(
|
|
||||||
~name="toDiscretePointSet",
|
|
||||||
~inputs=[FRTypeArray(FRTypeRecord([("x", FRTypeNumeric), ("y", FRTypeNumeric)]))],
|
|
||||||
~run=(inputs, _) => inputsTodist(inputs, r => Discrete(Discrete.make(r))),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Declaration",
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(~name="declareFn", ~inputs=[Declaration.frType], ~run=(inputs, _) => {
|
|
||||||
inputs->getOrError(0)->E.R.bind(Declaration.fromExpressionValue)
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Normal",
|
|
||||||
~examples=`normal(5,1)
|
|
||||||
normal({p5: 4, p95: 10})
|
|
||||||
normal({mean: 5, stdev: 2})`,
|
|
||||||
~definitions=[
|
|
||||||
TwoArgDist.make("normal", twoArgs(SymbolicDist.Normal.make)),
|
|
||||||
TwoArgDist.makeRecordP5P95("normal", r =>
|
|
||||||
twoArgs(SymbolicDist.Normal.from90PercentCI, r)->Ok
|
|
||||||
),
|
|
||||||
TwoArgDist.makeRecordMeanStdev("normal", twoArgs(SymbolicDist.Normal.make)),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Lognormal",
|
|
||||||
~examples=`lognormal(0.5, 0.8)
|
|
||||||
lognormal({p5: 4, p95: 10})
|
|
||||||
lognormal({mean: 5, stdev: 2})`,
|
|
||||||
~definitions=[
|
|
||||||
TwoArgDist.make("lognormal", twoArgs(SymbolicDist.Lognormal.make)),
|
|
||||||
TwoArgDist.makeRecordP5P95("lognormal", r =>
|
|
||||||
twoArgs(SymbolicDist.Lognormal.from90PercentCI, r)->Ok
|
|
||||||
),
|
|
||||||
TwoArgDist.makeRecordMeanStdev("lognormal", twoArgs(SymbolicDist.Lognormal.fromMeanAndStdev)),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Uniform",
|
|
||||||
~examples=`uniform(10, 12)`,
|
|
||||||
~definitions=[TwoArgDist.make("uniform", twoArgs(SymbolicDist.Uniform.make))],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Beta",
|
|
||||||
~examples=`beta(20, 25)
|
|
||||||
beta({mean: 0.39, stdev: 0.1})`,
|
|
||||||
~definitions=[
|
|
||||||
TwoArgDist.make("beta", twoArgs(SymbolicDist.Beta.make)),
|
|
||||||
TwoArgDist.makeRecordMeanStdev("beta", twoArgs(SymbolicDist.Beta.fromMeanAndStdev)),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Cauchy",
|
|
||||||
~examples=`cauchy(5, 1)`,
|
|
||||||
~definitions=[TwoArgDist.make("cauchy", twoArgs(SymbolicDist.Cauchy.make))],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Gamma",
|
|
||||||
~examples=`gamma(5, 1)`,
|
|
||||||
~definitions=[TwoArgDist.make("gamma", twoArgs(SymbolicDist.Gamma.make))],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Logistic",
|
|
||||||
~examples=`gamma(5, 1)`,
|
|
||||||
~definitions=[TwoArgDist.make("logistic", twoArgs(SymbolicDist.Logistic.make))],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="To (Distribution)",
|
|
||||||
~examples=`5 to 10
|
|
||||||
to(5,10)
|
|
||||||
-5 to 5`,
|
|
||||||
~definitions=[
|
|
||||||
TwoArgDist.make("to", twoArgs(SymbolicDist.From90thPercentile.make)),
|
|
||||||
TwoArgDist.make(
|
|
||||||
"credibleIntervalToDistribution",
|
|
||||||
twoArgs(SymbolicDist.From90thPercentile.make),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Exponential",
|
|
||||||
~examples=`exponential(2)`,
|
|
||||||
~definitions=[OneArgDist.make("exponential", SymbolicDist.Exponential.make)],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Bernoulli",
|
|
||||||
~examples=`bernoulli(0.5)`,
|
|
||||||
~definitions=[OneArgDist.make("bernoulli", SymbolicDist.Bernoulli.make)],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="PointMass",
|
|
||||||
~examples=`pointMass(0.5)`,
|
|
||||||
~definitions=[OneArgDist.make("pointMass", SymbolicDist.Float.makeSafe)],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="toContinuousPointSet",
|
|
||||||
~description="Converts a set of points to a continuous distribution",
|
|
||||||
~examples=`toContinuousPointSet([
|
|
||||||
{x: 0, y: 0.1},
|
|
||||||
{x: 1, y: 0.2},
|
|
||||||
{x: 2, y: 0.15},
|
|
||||||
{x: 3, y: 0.1}
|
|
||||||
])`,
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(
|
|
||||||
~name="toContinuousPointSet",
|
|
||||||
~inputs=[FRTypeArray(FRTypeRecord([("x", FRTypeNumeric), ("y", FRTypeNumeric)]))],
|
|
||||||
~run=(inputs, _) => inputsTodist(inputs, r => Continuous(Continuous.make(r))),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="toDiscretePointSet",
|
|
||||||
~description="Converts a set of points to a discrete distribution",
|
|
||||||
~examples=`toDiscretePointSet([
|
|
||||||
{x: 0, y: 0.1},
|
|
||||||
{x: 1, y: 0.2},
|
|
||||||
{x: 2, y: 0.15},
|
|
||||||
{x: 3, y: 0.1}
|
|
||||||
])`,
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(
|
|
||||||
~name="toDiscretePointSet",
|
|
||||||
~inputs=[FRTypeArray(FRTypeRecord([("x", FRTypeNumeric), ("y", FRTypeNumeric)]))],
|
|
||||||
~run=(inputs, _) => inputsTodist(inputs, r => Discrete(Discrete.make(r))),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Declaration (Continuous Function)",
|
|
||||||
~description="Adds metadata to a function of the input ranges. Works now for numeric and date inputs. This is useful when making predictions. It allows you to limit the domain that your prediction will be used and scored within.",
|
|
||||||
~examples=`declareFn({
|
|
||||||
fn: {|a,b| a },
|
|
||||||
inputs: [
|
|
||||||
{min: 0, max: 100},
|
|
||||||
{min: 30, max: 50}
|
|
||||||
]
|
|
||||||
})`,
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(~name="declareFn", ~inputs=[Declaration.frType], ~run=(inputs, _) => {
|
|
||||||
inputs->E.A.unsafe_get(0)->Declaration.fromExpressionValue
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
~isExperimental=true,
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Floor",
|
|
||||||
~definitions=[NumberToNumber.make("floor", Js.Math.floor_float)],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Ceiling",
|
|
||||||
~definitions=[NumberToNumber.make("ceil", Js.Math.ceil_float)],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Absolute Value",
|
|
||||||
~definitions=[NumberToNumber.make("abs", Js.Math.abs_float)],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(~name="Exponent", ~definitions=[NumberToNumber.make("exp", Js.Math.exp)], ()),
|
|
||||||
Function.make(~name="Log", ~definitions=[NumberToNumber.make("log", Js.Math.log)], ()),
|
|
||||||
Function.make(
|
|
||||||
~name="Log Base 10",
|
|
||||||
~definitions=[NumberToNumber.make("log10", Js.Math.log10)],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(~name="Log Base 2", ~definitions=[NumberToNumber.make("log2", Js.Math.log2)], ()),
|
|
||||||
Function.make(~name="Round", ~definitions=[NumberToNumber.make("round", Js.Math.round)], ()),
|
|
||||||
Function.make(
|
|
||||||
~name="Sum",
|
|
||||||
~definitions=[ArrayNumberDist.make("sum", r => r->E.A.Floats.sum->Wrappers.evNumber->Ok)],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Product",
|
|
||||||
~definitions=[
|
|
||||||
ArrayNumberDist.make("product", r => r->E.A.Floats.product->Wrappers.evNumber->Ok),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Min",
|
|
||||||
~definitions=[ArrayNumberDist.make("min", r => r->E.A.Floats.min->Wrappers.evNumber->Ok)],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Max",
|
|
||||||
~definitions=[ArrayNumberDist.make("max", r => r->E.A.Floats.max->Wrappers.evNumber->Ok)],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Mean",
|
|
||||||
~definitions=[ArrayNumberDist.make("mean", r => r->E.A.Floats.mean->Wrappers.evNumber->Ok)],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Geometric Mean",
|
|
||||||
~definitions=[
|
|
||||||
ArrayNumberDist.make("geomean", r => r->E.A.Floats.geomean->Wrappers.evNumber->Ok),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Standard Deviation",
|
|
||||||
~definitions=[ArrayNumberDist.make("stdev", r => r->E.A.Floats.stdev->Wrappers.evNumber->Ok)],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Variance",
|
|
||||||
~definitions=[
|
|
||||||
ArrayNumberDist.make("variance", r => r->E.A.Floats.stdev->Wrappers.evNumber->Ok),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="First",
|
|
||||||
~definitions=[
|
|
||||||
ArrayNumberDist.make2("first", r =>
|
|
||||||
r->E.A.first |> E.O.toResult(impossibleError) |> E.R.fmap(Wrappers.evNumber)
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Last",
|
|
||||||
~definitions=[
|
|
||||||
ArrayNumberDist.make2("last", r =>
|
|
||||||
r->E.A.last |> E.O.toResult(impossibleError) |> E.R.fmap(Wrappers.evNumber)
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Sort",
|
|
||||||
~definitions=[
|
|
||||||
ArrayNumberDist.make("sort", r =>
|
|
||||||
r->E.A.Floats.sort->E.A2.fmap(Wrappers.evNumber)->Wrappers.evArray->Ok
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Reverse",
|
|
||||||
~definitions=[
|
|
||||||
ArrayNumberDist.make("reverse", r =>
|
|
||||||
r->Belt_Array.reverse->E.A2.fmap(Wrappers.evNumber)->Wrappers.evArray->Ok
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Cumulative Sum",
|
|
||||||
~definitions=[
|
|
||||||
ArrayNumberDist.make("cumsum", r =>
|
|
||||||
r->E.A.Floats.cumsum->E.A2.fmap(Wrappers.evNumber)->Wrappers.evArray->Ok
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Cumulative Prod",
|
|
||||||
~definitions=[
|
|
||||||
ArrayNumberDist.make("cumprod", r =>
|
|
||||||
r->E.A.Floats.cumsum->E.A2.fmap(Wrappers.evNumber)->Wrappers.evArray->Ok
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Diff",
|
|
||||||
~definitions=[
|
|
||||||
ArrayNumberDist.make("diff", r =>
|
|
||||||
r->E.A.Floats.diff->E.A2.fmap(Wrappers.evNumber)->Wrappers.evArray->Ok
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Dict.merge",
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(
|
|
||||||
~name="merge",
|
|
||||||
~inputs=[FRTypeDict(FRTypeAny), FRTypeDict(FRTypeAny)],
|
|
||||||
~run=(inputs, _) => {
|
|
||||||
switch inputs {
|
|
||||||
| [FRValueDict(d1), FRValueDict(d2)] => {
|
|
||||||
let newDict =
|
|
||||||
E.Dict.concat(d1, d2) |> Js.Dict.map((. r) =>
|
|
||||||
FunctionRegistry_Core.FRType.matchReverse(r)
|
|
||||||
)
|
|
||||||
newDict->Js.Dict.entries->Belt.Map.String.fromArray->Wrappers.evRecord->Ok
|
|
||||||
}
|
|
||||||
| _ => Error(impossibleError)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
//TODO: Make sure that two functions can't have the same name. This causes chaos elsewhere.
|
|
||||||
Function.make(
|
|
||||||
~name="Dict.mergeMany",
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(~name="mergeMany", ~inputs=[FRTypeArray(FRTypeDict(FRTypeAny))], ~run=(
|
|
||||||
inputs,
|
|
||||||
_,
|
|
||||||
) =>
|
|
||||||
inputs
|
|
||||||
->Prepare.ToTypedArray.dicts
|
|
||||||
->E.R2.fmap(E.Dict.concatMany)
|
|
||||||
->E.R2.fmap(Js.Dict.map((. r) => FunctionRegistry_Core.FRType.matchReverse(r)))
|
|
||||||
->E.R2.fmap(r => r->Js.Dict.entries->Belt.Map.String.fromArray)
|
|
||||||
->E.R2.fmap(Wrappers.evRecord)
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Dict.keys",
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(~name="keys", ~inputs=[FRTypeDict(FRTypeAny)], ~run=(inputs, _) =>
|
|
||||||
switch inputs {
|
|
||||||
| [FRValueDict(d1)] => Js.Dict.keys(d1)->E.A2.fmap(Wrappers.evString)->Wrappers.evArray->Ok
|
|
||||||
| _ => Error(impossibleError)
|
|
||||||
}
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Dict.values",
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(~name="values", ~inputs=[FRTypeDict(FRTypeAny)], ~run=(inputs, _) =>
|
|
||||||
switch inputs {
|
|
||||||
| [FRValueDict(d1)] =>
|
|
||||||
Js.Dict.values(d1)
|
|
||||||
->E.A2.fmap(FunctionRegistry_Core.FRType.matchReverse)
|
|
||||||
->Wrappers.evArray
|
|
||||||
->Ok
|
|
||||||
| _ => Error(impossibleError)
|
|
||||||
}
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Dict.toList",
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(~name="dictToList", ~inputs=[FRTypeDict(FRTypeAny)], ~run=(inputs, _) =>
|
|
||||||
switch inputs {
|
|
||||||
| [FRValueDict(dict)] =>
|
|
||||||
dict
|
|
||||||
->Js.Dict.entries
|
|
||||||
->E.A2.fmap(((key, value)) =>
|
|
||||||
Wrappers.evArray([
|
|
||||||
Wrappers.evString(key),
|
|
||||||
FunctionRegistry_Core.FRType.matchReverse(value),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
->Wrappers.evArray
|
|
||||||
->Ok
|
|
||||||
| _ => Error(impossibleError)
|
|
||||||
}
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Dict.fromList",
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(~name="dictFromList", ~inputs=[FRTypeArray(FRTypeArray(FRTypeAny))], ~run=(
|
|
||||||
inputs,
|
|
||||||
_,
|
|
||||||
) => {
|
|
||||||
let convertInternalItems = items =>
|
|
||||||
items
|
|
||||||
->E.A2.fmap(item => {
|
|
||||||
switch item {
|
|
||||||
| [FRValueString(string), value] =>
|
|
||||||
(string, FunctionRegistry_Core.FRType.matchReverse(value))->Ok
|
|
||||||
| _ => Error(impossibleError)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
->E.A.R.firstErrorOrOpen
|
|
||||||
->E.R2.fmap(Belt.Map.String.fromArray)
|
|
||||||
->E.R2.fmap(Wrappers.evRecord)
|
|
||||||
inputs->getOrError(0)->E.R.bind(Prepare.ToValueArray.Array.arrayOfArrays)
|
|
||||||
|> E.R2.bind(convertInternalItems)
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="List.make",
|
|
||||||
~definitions=[
|
|
||||||
//Todo: If the second item is a function with no args, it could be nice to run this function and return the result.
|
|
||||||
FnDefinition.make(~name="listMake", ~inputs=[FRTypeNumber, FRTypeAny], ~run=(inputs, _) => {
|
|
||||||
switch inputs {
|
|
||||||
| [FRValueNumber(number), value] =>
|
|
||||||
Belt.Array.make(E.Float.toInt(number), value)
|
|
||||||
->E.A2.fmap(FunctionRegistry_Core.FRType.matchReverse)
|
|
||||||
->Wrappers.evArray
|
|
||||||
->Ok
|
|
||||||
| _ => Error(impossibleError)
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="upTo",
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(~name="upTo", ~inputs=[FRTypeNumber, FRTypeNumber], ~run=(inputs, _) =>
|
|
||||||
inputs
|
|
||||||
->Prepare.ToValueTuple.twoNumbers
|
|
||||||
->E.R2.fmap(((low, high)) =>
|
|
||||||
E.A.Floats.range(low, high, (high -. low +. 1.0)->E.Float.toInt)
|
|
||||||
->E.A2.fmap(Wrappers.evNumber)
|
|
||||||
->Wrappers.evArray
|
|
||||||
)
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
|
@ -0,0 +1,169 @@
|
||||||
|
open FunctionRegistry_Core
|
||||||
|
open FunctionRegistry_Helpers
|
||||||
|
|
||||||
|
let nameSpace = "Dict"
|
||||||
|
|
||||||
|
module Internals = {
|
||||||
|
type t = ReducerInterface_InternalExpressionValue.map
|
||||||
|
|
||||||
|
let keys = (a: t): internalExpressionValue => IEvArray(
|
||||||
|
Belt.Map.String.keysToArray(a)->E.A2.fmap(Wrappers.evString),
|
||||||
|
)
|
||||||
|
|
||||||
|
let values = (a: t): internalExpressionValue => IEvArray(Belt.Map.String.valuesToArray(a))
|
||||||
|
|
||||||
|
let toList = (a: t): internalExpressionValue =>
|
||||||
|
Belt.Map.String.toArray(a)
|
||||||
|
->E.A2.fmap(((key, value)) => Wrappers.evArray([IEvString(key), value]))
|
||||||
|
->Wrappers.evArray
|
||||||
|
|
||||||
|
let fromList = (items: array<internalExpressionValue>): result<internalExpressionValue, string> =>
|
||||||
|
items
|
||||||
|
->E.A2.fmap(item => {
|
||||||
|
switch (item: internalExpressionValue) {
|
||||||
|
| IEvArray([IEvString(string), value]) => (string, value)->Ok
|
||||||
|
| _ => Error(impossibleError)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
->E.A.R.firstErrorOrOpen
|
||||||
|
->E.R2.fmap(Belt.Map.String.fromArray)
|
||||||
|
->E.R2.fmap(Wrappers.evRecord)
|
||||||
|
|
||||||
|
let merge = (a: t, b: t): internalExpressionValue => IEvRecord(
|
||||||
|
Belt.Map.String.merge(a, b, (_, _, c) => c),
|
||||||
|
)
|
||||||
|
|
||||||
|
//Belt.Map.String has a function for mergeMany, but I couldn't understand how to use it yet.
|
||||||
|
let mergeMany = (a: array<t>): internalExpressionValue => {
|
||||||
|
let mergedValues =
|
||||||
|
a->E.A2.fmap(Belt.Map.String.toArray)->Belt.Array.concatMany->Belt.Map.String.fromArray
|
||||||
|
IEvRecord(mergedValues)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let library = [
|
||||||
|
Function.make(
|
||||||
|
~name="merge",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace=true,
|
||||||
|
~output=EvtRecord,
|
||||||
|
~examples=[`Dict.merge({a: 1, b: 2}, {c: 3, d: 4})`],
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="merge",
|
||||||
|
~inputs=[FRTypeDict(FRTypeAny), FRTypeDict(FRTypeAny)],
|
||||||
|
~run=(inputs, _, _) => {
|
||||||
|
switch inputs {
|
||||||
|
| [IEvRecord(d1), IEvRecord(d2)] => Internals.merge(d1, d2)->Ok
|
||||||
|
| _ => Error(impossibleError)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
//TODO: Change to use new mergeMany() function.
|
||||||
|
Function.make(
|
||||||
|
~name="mergeMany",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace=true,
|
||||||
|
~output=EvtRecord,
|
||||||
|
~examples=[`Dict.mergeMany([{a: 1, b: 2}, {c: 3, d: 4}])`],
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="mergeMany",
|
||||||
|
~inputs=[FRTypeArray(FRTypeDict(FRTypeAny))],
|
||||||
|
~run=(_, inputs, _) =>
|
||||||
|
inputs
|
||||||
|
->Prepare.ToTypedArray.dicts
|
||||||
|
->E.R2.fmap(E.Dict.concatMany)
|
||||||
|
->E.R2.fmap(Js.Dict.map((. r) => FunctionRegistry_Core.FRType.matchReverse(r)))
|
||||||
|
->E.R2.fmap(r => r->Js.Dict.entries->Belt.Map.String.fromArray)
|
||||||
|
->E.R2.fmap(Wrappers.evRecord),
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="keys",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace=true,
|
||||||
|
~output=EvtArray,
|
||||||
|
~examples=[`Dict.keys({a: 1, b: 2})`],
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="keys",
|
||||||
|
~inputs=[FRTypeDict(FRTypeAny)],
|
||||||
|
~run=(inputs, _, _) =>
|
||||||
|
switch inputs {
|
||||||
|
| [IEvRecord(d1)] => Internals.keys(d1)->Ok
|
||||||
|
| _ => Error(impossibleError)
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="values",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace=true,
|
||||||
|
~output=EvtArray,
|
||||||
|
~examples=[`Dict.values({a: 1, b: 2})`],
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="values",
|
||||||
|
~inputs=[FRTypeDict(FRTypeAny)],
|
||||||
|
~run=(inputs, _, _) =>
|
||||||
|
switch inputs {
|
||||||
|
| [IEvRecord(d1)] => Internals.values(d1)->Ok
|
||||||
|
| _ => Error(impossibleError)
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="toList",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace=true,
|
||||||
|
~output=EvtArray,
|
||||||
|
~examples=[`Dict.toList({a: 1, b: 2})`],
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="toList",
|
||||||
|
~inputs=[FRTypeDict(FRTypeAny)],
|
||||||
|
~run=(inputs, _, _) =>
|
||||||
|
switch inputs {
|
||||||
|
| [IEvRecord(dict)] => dict->Internals.toList->Ok
|
||||||
|
| _ => Error(impossibleError)
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="fromList",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace=true,
|
||||||
|
~output=EvtRecord,
|
||||||
|
~examples=[`Dict.fromList([["a", 1], ["b", 2]])`],
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="fromList",
|
||||||
|
~inputs=[FRTypeArray(FRTypeArray(FRTypeAny))],
|
||||||
|
~run=(inputs, _, _) =>
|
||||||
|
switch inputs {
|
||||||
|
| [IEvArray(items)] => Internals.fromList(items)
|
||||||
|
| _ => Error(impossibleError)
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,152 @@
|
||||||
|
open FunctionRegistry_Core
|
||||||
|
open FunctionRegistry_Helpers
|
||||||
|
let twoArgs = E.Tuple2.toFnCall
|
||||||
|
|
||||||
|
module DistributionCreation = {
|
||||||
|
let nameSpace = "Dist"
|
||||||
|
let output = ReducerInterface_InternalExpressionValue.EvtDistribution
|
||||||
|
let requiresNamespace = false
|
||||||
|
|
||||||
|
let fnMake = (~name, ~examples, ~definitions) => {
|
||||||
|
Function.make(~name, ~nameSpace, ~output, ~examples, ~definitions, ~requiresNamespace, ())
|
||||||
|
}
|
||||||
|
|
||||||
|
module TwoArgDist = {
|
||||||
|
let process = (~fn, ~env, r) =>
|
||||||
|
r
|
||||||
|
->E.R.bind(Process.DistOrNumberToDist.twoValuesUsingSymbolicDist(~fn, ~values=_, ~env))
|
||||||
|
->E.R2.fmap(Wrappers.evDistribution)
|
||||||
|
|
||||||
|
let make = (name, fn) => {
|
||||||
|
FnDefinition.make(
|
||||||
|
~name,
|
||||||
|
~inputs=[FRTypeDistOrNumber, FRTypeDistOrNumber],
|
||||||
|
~run=(_, inputs, env) => inputs->Prepare.ToValueTuple.twoDistOrNumber->process(~fn, ~env),
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let makeRecordP5P95 = (name, fn) => {
|
||||||
|
FnDefinition.make(
|
||||||
|
~name,
|
||||||
|
~inputs=[FRTypeRecord([("p5", FRTypeDistOrNumber), ("p95", FRTypeDistOrNumber)])],
|
||||||
|
~run=(_, inputs, env) =>
|
||||||
|
inputs->Prepare.ToValueTuple.Record.twoDistOrNumber->process(~fn, ~env),
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let makeRecordMeanStdev = (name, fn) => {
|
||||||
|
FnDefinition.make(
|
||||||
|
~name,
|
||||||
|
~inputs=[FRTypeRecord([("mean", FRTypeDistOrNumber), ("stdev", FRTypeDistOrNumber)])],
|
||||||
|
~run=(_, inputs, env) =>
|
||||||
|
inputs->Prepare.ToValueTuple.Record.twoDistOrNumber->process(~fn, ~env),
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module OneArgDist = {
|
||||||
|
let process = (~fn, ~env, r) =>
|
||||||
|
r
|
||||||
|
->E.R.bind(Process.DistOrNumberToDist.oneValueUsingSymbolicDist(~fn, ~value=_, ~env))
|
||||||
|
->E.R2.fmap(Wrappers.evDistribution)
|
||||||
|
|
||||||
|
let make = (name, fn) =>
|
||||||
|
FnDefinition.make(
|
||||||
|
~name,
|
||||||
|
~inputs=[FRTypeDistOrNumber],
|
||||||
|
~run=(_, inputs, env) => inputs->Prepare.ToValueTuple.oneDistOrNumber->process(~fn, ~env),
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let library = [
|
||||||
|
fnMake(
|
||||||
|
~name="normal",
|
||||||
|
~examples=["normal(5,1)", "normal({p5: 4, p95: 10})", "normal({mean: 5, stdev: 2})"],
|
||||||
|
~definitions=[
|
||||||
|
TwoArgDist.make("normal", twoArgs(SymbolicDist.Normal.make)),
|
||||||
|
TwoArgDist.makeRecordP5P95("normal", r =>
|
||||||
|
twoArgs(SymbolicDist.Normal.from90PercentCI, r)->Ok
|
||||||
|
),
|
||||||
|
TwoArgDist.makeRecordMeanStdev("normal", twoArgs(SymbolicDist.Normal.make)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
fnMake(
|
||||||
|
~name="lognormal",
|
||||||
|
~examples=[
|
||||||
|
"lognormal(0.5, 0.8)",
|
||||||
|
"lognormal({p5: 4, p95: 10})",
|
||||||
|
"lognormal({mean: 5, stdev: 2})",
|
||||||
|
],
|
||||||
|
~definitions=[
|
||||||
|
TwoArgDist.make("lognormal", twoArgs(SymbolicDist.Lognormal.make)),
|
||||||
|
TwoArgDist.makeRecordP5P95("lognormal", r =>
|
||||||
|
twoArgs(SymbolicDist.Lognormal.from90PercentCI, r)->Ok
|
||||||
|
),
|
||||||
|
TwoArgDist.makeRecordMeanStdev(
|
||||||
|
"lognormal",
|
||||||
|
twoArgs(SymbolicDist.Lognormal.fromMeanAndStdev),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
fnMake(
|
||||||
|
~name="uniform",
|
||||||
|
~examples=[`uniform(10, 12)`],
|
||||||
|
~definitions=[TwoArgDist.make("uniform", twoArgs(SymbolicDist.Uniform.make))],
|
||||||
|
),
|
||||||
|
fnMake(
|
||||||
|
~name="beta",
|
||||||
|
~examples=[`beta(20, 25)`, `beta({mean: 0.39, stdev: 0.1})`],
|
||||||
|
~definitions=[
|
||||||
|
TwoArgDist.make("beta", twoArgs(SymbolicDist.Beta.make)),
|
||||||
|
TwoArgDist.makeRecordMeanStdev("beta", twoArgs(SymbolicDist.Beta.fromMeanAndStdev)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
fnMake(
|
||||||
|
~name="cauchy",
|
||||||
|
~examples=[`cauchy(5, 1)`],
|
||||||
|
~definitions=[TwoArgDist.make("cauchy", twoArgs(SymbolicDist.Cauchy.make))],
|
||||||
|
),
|
||||||
|
fnMake(
|
||||||
|
~name="gamma",
|
||||||
|
~examples=[`gamma(5, 1)`],
|
||||||
|
~definitions=[TwoArgDist.make("gamma", twoArgs(SymbolicDist.Gamma.make))],
|
||||||
|
),
|
||||||
|
fnMake(
|
||||||
|
~name="logistic",
|
||||||
|
~examples=[`logistic(5, 1)`],
|
||||||
|
~definitions=[TwoArgDist.make("logistic", twoArgs(SymbolicDist.Logistic.make))],
|
||||||
|
),
|
||||||
|
fnMake(
|
||||||
|
~name="to (distribution)",
|
||||||
|
~examples=[`5 to 10`, `to(5,10)`, `-5 to 5`],
|
||||||
|
~definitions=[
|
||||||
|
TwoArgDist.make("to", twoArgs(SymbolicDist.From90thPercentile.make)),
|
||||||
|
TwoArgDist.make(
|
||||||
|
"credibleIntervalToDistribution",
|
||||||
|
twoArgs(SymbolicDist.From90thPercentile.make),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
fnMake(
|
||||||
|
~name="exponential",
|
||||||
|
~examples=[`exponential(2)`],
|
||||||
|
~definitions=[OneArgDist.make("exponential", SymbolicDist.Exponential.make)],
|
||||||
|
),
|
||||||
|
fnMake(
|
||||||
|
~name="bernoulli",
|
||||||
|
~examples=[`bernoulli(0.5)`],
|
||||||
|
~definitions=[OneArgDist.make("bernoulli", SymbolicDist.Bernoulli.make)],
|
||||||
|
),
|
||||||
|
fnMake(
|
||||||
|
~name="pointMass",
|
||||||
|
~examples=[`pointMass(0.5)`],
|
||||||
|
~definitions=[OneArgDist.make("pointMass", SymbolicDist.Float.makeSafe)],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
let library = DistributionCreation.library
|
|
@ -0,0 +1,62 @@
|
||||||
|
open FunctionRegistry_Core
|
||||||
|
open FunctionRegistry_Helpers
|
||||||
|
|
||||||
|
module Declaration = {
|
||||||
|
let frType = FRTypeRecord([
|
||||||
|
("fn", FRTypeLambda),
|
||||||
|
("inputs", FRTypeArray(FRTypeRecord([("min", FRTypeNumber), ("max", FRTypeNumber)]))),
|
||||||
|
])
|
||||||
|
|
||||||
|
let fromExpressionValue = (e: frValue): result<internalExpressionValue, string> => {
|
||||||
|
switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.twoArgs([e]) {
|
||||||
|
| Ok([FRValueLambda(lambda), FRValueArray(inputs)]) => {
|
||||||
|
open FunctionRegistry_Helpers.Prepare
|
||||||
|
let getMinMax = arg =>
|
||||||
|
ToValueArray.Record.toArgs([arg])
|
||||||
|
->E.R.bind(ToValueTuple.twoNumbers)
|
||||||
|
->E.R2.fmap(((min, max)) => Declaration.ContinuousFloatArg.make(min, max))
|
||||||
|
inputs
|
||||||
|
->E.A2.fmap(getMinMax)
|
||||||
|
->E.A.R.firstErrorOrOpen
|
||||||
|
->E.R2.fmap(args => ReducerInterface_InternalExpressionValue.IEvDeclaration(
|
||||||
|
Declaration.make(lambda, args),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
| Error(r) => Error(r)
|
||||||
|
| Ok(_) => Error(FunctionRegistry_Helpers.impossibleError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let nameSpace = "Function"
|
||||||
|
|
||||||
|
let library = [
|
||||||
|
Function.make(
|
||||||
|
~name="declare",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace=true,
|
||||||
|
~output=EvtDeclaration,
|
||||||
|
~description="Adds metadata to a function of the input ranges. Works now for numeric and date inputs. This is useful when making predictions. It allows you to limit the domain that your prediction will be used and scored within.",
|
||||||
|
~examples=[
|
||||||
|
`Function.declare({
|
||||||
|
fn: {|a,b| a },
|
||||||
|
inputs: [
|
||||||
|
{min: 0, max: 100},
|
||||||
|
{min: 30, max: 50}
|
||||||
|
]
|
||||||
|
})`,
|
||||||
|
],
|
||||||
|
~isExperimental=true,
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="declare",
|
||||||
|
~inputs=[Declaration.frType],
|
||||||
|
~run=(_, inputs, _) => {
|
||||||
|
inputs->getOrError(0)->E.R.bind(Declaration.fromExpressionValue)
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,128 @@
|
||||||
|
open FunctionRegistry_Core
|
||||||
|
open FunctionRegistry_Helpers
|
||||||
|
|
||||||
|
let nameSpace = "List"
|
||||||
|
let requiresNamespace = true
|
||||||
|
|
||||||
|
module Internals = {
|
||||||
|
let makeFromNumber = (
|
||||||
|
n: float,
|
||||||
|
value: internalExpressionValue,
|
||||||
|
): internalExpressionValue => IEvArray(Belt.Array.make(E.Float.toInt(n), value))
|
||||||
|
|
||||||
|
let upTo = (low: float, high: float): internalExpressionValue => IEvArray(
|
||||||
|
E.A.Floats.range(low, high, (high -. low +. 1.0)->E.Float.toInt)->E.A2.fmap(Wrappers.evNumber),
|
||||||
|
)
|
||||||
|
|
||||||
|
let first = (v: array<internalExpressionValue>): result<internalExpressionValue, string> =>
|
||||||
|
v->E.A.first |> E.O.toResult("No first element")
|
||||||
|
|
||||||
|
let last = (v: array<internalExpressionValue>): result<internalExpressionValue, string> =>
|
||||||
|
v->E.A.last |> E.O.toResult("No last element")
|
||||||
|
|
||||||
|
let reverse = (array: array<internalExpressionValue>): internalExpressionValue => IEvArray(
|
||||||
|
Belt.Array.reverse(array),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let library = [
|
||||||
|
Function.make(
|
||||||
|
~name="make",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtArray,
|
||||||
|
~examples=[`List.make(2, "testValue")`],
|
||||||
|
~definitions=[
|
||||||
|
//Todo: If the second item is a function with no args, it could be nice to run this function and return the result.
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="make",
|
||||||
|
~inputs=[FRTypeNumber, FRTypeAny],
|
||||||
|
~run=(inputs, _, _) => {
|
||||||
|
switch inputs {
|
||||||
|
| [IEvNumber(number), value] => Internals.makeFromNumber(number, value)->Ok
|
||||||
|
| _ => Error(impossibleError)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="upTo",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtArray,
|
||||||
|
~examples=[`List.upTo(1,4)`],
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="upTo",
|
||||||
|
~inputs=[FRTypeNumber, FRTypeNumber],
|
||||||
|
~run=(_, inputs, _) =>
|
||||||
|
inputs
|
||||||
|
->Prepare.ToValueTuple.twoNumbers
|
||||||
|
->E.R2.fmap(((low, high)) => Internals.upTo(low, high)),
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="first",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~examples=[`List.first([1,4,5])`],
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="first",
|
||||||
|
~inputs=[FRTypeArray(FRTypeAny)],
|
||||||
|
~run=(inputs, _, _) =>
|
||||||
|
switch inputs {
|
||||||
|
| [IEvArray(array)] => Internals.first(array)
|
||||||
|
| _ => Error(impossibleError)
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="last",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~examples=[`List.last([1,4,5])`],
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="last",
|
||||||
|
~inputs=[FRTypeArray(FRTypeAny)],
|
||||||
|
~run=(inputs, _, _) =>
|
||||||
|
switch inputs {
|
||||||
|
| [IEvArray(array)] => Internals.last(array)
|
||||||
|
| _ => Error(impossibleError)
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="reverse",
|
||||||
|
~nameSpace,
|
||||||
|
~output=EvtArray,
|
||||||
|
~requiresNamespace=false,
|
||||||
|
~examples=[`List.reverse([1,4,5])`],
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="reverse",
|
||||||
|
~inputs=[FRTypeArray(FRTypeAny)],
|
||||||
|
~run=(inputs, _, _) =>
|
||||||
|
switch inputs {
|
||||||
|
| [IEvArray(array)] => Internals.reverse(array)->Ok
|
||||||
|
| _ => Error(impossibleError)
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,251 @@
|
||||||
|
open FunctionRegistry_Core
|
||||||
|
open FunctionRegistry_Helpers
|
||||||
|
|
||||||
|
let nameSpace = "Number"
|
||||||
|
let requiresNamespace = false
|
||||||
|
|
||||||
|
module NumberToNumber = {
|
||||||
|
let make = (name, fn) =>
|
||||||
|
FnDefinition.make(
|
||||||
|
~name,
|
||||||
|
~inputs=[FRTypeNumber],
|
||||||
|
~run=(_, inputs, _) => {
|
||||||
|
inputs
|
||||||
|
->getOrError(0)
|
||||||
|
->E.R.bind(Prepare.oneNumber)
|
||||||
|
->E.R2.fmap(fn)
|
||||||
|
->E.R2.fmap(Wrappers.evNumber)
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
module ArrayNumberDist = {
|
||||||
|
let make = (name, fn) => {
|
||||||
|
FnDefinition.make(
|
||||||
|
~name,
|
||||||
|
~inputs=[FRTypeArray(FRTypeNumber)],
|
||||||
|
~run=(_, inputs, _) =>
|
||||||
|
Prepare.ToTypedArray.numbers(inputs)
|
||||||
|
->E.R.bind(r => E.A.length(r) === 0 ? Error("List is empty") : Ok(r))
|
||||||
|
->E.R.bind(fn),
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
let make2 = (name, fn) => {
|
||||||
|
FnDefinition.make(
|
||||||
|
~name,
|
||||||
|
~inputs=[FRTypeArray(FRTypeAny)],
|
||||||
|
~run=(_, inputs, _) =>
|
||||||
|
Prepare.ToTypedArray.numbers(inputs)
|
||||||
|
->E.R.bind(r => E.A.length(r) === 0 ? Error("List is empty") : Ok(r))
|
||||||
|
->E.R.bind(fn),
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let library = [
|
||||||
|
Function.make(
|
||||||
|
~name="floor",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`floor(3.5)`],
|
||||||
|
~definitions=[NumberToNumber.make("floor", Js.Math.floor_float)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="ceiling",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`ceil(3.5)`],
|
||||||
|
~definitions=[NumberToNumber.make("ceil", Js.Math.ceil_float)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="absolute value",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`abs(3.5)`],
|
||||||
|
~definitions=[NumberToNumber.make("abs", Js.Math.abs_float)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="exponent",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`exp(3.5)`],
|
||||||
|
~definitions=[NumberToNumber.make("exp", Js.Math.exp)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="log",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`log(3.5)`],
|
||||||
|
~definitions=[NumberToNumber.make("log", Js.Math.log)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="log base 10",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`log10(3.5)`],
|
||||||
|
~definitions=[NumberToNumber.make("log10", Js.Math.log10)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="log base 2",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`log2(3.5)`],
|
||||||
|
~definitions=[NumberToNumber.make("log2", Js.Math.log2)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="round",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`round(3.5)`],
|
||||||
|
~definitions=[NumberToNumber.make("round", Js.Math.round)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="sum",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`sum([3,5,2])`],
|
||||||
|
~definitions=[ArrayNumberDist.make("sum", r => r->E.A.Floats.sum->Wrappers.evNumber->Ok)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="product",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`product([3,5,2])`],
|
||||||
|
~definitions=[
|
||||||
|
ArrayNumberDist.make("product", r => r->E.A.Floats.product->Wrappers.evNumber->Ok),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="min",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`min([3,5,2])`],
|
||||||
|
~definitions=[ArrayNumberDist.make("min", r => r->E.A.Floats.min->Wrappers.evNumber->Ok)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="max",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`max([3,5,2])`],
|
||||||
|
~definitions=[ArrayNumberDist.make("max", r => r->E.A.Floats.max->Wrappers.evNumber->Ok)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="mean",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`mean([3,5,2])`],
|
||||||
|
~definitions=[ArrayNumberDist.make("mean", r => r->E.A.Floats.mean->Wrappers.evNumber->Ok)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="geometric mean",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`geomean([3,5,2])`],
|
||||||
|
~definitions=[
|
||||||
|
ArrayNumberDist.make("geomean", r => r->E.A.Floats.geomean->Wrappers.evNumber->Ok),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="standard deviation",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`stdev([3,5,2,3,5])`],
|
||||||
|
~definitions=[ArrayNumberDist.make("stdev", r => r->E.A.Floats.stdev->Wrappers.evNumber->Ok)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="variance",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`variance([3,5,2,3,5])`],
|
||||||
|
~definitions=[
|
||||||
|
ArrayNumberDist.make("variance", r => r->E.A.Floats.variance->Wrappers.evNumber->Ok),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="sort",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtArray,
|
||||||
|
~examples=[`sort([3,5,2,3,5])`],
|
||||||
|
~definitions=[
|
||||||
|
ArrayNumberDist.make("sort", r =>
|
||||||
|
r->E.A.Floats.sort->E.A2.fmap(Wrappers.evNumber)->Wrappers.evArray->Ok
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="cumulative sum",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtArray,
|
||||||
|
~examples=[`cumsum([3,5,2,3,5])`],
|
||||||
|
~definitions=[
|
||||||
|
ArrayNumberDist.make("cumsum", r =>
|
||||||
|
r->E.A.Floats.cumSum->E.A2.fmap(Wrappers.evNumber)->Wrappers.evArray->Ok
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="cumulative prod",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtArray,
|
||||||
|
~examples=[`cumprod([3,5,2,3,5])`],
|
||||||
|
~definitions=[
|
||||||
|
ArrayNumberDist.make("cumprod", r =>
|
||||||
|
r->E.A.Floats.cumProd->E.A2.fmap(Wrappers.evNumber)->Wrappers.evArray->Ok
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="diff",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtArray,
|
||||||
|
~examples=[`diff([3,5,2,3,5])`],
|
||||||
|
~definitions=[
|
||||||
|
ArrayNumberDist.make("diff", r =>
|
||||||
|
r->E.A.Floats.diff->E.A2.fmap(Wrappers.evNumber)->Wrappers.evArray->Ok
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,73 @@
|
||||||
|
open FunctionRegistry_Core
|
||||||
|
open FunctionRegistry_Helpers
|
||||||
|
|
||||||
|
let nameSpace = "Pointset"
|
||||||
|
let requiresNamespace = true
|
||||||
|
|
||||||
|
let inputsTodist = (inputs: array<FunctionRegistry_Core.frValue>, makeDist) => {
|
||||||
|
let array = inputs->getOrError(0)->E.R.bind(Prepare.ToValueArray.Array.openA)
|
||||||
|
let xyCoords =
|
||||||
|
array->E.R.bind(xyCoords =>
|
||||||
|
xyCoords
|
||||||
|
->E.A2.fmap(xyCoord =>
|
||||||
|
[xyCoord]->Prepare.ToValueArray.Record.twoArgs->E.R.bind(Prepare.ToValueTuple.twoNumbers)
|
||||||
|
)
|
||||||
|
->E.A.R.firstErrorOrOpen
|
||||||
|
)
|
||||||
|
let expressionValue =
|
||||||
|
xyCoords
|
||||||
|
->E.R.bind(r => r->XYShape.T.makeFromZipped->E.R2.errMap(XYShape.Error.toString))
|
||||||
|
->E.R2.fmap(r => ReducerInterface_InternalExpressionValue.IEvDistribution(
|
||||||
|
PointSet(makeDist(r)),
|
||||||
|
))
|
||||||
|
expressionValue
|
||||||
|
}
|
||||||
|
|
||||||
|
let library = [
|
||||||
|
Function.make(
|
||||||
|
~name="makeContinuous",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~examples=[
|
||||||
|
`Pointset.makeContinuous([
|
||||||
|
{x: 0, y: 0.2},
|
||||||
|
{x: 1, y: 0.7},
|
||||||
|
{x: 2, y: 0.8},
|
||||||
|
{x: 3, y: 0.2}
|
||||||
|
])`,
|
||||||
|
],
|
||||||
|
~output=ReducerInterface_InternalExpressionValue.EvtDistribution,
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="makeContinuous",
|
||||||
|
~inputs=[FRTypeArray(FRTypeRecord([("x", FRTypeNumeric), ("y", FRTypeNumeric)]))],
|
||||||
|
~run=(_, inputs, _) => inputsTodist(inputs, r => Continuous(Continuous.make(r))),
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="makeDiscrete",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~examples=[
|
||||||
|
`Pointset.makeDiscrete([
|
||||||
|
{x: 0, y: 0.2},
|
||||||
|
{x: 1, y: 0.7},
|
||||||
|
{x: 2, y: 0.8},
|
||||||
|
{x: 3, y: 0.2}
|
||||||
|
])`,
|
||||||
|
],
|
||||||
|
~output=ReducerInterface_InternalExpressionValue.EvtDistribution,
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="makeDiscrete",
|
||||||
|
~inputs=[FRTypeArray(FRTypeRecord([("x", FRTypeNumeric), ("y", FRTypeNumeric)]))],
|
||||||
|
~run=(_, inputs, _) => inputsTodist(inputs, r => Discrete(Discrete.make(r))),
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,89 @@
|
||||||
|
open FunctionRegistry_Core
|
||||||
|
|
||||||
|
let nameSpace = "Dist"
|
||||||
|
let requiresNamespace = true
|
||||||
|
|
||||||
|
let runScoring = (estimate, answer, prior, env) => {
|
||||||
|
GenericDist.Score.logScore(~estimate, ~answer, ~prior, ~env)
|
||||||
|
->E.R2.fmap(FunctionRegistry_Helpers.Wrappers.evNumber)
|
||||||
|
->E.R2.errMap(DistributionTypes.Error.toString)
|
||||||
|
}
|
||||||
|
|
||||||
|
let library = [
|
||||||
|
Function.make(
|
||||||
|
~name="logScore",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[
|
||||||
|
"Dist.logScore({estimate: normal(5,2), answer: normal(5.2,1), prior: normal(5.5,3)})",
|
||||||
|
"Dist.logScore({estimate: normal(5,2), answer: normal(5.2,1)})",
|
||||||
|
"Dist.logScore({estimate: normal(5,2), answer: 4.5})",
|
||||||
|
],
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="logScore",
|
||||||
|
~inputs=[
|
||||||
|
FRTypeRecord([
|
||||||
|
("estimate", FRTypeDist),
|
||||||
|
("answer", FRTypeDistOrNumber),
|
||||||
|
("prior", FRTypeDist),
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
~run=(_, inputs, env) => {
|
||||||
|
switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.threeArgs(inputs) {
|
||||||
|
| Ok([FRValueDist(estimate), FRValueDistOrNumber(FRValueDist(d)), FRValueDist(prior)]) =>
|
||||||
|
runScoring(estimate, Score_Dist(d), Some(prior), env)
|
||||||
|
| Ok([
|
||||||
|
FRValueDist(estimate),
|
||||||
|
FRValueDistOrNumber(FRValueNumber(d)),
|
||||||
|
FRValueDist(prior),
|
||||||
|
]) =>
|
||||||
|
runScoring(estimate, Score_Scalar(d), Some(prior), env)
|
||||||
|
| Error(e) => Error(e)
|
||||||
|
| _ => Error(FunctionRegistry_Helpers.impossibleError)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="logScore",
|
||||||
|
~inputs=[FRTypeRecord([("estimate", FRTypeDist), ("answer", FRTypeDistOrNumber)])],
|
||||||
|
~run=(_, inputs, env) => {
|
||||||
|
switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.twoArgs(inputs) {
|
||||||
|
| Ok([FRValueDist(estimate), FRValueDistOrNumber(FRValueDist(d))]) =>
|
||||||
|
runScoring(estimate, Score_Dist(d), None, env)
|
||||||
|
| Ok([FRValueDist(estimate), FRValueDistOrNumber(FRValueNumber(d))]) =>
|
||||||
|
runScoring(estimate, Score_Scalar(d), None, env)
|
||||||
|
| Error(e) => Error(e)
|
||||||
|
| _ => Error(FunctionRegistry_Helpers.impossibleError)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="klDivergence",
|
||||||
|
~nameSpace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~requiresNamespace,
|
||||||
|
~examples=["Dist.klDivergence(normal(5,2), normal(5,1.5))"],
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="klDivergence",
|
||||||
|
~inputs=[FRTypeDist, FRTypeDist],
|
||||||
|
~run=(_, inputs, env) => {
|
||||||
|
switch inputs {
|
||||||
|
| [FRValueDist(estimate), FRValueDist(d)] =>
|
||||||
|
runScoring(estimate, Score_Dist(d), None, env)
|
||||||
|
| _ => Error(FunctionRegistry_Helpers.impossibleError)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
]
|
|
@ -1,3 +1,6 @@
|
||||||
|
// Only Bindings as the global module is supported
|
||||||
|
// Other module operations such as import export will be prepreocessed jobs
|
||||||
|
|
||||||
module ExpressionT = Reducer_Expression_T
|
module ExpressionT = Reducer_Expression_T
|
||||||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||||
open Reducer_ErrorValue
|
open Reducer_ErrorValue
|
||||||
|
@ -75,10 +78,10 @@ let emptyBindings = emptyModule
|
||||||
let fromTypeScriptBindings = ReducerInterface_InternalExpressionValue.nameSpaceFromTypeScriptBindings
|
let fromTypeScriptBindings = ReducerInterface_InternalExpressionValue.nameSpaceFromTypeScriptBindings
|
||||||
let toTypeScriptBindings = ReducerInterface_InternalExpressionValue.nameSpaceToTypeScriptBindings
|
let toTypeScriptBindings = ReducerInterface_InternalExpressionValue.nameSpaceToTypeScriptBindings
|
||||||
|
|
||||||
let toExpressionValue = (nameSpace: t): internalExpressionValue => IEvModule(nameSpace)
|
let toExpressionValue = (nameSpace: t): internalExpressionValue => IEvBindings(nameSpace)
|
||||||
let fromExpressionValue = (aValue: internalExpressionValue): t =>
|
let fromExpressionValue = (aValue: internalExpressionValue): t =>
|
||||||
switch aValue {
|
switch aValue {
|
||||||
| IEvModule(nameSpace) => nameSpace
|
| IEvBindings(nameSpace) => nameSpace
|
||||||
| _ => emptyModule
|
| _ => emptyModule
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,6 +129,17 @@ let functionNotFoundErrorFFIFn = (functionName: string): ExpressionT.ffiFn => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let convertOptionToFfiFnReturningResult = (
|
||||||
|
myFunctionName: string,
|
||||||
|
myFunction: ExpressionT.optionFfiFnReturningResult,
|
||||||
|
): ExpressionT.ffiFn => {
|
||||||
|
(args: array<InternalExpressionValue.t>, environment) => {
|
||||||
|
myFunction(args, environment)->Belt.Option.getWithDefault(
|
||||||
|
functionNotFoundErrorFFIFn(myFunctionName)(args, environment),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let convertOptionToFfiFn = (
|
let convertOptionToFfiFn = (
|
||||||
myFunctionName: string,
|
myFunctionName: string,
|
||||||
myFunction: ExpressionT.optionFfiFn,
|
myFunction: ExpressionT.optionFfiFn,
|
||||||
|
@ -159,4 +173,15 @@ let defineFunction = (nameSpace: t, identifier: string, value: ExpressionT.optio
|
||||||
nameSpace->define(identifier, convertOptionToFfiFn(identifier, value)->eLambdaFFIValue)
|
nameSpace->define(identifier, convertOptionToFfiFn(identifier, value)->eLambdaFFIValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
let emptyStdLib: t = emptyModule->defineBool("stdlib", true)
|
let defineFunctionReturningResult = (
|
||||||
|
nameSpace: t,
|
||||||
|
identifier: string,
|
||||||
|
value: ExpressionT.optionFfiFnReturningResult,
|
||||||
|
): t => {
|
||||||
|
nameSpace->define(
|
||||||
|
identifier,
|
||||||
|
convertOptionToFfiFnReturningResult(identifier, value)->eLambdaFFIValue,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let emptyStdLib: t = emptyModule->defineBool("_standardLibrary", true)
|
|
@ -3,15 +3,15 @@ module ExpressionT = Reducer_Expression_T
|
||||||
module ExternalLibrary = ReducerInterface.ExternalLibrary
|
module ExternalLibrary = ReducerInterface.ExternalLibrary
|
||||||
module Lambda = Reducer_Expression_Lambda
|
module Lambda = Reducer_Expression_Lambda
|
||||||
module MathJs = Reducer_MathJs
|
module MathJs = Reducer_MathJs
|
||||||
module Module = Reducer_Module
|
module Bindings = Reducer_Bindings
|
||||||
module Result = Belt.Result
|
module Result = Belt.Result
|
||||||
module TypeBuilder = Reducer_Type_TypeBuilder
|
module TypeBuilder = Reducer_Type_TypeBuilder
|
||||||
open ReducerInterface_InternalExpressionValue
|
open ReducerInterface_InternalExpressionValue
|
||||||
open Reducer_ErrorValue
|
open Reducer_ErrorValue
|
||||||
|
|
||||||
/*
|
/*
|
||||||
MathJs provides default implementations for builtins
|
MathJs provides default implementations for built-ins
|
||||||
This is where all the expected builtins like + = * / sin cos log ln etc are handled
|
This is where all the expected built-ins like + = * / sin cos log ln etc are handled
|
||||||
DO NOT try to add external function mapping here!
|
DO NOT try to add external function mapping here!
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -49,9 +49,9 @@ let callInternal = (call: functionCall, environment, reducer: ExpressionT.reduce
|
||||||
}
|
}
|
||||||
|
|
||||||
let moduleAtIndex = (nameSpace: nameSpace, sIndex) =>
|
let moduleAtIndex = (nameSpace: nameSpace, sIndex) =>
|
||||||
switch Module.get(nameSpace, sIndex) {
|
switch Bindings.get(nameSpace, sIndex) {
|
||||||
| Some(value) => value->Ok
|
| Some(value) => value->Ok
|
||||||
| None => RERecordPropertyNotFound("Module property not found", sIndex)->Error
|
| None => RERecordPropertyNotFound("Bindings property not found", sIndex)->Error
|
||||||
}
|
}
|
||||||
|
|
||||||
let recordAtIndex = (dict: Belt.Map.String.t<internalExpressionValue>, sIndex) =>
|
let recordAtIndex = (dict: Belt.Map.String.t<internalExpressionValue>, sIndex) =>
|
||||||
|
@ -81,19 +81,19 @@ let callInternal = (call: functionCall, environment, reducer: ExpressionT.reduce
|
||||||
}
|
}
|
||||||
|
|
||||||
let doSetBindings = (bindings: nameSpace, symbol: string, value: internalExpressionValue) => {
|
let doSetBindings = (bindings: nameSpace, symbol: string, value: internalExpressionValue) => {
|
||||||
Module.set(bindings, symbol, value)->IEvModule->Ok
|
Bindings.set(bindings, symbol, value)->IEvBindings->Ok
|
||||||
}
|
}
|
||||||
|
|
||||||
let doSetTypeAliasBindings = (
|
let doSetTypeAliasBindings = (
|
||||||
bindings: nameSpace,
|
bindings: nameSpace,
|
||||||
symbol: string,
|
symbol: string,
|
||||||
value: internalExpressionValue,
|
value: internalExpressionValue,
|
||||||
) => Module.setTypeAlias(bindings, symbol, value)->IEvModule->Ok
|
) => Bindings.setTypeAlias(bindings, symbol, value)->IEvBindings->Ok
|
||||||
|
|
||||||
let doSetTypeOfBindings = (bindings: nameSpace, symbol: string, value: internalExpressionValue) =>
|
let doSetTypeOfBindings = (bindings: nameSpace, symbol: string, value: internalExpressionValue) =>
|
||||||
Module.setTypeOf(bindings, symbol, value)->IEvModule->Ok
|
Bindings.setTypeOf(bindings, symbol, value)->IEvBindings->Ok
|
||||||
|
|
||||||
let doExportBindings = (bindings: nameSpace) => bindings->Module.toExpressionValue->Ok
|
let doExportBindings = (bindings: nameSpace) => bindings->Bindings.toExpressionValue->Ok
|
||||||
|
|
||||||
let doKeepArray = (aValueArray, aLambdaValue) => {
|
let doKeepArray = (aValueArray, aLambdaValue) => {
|
||||||
let rMappedList = aValueArray->Belt.Array.reduceReverse(Ok(list{}), (rAcc, elem) =>
|
let rMappedList = aValueArray->Belt.Array.reduceReverse(Ok(list{}), (rAcc, elem) =>
|
||||||
|
@ -149,6 +149,27 @@ let callInternal = (call: functionCall, environment, reducer: ExpressionT.reduce
|
||||||
doLambdaCall(aLambdaValue, list{IEvNumber(a), IEvNumber(b), IEvNumber(c)})
|
doLambdaCall(aLambdaValue, list{IEvNumber(a), IEvNumber(b), IEvNumber(c)})
|
||||||
SampleSetDist.map3(~fn, ~t1, ~t2, ~t3)->toType
|
SampleSetDist.map3(~fn, ~t1, ~t2, ~t3)->toType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let parseSampleSetArray = (arr: array<internalExpressionValue>): option<
|
||||||
|
array<SampleSetDist.t>,
|
||||||
|
> => {
|
||||||
|
let parseSampleSet = (value: internalExpressionValue): option<SampleSetDist.t> =>
|
||||||
|
switch value {
|
||||||
|
| IEvDistribution(SampleSet(dist)) => Some(dist)
|
||||||
|
| _ => None
|
||||||
|
}
|
||||||
|
E.A.O.openIfAllSome(E.A.fmap(parseSampleSet, arr))
|
||||||
|
}
|
||||||
|
|
||||||
|
let mapN = (aValueArray: array<internalExpressionValue>, aLambdaValue) => {
|
||||||
|
switch parseSampleSetArray(aValueArray) {
|
||||||
|
| Some(t1) =>
|
||||||
|
let fn = a => doLambdaCall(aLambdaValue, list{IEvArray(E.A.fmap(x => IEvNumber(x), a))})
|
||||||
|
SampleSetDist.mapN(~fn, ~t1)->toType
|
||||||
|
| None =>
|
||||||
|
Error(REFunctionNotFound(call->functionCallToCallSignature->functionCallSignatureToString))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let doReduceArray = (aValueArray, initialValue, aLambdaValue) => {
|
let doReduceArray = (aValueArray, initialValue, aLambdaValue) => {
|
||||||
|
@ -169,16 +190,16 @@ let callInternal = (call: functionCall, environment, reducer: ExpressionT.reduce
|
||||||
|
|
||||||
switch call {
|
switch call {
|
||||||
| ("$_atIndex_$", [IEvArray(aValueArray), IEvNumber(fIndex)]) => arrayAtIndex(aValueArray, fIndex)
|
| ("$_atIndex_$", [IEvArray(aValueArray), IEvNumber(fIndex)]) => arrayAtIndex(aValueArray, fIndex)
|
||||||
| ("$_atIndex_$", [IEvModule(dict), IEvString(sIndex)]) => moduleAtIndex(dict, sIndex)
|
| ("$_atIndex_$", [IEvBindings(dict), IEvString(sIndex)]) => moduleAtIndex(dict, sIndex)
|
||||||
| ("$_atIndex_$", [IEvRecord(dict), IEvString(sIndex)]) => recordAtIndex(dict, sIndex)
|
| ("$_atIndex_$", [IEvRecord(dict), IEvString(sIndex)]) => recordAtIndex(dict, sIndex)
|
||||||
| ("$_constructArray_$", [IEvArray(aValueArray)]) => IEvArray(aValueArray)->Ok
|
| ("$_constructArray_$", [IEvArray(aValueArray)]) => IEvArray(aValueArray)->Ok
|
||||||
| ("$_constructRecord_$", [IEvArray(arrayOfPairs)]) => constructRecord(arrayOfPairs)
|
| ("$_constructRecord_$", [IEvArray(arrayOfPairs)]) => constructRecord(arrayOfPairs)
|
||||||
| ("$_exportBindings_$", [IEvModule(nameSpace)]) => doExportBindings(nameSpace)
|
| ("$_exportBindings_$", [IEvBindings(nameSpace)]) => doExportBindings(nameSpace)
|
||||||
| ("$_setBindings_$", [IEvModule(nameSpace), IEvSymbol(symbol), value]) =>
|
| ("$_setBindings_$", [IEvBindings(nameSpace), IEvSymbol(symbol), value]) =>
|
||||||
doSetBindings(nameSpace, symbol, value)
|
doSetBindings(nameSpace, symbol, value)
|
||||||
| ("$_setTypeAliasBindings_$", [IEvModule(nameSpace), IEvTypeIdentifier(symbol), value]) =>
|
| ("$_setTypeAliasBindings_$", [IEvBindings(nameSpace), IEvTypeIdentifier(symbol), value]) =>
|
||||||
doSetTypeAliasBindings(nameSpace, symbol, value)
|
doSetTypeAliasBindings(nameSpace, symbol, value)
|
||||||
| ("$_setTypeOfBindings_$", [IEvModule(nameSpace), IEvSymbol(symbol), value]) =>
|
| ("$_setTypeOfBindings_$", [IEvBindings(nameSpace), IEvSymbol(symbol), value]) =>
|
||||||
doSetTypeOfBindings(nameSpace, symbol, value)
|
doSetTypeOfBindings(nameSpace, symbol, value)
|
||||||
| ("$_typeModifier_memberOf_$", [IEvTypeIdentifier(typeIdentifier), IEvArray(arr)]) =>
|
| ("$_typeModifier_memberOf_$", [IEvTypeIdentifier(typeIdentifier), IEvArray(arr)]) =>
|
||||||
TypeBuilder.typeModifier_memberOf(IEvTypeIdentifier(typeIdentifier), IEvArray(arr))
|
TypeBuilder.typeModifier_memberOf(IEvTypeIdentifier(typeIdentifier), IEvArray(arr))
|
||||||
|
@ -198,7 +219,7 @@ let callInternal = (call: functionCall, environment, reducer: ExpressionT.reduce
|
||||||
| ("$_typeFunction_$", [IEvArray(arr)]) => TypeBuilder.typeFunction(arr)
|
| ("$_typeFunction_$", [IEvArray(arr)]) => TypeBuilder.typeFunction(arr)
|
||||||
| ("$_typeTuple_$", [IEvArray(elems)]) => TypeBuilder.typeTuple(elems)
|
| ("$_typeTuple_$", [IEvArray(elems)]) => TypeBuilder.typeTuple(elems)
|
||||||
| ("$_typeArray_$", [elem]) => TypeBuilder.typeArray(elem)
|
| ("$_typeArray_$", [elem]) => TypeBuilder.typeArray(elem)
|
||||||
| ("$_typeRecord_$", [IEvArray(arrayOfPairs)]) => TypeBuilder.typeRecord(arrayOfPairs)
|
| ("$_typeRecord_$", [IEvRecord(propertyMap)]) => TypeBuilder.typeRecord(propertyMap)
|
||||||
| ("concat", [IEvArray(aValueArray), IEvArray(bValueArray)]) =>
|
| ("concat", [IEvArray(aValueArray), IEvArray(bValueArray)]) =>
|
||||||
doAddArray(aValueArray, bValueArray)
|
doAddArray(aValueArray, bValueArray)
|
||||||
| ("concat", [IEvString(aValueString), IEvString(bValueString)]) =>
|
| ("concat", [IEvString(aValueString), IEvString(bValueString)]) =>
|
||||||
|
@ -230,6 +251,8 @@ let callInternal = (call: functionCall, environment, reducer: ExpressionT.reduce
|
||||||
],
|
],
|
||||||
) =>
|
) =>
|
||||||
SampleMap.map3(dist1, dist2, dist3, aLambdaValue)
|
SampleMap.map3(dist1, dist2, dist3, aLambdaValue)
|
||||||
|
| ("mapSamplesN", [IEvArray(aValueArray), IEvLambda(aLambdaValue)]) =>
|
||||||
|
SampleMap.mapN(aValueArray, aLambdaValue)
|
||||||
| ("reduce", [IEvArray(aValueArray), initialValue, IEvLambda(aLambdaValue)]) =>
|
| ("reduce", [IEvArray(aValueArray), initialValue, IEvLambda(aLambdaValue)]) =>
|
||||||
doReduceArray(aValueArray, initialValue, aLambdaValue)
|
doReduceArray(aValueArray, initialValue, aLambdaValue)
|
||||||
| ("reduceReverse", [IEvArray(aValueArray), initialValue, IEvLambda(aLambdaValue)]) =>
|
| ("reduceReverse", [IEvArray(aValueArray), initialValue, IEvLambda(aLambdaValue)]) =>
|
||||||
|
@ -246,7 +269,6 @@ let callInternal = (call: functionCall, environment, reducer: ExpressionT.reduce
|
||||||
Error(REFunctionNotFound(call->functionCallToCallSignature->functionCallSignatureToString)) // Report full type signature as error
|
Error(REFunctionNotFound(call->functionCallToCallSignature->functionCallSignatureToString)) // Report full type signature as error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Reducer uses Result monad while reducing expressions
|
Reducer uses Result monad while reducing expressions
|
||||||
*/
|
*/
|
||||||
|
@ -255,11 +277,10 @@ let dispatch = (call: functionCall, environment, reducer: ExpressionT.reducerFn)
|
||||||
errorValue,
|
errorValue,
|
||||||
> =>
|
> =>
|
||||||
try {
|
try {
|
||||||
let callInternalWithReducer = (call, environment) => callInternal(call, environment, reducer)
|
|
||||||
let (fn, args) = call
|
let (fn, args) = call
|
||||||
// There is a bug that prevents string match in patterns
|
// There is a bug that prevents string match in patterns
|
||||||
// So we have to recreate a copy of the string
|
// So we have to recreate a copy of the string
|
||||||
ExternalLibrary.dispatch((Js.String.make(fn), args), environment, callInternalWithReducer)
|
ExternalLibrary.dispatch((Js.String.make(fn), args), environment, reducer, callInternal)
|
||||||
} catch {
|
} catch {
|
||||||
| Js.Exn.Error(obj) => REJavaScriptExn(Js.Exn.message(obj), Js.Exn.name(obj))->Error
|
| Js.Exn.Error(obj) => REJavaScriptExn(Js.Exn.message(obj), Js.Exn.name(obj))->Error
|
||||||
| _ => RETodo("unhandled rescript exception")->Error
|
| _ => RETodo("unhandled rescript exception")->Error
|
||||||
|
|
|
@ -9,7 +9,7 @@ module ExpressionBuilder = Reducer_Expression_ExpressionBuilder
|
||||||
module ExpressionT = Reducer_Expression_T
|
module ExpressionT = Reducer_Expression_T
|
||||||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||||
module ExpressionWithContext = Reducer_ExpressionWithContext
|
module ExpressionWithContext = Reducer_ExpressionWithContext
|
||||||
module Module = Reducer_Module
|
module Bindings = Reducer_Bindings
|
||||||
module Result = Belt.Result
|
module Result = Belt.Result
|
||||||
open Reducer_Expression_ExpressionBuilder
|
open Reducer_Expression_ExpressionBuilder
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ let dispatchMacroCall = (
|
||||||
let rExternalBindingsValue = reduceExpression(bindingExpr, bindings, environment)
|
let rExternalBindingsValue = reduceExpression(bindingExpr, bindings, environment)
|
||||||
|
|
||||||
rExternalBindingsValue->Result.flatMap(nameSpaceValue => {
|
rExternalBindingsValue->Result.flatMap(nameSpaceValue => {
|
||||||
let newBindings = Module.fromExpressionValue(nameSpaceValue)
|
let newBindings = Bindings.fromExpressionValue(nameSpaceValue)
|
||||||
|
|
||||||
let rNewStatement = BindingsReplacer.replaceSymbols(newBindings, statement)
|
let rNewStatement = BindingsReplacer.replaceSymbols(newBindings, statement)
|
||||||
rNewStatement->Result.map(boundStatement =>
|
rNewStatement->Result.map(boundStatement =>
|
||||||
|
@ -144,7 +144,7 @@ let dispatchMacroCall = (
|
||||||
let ifTrueBlock = eBlock(list{ifTrue})
|
let ifTrueBlock = eBlock(list{ifTrue})
|
||||||
ExpressionWithContext.withContext(ifTrueBlock, bindings)->Ok
|
ExpressionWithContext.withContext(ifTrueBlock, bindings)->Ok
|
||||||
}
|
}
|
||||||
| _ => REExpectedType("Boolean")->Error
|
| _ => REExpectedType("Boolean", "")->Error
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ type errorValue =
|
||||||
| REArrayIndexNotFound(string, int)
|
| REArrayIndexNotFound(string, int)
|
||||||
| REAssignmentExpected
|
| REAssignmentExpected
|
||||||
| REDistributionError(DistributionTypes.error)
|
| REDistributionError(DistributionTypes.error)
|
||||||
| REExpectedType(string)
|
| REExpectedType(string, string)
|
||||||
| REExpressionExpected
|
| REExpressionExpected
|
||||||
| REFunctionExpected(string)
|
| REFunctionExpected(string)
|
||||||
| REFunctionNotFound(string)
|
| REFunctionNotFound(string)
|
||||||
|
@ -55,6 +55,6 @@ let errorToString = err =>
|
||||||
| RESymbolNotFound(symbolName) => `${symbolName} is not defined`
|
| RESymbolNotFound(symbolName) => `${symbolName} is not defined`
|
||||||
| RESyntaxError(desc, _) => `Syntax Error: ${desc}`
|
| RESyntaxError(desc, _) => `Syntax Error: ${desc}`
|
||||||
| RETodo(msg) => `TODO: ${msg}`
|
| RETodo(msg) => `TODO: ${msg}`
|
||||||
| REExpectedType(typeName) => `Expected type: ${typeName}`
|
| REExpectedType(typeName, valueString) => `Expected type: ${typeName} but got: ${valueString}`
|
||||||
| REUnitNotFound(unitName) => `Unit not found: ${unitName}`
|
| REUnitNotFound(unitName) => `Unit not found: ${unitName}`
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
// There are switch stament cases in the code which are impossible to reach by design.
|
// There are switch statement cases in the code which are impossible to reach by design.
|
||||||
// ImpossibleException is a sign of programming error.
|
// ImpossibleException is a sign of programming error.
|
||||||
exception ImpossibleException
|
exception ImpossibleException(string)
|
||||||
|
|
|
@ -6,7 +6,7 @@ module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||||
module Lambda = Reducer_Expression_Lambda
|
module Lambda = Reducer_Expression_Lambda
|
||||||
module Macro = Reducer_Expression_Macro
|
module Macro = Reducer_Expression_Macro
|
||||||
module MathJs = Reducer_MathJs
|
module MathJs = Reducer_MathJs
|
||||||
module Module = Reducer_Module
|
module Bindings = Reducer_Bindings
|
||||||
module Result = Belt.Result
|
module Result = Belt.Result
|
||||||
module T = Reducer_Expression_T
|
module T = Reducer_Expression_T
|
||||||
|
|
||||||
|
@ -121,10 +121,10 @@ let evaluateUsingOptions = (
|
||||||
ReducerInterface_ExternalExpressionValue.defaultEnvironment,
|
ReducerInterface_ExternalExpressionValue.defaultEnvironment,
|
||||||
)
|
)
|
||||||
|
|
||||||
let mergedBindings: InternalExpressionValue.nameSpace = Module.merge(
|
let mergedBindings: InternalExpressionValue.nameSpace = Bindings.merge(
|
||||||
ReducerInterface_StdLib.internalStdLib,
|
ReducerInterface_StdLib.internalStdLib,
|
||||||
Belt.Option.map(externalBindings, Module.fromTypeScriptBindings)->Belt.Option.getWithDefault(
|
Belt.Option.map(externalBindings, Bindings.fromTypeScriptBindings)->Belt.Option.getWithDefault(
|
||||||
Module.emptyModule,
|
Bindings.emptyModule,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ module ErrorValue = Reducer_ErrorValue
|
||||||
module ExpressionT = Reducer_Expression_T
|
module ExpressionT = Reducer_Expression_T
|
||||||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||||
module Result = Belt.Result
|
module Result = Belt.Result
|
||||||
module Module = Reducer_Module
|
module Bindings = Reducer_Bindings
|
||||||
|
|
||||||
type bindings = ExpressionT.bindings
|
type bindings = ExpressionT.bindings
|
||||||
type context = bindings
|
type context = bindings
|
||||||
|
@ -41,7 +41,7 @@ let toString = expressionWithContext =>
|
||||||
| ExpressionNoContext(expr) => ExpressionT.toString(expr)
|
| ExpressionNoContext(expr) => ExpressionT.toString(expr)
|
||||||
| ExpressionWithContext(expr, context) =>
|
| ExpressionWithContext(expr, context) =>
|
||||||
`${ExpressionT.toString(expr)} context: ${context
|
`${ExpressionT.toString(expr)} context: ${context
|
||||||
->Module.toExpressionValue
|
->Bindings.toExpressionValue
|
||||||
->InternalExpressionValue.toString}`
|
->InternalExpressionValue.toString}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ module ErrorValue = Reducer_ErrorValue
|
||||||
module ExpressionT = Reducer_Expression_T
|
module ExpressionT = Reducer_Expression_T
|
||||||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||||
module Result = Belt.Result
|
module Result = Belt.Result
|
||||||
module Module = Reducer_Module
|
module Bindings = Reducer_Bindings
|
||||||
|
|
||||||
type errorValue = Reducer_ErrorValue.errorValue
|
type errorValue = Reducer_ErrorValue.errorValue
|
||||||
type expression = ExpressionT.expression
|
type expression = ExpressionT.expression
|
||||||
|
@ -42,8 +42,8 @@ and replaceSymbolsOnExpressionList = (bindings, list) => {
|
||||||
}
|
}
|
||||||
and replaceSymbolOnValue = (bindings, evValue: internalExpressionValue) =>
|
and replaceSymbolOnValue = (bindings, evValue: internalExpressionValue) =>
|
||||||
switch evValue {
|
switch evValue {
|
||||||
| IEvSymbol(symbol) => Module.getWithDefault(bindings, symbol, evValue)->Ok
|
| IEvSymbol(symbol) => Bindings.getWithDefault(bindings, symbol, evValue)->Ok
|
||||||
| IEvCall(symbol) => Module.getWithDefault(bindings, symbol, evValue)->checkIfCallable
|
| IEvCall(symbol) => Bindings.getWithDefault(bindings, symbol, evValue)->checkIfCallable
|
||||||
| _ => evValue->Ok
|
| _ => evValue->Ok
|
||||||
}
|
}
|
||||||
and checkIfCallable = (evValue: internalExpressionValue) =>
|
and checkIfCallable = (evValue: internalExpressionValue) =>
|
||||||
|
|
|
@ -2,7 +2,7 @@ module BBindingsReplacer = Reducer_Expression_BindingsReplacer
|
||||||
module BErrorValue = Reducer_ErrorValue
|
module BErrorValue = Reducer_ErrorValue
|
||||||
module BExpressionT = Reducer_Expression_T
|
module BExpressionT = Reducer_Expression_T
|
||||||
module BInternalExpressionValue = ReducerInterface_InternalExpressionValue
|
module BInternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||||
module BModule = Reducer_Module
|
module BBindings = Reducer_Bindings
|
||||||
|
|
||||||
type errorValue = BErrorValue.errorValue
|
type errorValue = BErrorValue.errorValue
|
||||||
type expression = BExpressionT.expression
|
type expression = BExpressionT.expression
|
||||||
|
@ -15,7 +15,7 @@ let eArray = anArray => anArray->BInternalExpressionValue.IEvArray->BExpressionT
|
||||||
let eArrayString = anArray => anArray->BInternalExpressionValue.IEvArrayString->BExpressionT.EValue
|
let eArrayString = anArray => anArray->BInternalExpressionValue.IEvArrayString->BExpressionT.EValue
|
||||||
|
|
||||||
let eBindings = (anArray: array<(string, BInternalExpressionValue.t)>) =>
|
let eBindings = (anArray: array<(string, BInternalExpressionValue.t)>) =>
|
||||||
anArray->BModule.fromArray->BModule.toExpressionValue->BExpressionT.EValue
|
anArray->BBindings.fromArray->BBindings.toExpressionValue->BExpressionT.EValue
|
||||||
|
|
||||||
let eBool = aBool => aBool->BInternalExpressionValue.IEvBool->BExpressionT.EValue
|
let eBool = aBool => aBool->BInternalExpressionValue.IEvBool->BExpressionT.EValue
|
||||||
|
|
||||||
|
@ -35,12 +35,12 @@ let eLambda = (
|
||||||
BInternalExpressionValue.IEvLambda({
|
BInternalExpressionValue.IEvLambda({
|
||||||
parameters: parameters,
|
parameters: parameters,
|
||||||
context: context,
|
context: context,
|
||||||
body: NotFFI(expr)->BModule.castExpressionToInternalCode,
|
body: NotFFI(expr)->BBindings.castExpressionToInternalCode,
|
||||||
})->BExpressionT.EValue
|
})->BExpressionT.EValue
|
||||||
}
|
}
|
||||||
|
|
||||||
let eLambdaFFI = (ffiFn: ffiFn) => {
|
let eLambdaFFI = (ffiFn: ffiFn) => {
|
||||||
ffiFn->BModule.eLambdaFFIValue->BExpressionT.EValue
|
ffiFn->BBindings.eLambdaFFIValue->BExpressionT.EValue
|
||||||
}
|
}
|
||||||
|
|
||||||
let eNumber = aNumber => aNumber->BInternalExpressionValue.IEvNumber->BExpressionT.EValue
|
let eNumber = aNumber => aNumber->BInternalExpressionValue.IEvNumber->BExpressionT.EValue
|
||||||
|
@ -57,7 +57,7 @@ let eList = (list: list<expression>): expression => list->BExpressionT.EList
|
||||||
let eBlock = (exprs: list<expression>): expression => eFunction("$$_block_$$", exprs)
|
let eBlock = (exprs: list<expression>): expression => eFunction("$$_block_$$", exprs)
|
||||||
|
|
||||||
let eModule = (nameSpace: BInternalExpressionValue.nameSpace): expression =>
|
let eModule = (nameSpace: BInternalExpressionValue.nameSpace): expression =>
|
||||||
nameSpace->BInternalExpressionValue.IEvModule->BExpressionT.EValue
|
nameSpace->BInternalExpressionValue.IEvBindings->BExpressionT.EValue
|
||||||
|
|
||||||
let eLetStatement = (symbol: string, valueExpression: expression): expression =>
|
let eLetStatement = (symbol: string, valueExpression: expression): expression =>
|
||||||
eFunction("$_let_$", list{eSymbol(symbol), valueExpression})
|
eFunction("$_let_$", list{eSymbol(symbol), valueExpression})
|
||||||
|
|
|
@ -3,7 +3,7 @@ module ErrorValue = Reducer_ErrorValue
|
||||||
module ExpressionBuilder = Reducer_Expression_ExpressionBuilder
|
module ExpressionBuilder = Reducer_Expression_ExpressionBuilder
|
||||||
module ExpressionT = Reducer_Expression_T
|
module ExpressionT = Reducer_Expression_T
|
||||||
module ExpressionValue = ReducerInterface_InternalExpressionValue
|
module ExpressionValue = ReducerInterface_InternalExpressionValue
|
||||||
module Module = Reducer_Module
|
module Bindings = Reducer_Bindings
|
||||||
module Result = Belt.Result
|
module Result = Belt.Result
|
||||||
|
|
||||||
type environment = ReducerInterface_InternalExpressionValue.environment
|
type environment = ReducerInterface_InternalExpressionValue.environment
|
||||||
|
@ -50,7 +50,7 @@ let caseNotFFI = (lambdaValue: ExpressionValue.lambdaValue, expr, args, environm
|
||||||
let bindings = Belt.List.reduce(zippedParameterList, lambdaValue.context, (
|
let bindings = Belt.List.reduce(zippedParameterList, lambdaValue.context, (
|
||||||
acc,
|
acc,
|
||||||
(variable, variableValue),
|
(variable, variableValue),
|
||||||
) => acc->Module.set(variable, variableValue))
|
) => acc->Bindings.set(variable, variableValue))
|
||||||
let newExpression = ExpressionBuilder.eBlock(list{expr})
|
let newExpression = ExpressionBuilder.eBlock(list{expr})
|
||||||
reducer(newExpression, bindings, environment)
|
reducer(newExpression, bindings, environment)
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,12 +30,12 @@ let rec toString = expression =>
|
||||||
switch expression {
|
switch expression {
|
||||||
| EList(list{EValue(IEvCall("$$_block_$$")), ...statements}) =>
|
| EList(list{EValue(IEvCall("$$_block_$$")), ...statements}) =>
|
||||||
`{${Belt.List.map(statements, aValue => toString(aValue))
|
`{${Belt.List.map(statements, aValue => toString(aValue))
|
||||||
->Extra.List.interperse("; ")
|
->Extra.List.intersperse("; ")
|
||||||
->Belt.List.toArray
|
->Belt.List.toArray
|
||||||
->Js.String.concatMany("")}}`
|
->Js.String.concatMany("")}}`
|
||||||
| EList(aList) =>
|
| EList(aList) =>
|
||||||
`(${Belt.List.map(aList, aValue => toString(aValue))
|
`(${Belt.List.map(aList, aValue => toString(aValue))
|
||||||
->Extra.List.interperse(" ")
|
->Extra.List.intersperse(" ")
|
||||||
->Belt.List.toArray
|
->Belt.List.toArray
|
||||||
->Js.String.concatMany("")})`
|
->Js.String.concatMany("")})`
|
||||||
| EValue(aValue) => InternalExpressionValue.toString(aValue)
|
| EValue(aValue) => InternalExpressionValue.toString(aValue)
|
||||||
|
@ -72,6 +72,10 @@ type ffiFn = (
|
||||||
) => result<internalExpressionValue, Reducer_ErrorValue.errorValue>
|
) => result<internalExpressionValue, Reducer_ErrorValue.errorValue>
|
||||||
|
|
||||||
type optionFfiFn = (array<internalExpressionValue>, environment) => option<internalExpressionValue>
|
type optionFfiFn = (array<internalExpressionValue>, environment) => option<internalExpressionValue>
|
||||||
|
type optionFfiFnReturningResult = (
|
||||||
|
array<internalExpressionValue>,
|
||||||
|
environment,
|
||||||
|
) => option<result<internalExpressionValue, Reducer_ErrorValue.errorValue>>
|
||||||
|
|
||||||
type expressionOrFFI =
|
type expressionOrFFI =
|
||||||
| NotFFI(expression)
|
| NotFFI(expression)
|
||||||
|
|
|
@ -3,5 +3,5 @@
|
||||||
*/
|
*/
|
||||||
module ExtraList = Reducer_Extra_List
|
module ExtraList = Reducer_Extra_List
|
||||||
|
|
||||||
let interperse = (anArray, seperator) =>
|
let intersperse = (anArray, seperator) =>
|
||||||
anArray->Belt.List.fromArray->ExtraList.interperse(seperator)->Belt.List.toArray
|
anArray->Belt.List.fromArray->ExtraList.intersperse(seperator)->Belt.List.toArray
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/*
|
/*
|
||||||
Insert seperator between the elements of a list
|
Insert seperator between the elements of a list
|
||||||
*/
|
*/
|
||||||
let rec interperse = (aList, seperator) =>
|
let rec intersperse = (aList, seperator) =>
|
||||||
switch aList {
|
switch aList {
|
||||||
| list{} => list{}
|
| list{} => list{}
|
||||||
| list{a} => list{a}
|
| list{a} => list{a}
|
||||||
| list{a, ...rest} => list{a, seperator, ...interperse(rest, seperator)}
|
| list{a, ...rest} => list{a, seperator, ...intersperse(rest, seperator)}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
module ExpressionBuilder = Reducer_Expression_ExpressionBuilder
|
|
||||||
module ExpressionT = Reducer_Expression_T
|
|
||||||
|
|
||||||
type expression = ExpressionT.expression
|
|
||||||
|
|
||||||
let defaultCaseFFI = (functionName: string): expression => {
|
|
||||||
ExpressionBuilder.eLambdaFFI(Reducer_Module.functionNotFoundErrorFFIFn(functionName))
|
|
||||||
}
|
|
||||||
|
|
||||||
let addGuard = (
|
|
||||||
guard: expression,
|
|
||||||
expression: expression,
|
|
||||||
previousExpression: expression,
|
|
||||||
): expression => ExpressionBuilder.eTernary(guard, expression, previousExpression)
|
|
|
@ -187,13 +187,16 @@ basicLiteral
|
||||||
/ dollarIdentifier
|
/ dollarIdentifier
|
||||||
|
|
||||||
dollarIdentifierWithModule 'identifier'
|
dollarIdentifierWithModule 'identifier'
|
||||||
= head:moduleIdentifier
|
= head:$moduleIdentifier
|
||||||
tail:('.' _nl @$moduleIdentifier)* '.' _nl
|
tail:('.' _nl @$moduleIdentifier)* '.' _nl
|
||||||
final:$dollarIdentifier
|
final:$dollarIdentifier
|
||||||
{ tail.push(final);
|
{
|
||||||
return tail.reduce(function(result, element) {
|
let modifiers = [...tail]
|
||||||
return h.makeFunctionCall(h.postOperatorToFunction['[]'], [result, h.nodeString(element)])
|
modifiers.unshift(head)
|
||||||
}, head)}
|
modifiers.push(final)
|
||||||
|
let modifiedIdentifier = modifiers.join('.')
|
||||||
|
return h.nodeIdentifier(modifiedIdentifier)
|
||||||
|
}
|
||||||
|
|
||||||
identifier 'identifier'
|
identifier 'identifier'
|
||||||
= ([_a-z]+[_a-z0-9]i*) {return h.nodeIdentifier(text(), location())}
|
= ([_a-z]+[_a-z0-9]i*) {return h.nodeIdentifier(text(), location())}
|
||||||
|
|
|
@ -91,7 +91,7 @@ let rec pgToString = (peggyNode: peggyNode): string => {
|
||||||
args->Js.Array2.map(arg => PgNodeIdentifier(arg)->pgToString)->Js.Array2.toString
|
args->Js.Array2.map(arg => PgNodeIdentifier(arg)->pgToString)->Js.Array2.toString
|
||||||
|
|
||||||
let nodesToStringUsingSeparator = (nodes: array<node>, separator: string): string =>
|
let nodesToStringUsingSeparator = (nodes: array<node>, separator: string): string =>
|
||||||
nodes->Js.Array2.map(toString)->Extra.Array.interperse(separator)->Js.String.concatMany("")
|
nodes->Js.Array2.map(toString)->Extra.Array.intersperse(separator)->Js.String.concatMany("")
|
||||||
|
|
||||||
switch peggyNode {
|
switch peggyNode {
|
||||||
| PgNodeBlock(node) => "{" ++ node["statements"]->nodesToStringUsingSeparator("; ") ++ "}"
|
| PgNodeBlock(node) => "{" ++ node["statements"]->nodesToStringUsingSeparator("; ") ++ "}"
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
module ErrorValue = Reducer_ErrorValue
|
||||||
|
module ExpressionT = Reducer_Expression_T
|
||||||
|
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||||
|
module Bindings = Reducer_Bindings
|
||||||
|
module T = Reducer_Type_T
|
||||||
|
|
||||||
|
let ievFromTypeExpression = (
|
||||||
|
typeExpressionSourceCode: string,
|
||||||
|
reducerFn: ExpressionT.reducerFn,
|
||||||
|
): result<InternalExpressionValue.t, ErrorValue.t> => {
|
||||||
|
let sIndex = "compiled"
|
||||||
|
let sourceCode = `type ${sIndex}=${typeExpressionSourceCode}`
|
||||||
|
Reducer_Expression.parse(sourceCode)->Belt.Result.flatMap(expr => {
|
||||||
|
let rContext = reducerFn(
|
||||||
|
expr,
|
||||||
|
Bindings.emptyBindings,
|
||||||
|
InternalExpressionValue.defaultEnvironment,
|
||||||
|
)
|
||||||
|
Belt.Result.map(rContext, context =>
|
||||||
|
switch context {
|
||||||
|
| IEvBindings(nameSpace) =>
|
||||||
|
switch Bindings.getType(nameSpace, sIndex) {
|
||||||
|
| Some(value) => value
|
||||||
|
| None => raise(Reducer_Exception.ImpossibleException("Reducer_Type_Compile-none"))
|
||||||
|
}
|
||||||
|
| _ => raise(Reducer_Exception.ImpossibleException("Reducer_Type_Compile-raise"))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let fromTypeExpression = (
|
||||||
|
typeExpressionSourceCode: string,
|
||||||
|
reducerFn: ExpressionT.reducerFn,
|
||||||
|
): result<T.t, ErrorValue.t> => {
|
||||||
|
ievFromTypeExpression(
|
||||||
|
(typeExpressionSourceCode: string),
|
||||||
|
(reducerFn: ExpressionT.reducerFn),
|
||||||
|
)->Belt.Result.map(T.fromIEvValue)
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||||
|
module T = Reducer_Type_T
|
||||||
|
|
||||||
|
let isMin = (modifierArg: InternalExpressionValue.t, aValue: InternalExpressionValue.t): bool => {
|
||||||
|
let pair = (modifierArg, aValue)
|
||||||
|
switch pair {
|
||||||
|
| (IEvNumber(a), IEvNumber(b)) => a <= b
|
||||||
|
| _ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let isMax = (modifierArg: InternalExpressionValue.t, aValue: InternalExpressionValue.t): bool => {
|
||||||
|
let pair = (modifierArg, aValue)
|
||||||
|
switch pair {
|
||||||
|
| (IEvNumber(a), IEvNumber(b)) => a >= b
|
||||||
|
| _ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let isMemberOf = (
|
||||||
|
modifierArg: InternalExpressionValue.t,
|
||||||
|
aValue: InternalExpressionValue.t,
|
||||||
|
): bool => {
|
||||||
|
let pair = (modifierArg, aValue)
|
||||||
|
switch pair {
|
||||||
|
| (ievA, IEvArray(b)) => Js.Array2.includes(b, ievA)
|
||||||
|
| _ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let checkModifier = (
|
||||||
|
key: string,
|
||||||
|
modifierArg: InternalExpressionValue.t,
|
||||||
|
aValue: InternalExpressionValue.t,
|
||||||
|
): bool =>
|
||||||
|
switch key {
|
||||||
|
| "min" => isMin(modifierArg, aValue)
|
||||||
|
| "max" => isMax(modifierArg, aValue)
|
||||||
|
| "isMemberOf" => isMemberOf(modifierArg, aValue)
|
||||||
|
| _ => false
|
||||||
|
}
|
||||||
|
|
||||||
|
let checkModifiers = (
|
||||||
|
contracts: Belt.Map.String.t<InternalExpressionValue.t>,
|
||||||
|
aValue: InternalExpressionValue.t,
|
||||||
|
): bool => {
|
||||||
|
contracts->Belt.Map.String.reduce(true, (acc, key, modifierArg) =>
|
||||||
|
switch acc {
|
||||||
|
| true => checkModifier(key, modifierArg, aValue)
|
||||||
|
| _ => acc
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
|
@ -3,13 +3,42 @@ open InternalExpressionValue
|
||||||
|
|
||||||
type rec iType =
|
type rec iType =
|
||||||
| ItTypeIdentifier(string)
|
| ItTypeIdentifier(string)
|
||||||
| ItModifiedType({modifiedType: iType})
|
| ItModifiedType({modifiedType: iType, contracts: Belt.Map.String.t<InternalExpressionValue.t>})
|
||||||
| ItTypeOr({typeOr: array<iType>})
|
| ItTypeOr({typeOr: array<iType>})
|
||||||
| ItTypeFunction({inputs: array<iType>, output: iType})
|
| ItTypeFunction({inputs: array<iType>, output: iType})
|
||||||
| ItTypeArray({element: iType})
|
| ItTypeArray({element: iType})
|
||||||
| ItTypeTuple({elements: array<iType>})
|
| ItTypeTuple({elements: array<iType>})
|
||||||
| ItTypeRecord({properties: Belt.Map.String.t<iType>})
|
| ItTypeRecord({properties: Belt.Map.String.t<iType>})
|
||||||
|
|
||||||
|
type t = iType
|
||||||
|
type typeErrorValue = TypeMismatch(t, InternalExpressionValue.t)
|
||||||
|
|
||||||
|
let rec toString = (t: t): string => {
|
||||||
|
switch t {
|
||||||
|
| ItTypeIdentifier(s) => s
|
||||||
|
| ItModifiedType({modifiedType, contracts}) =>
|
||||||
|
`${toString(modifiedType)}${contracts->Belt.Map.String.reduce("", (acc, k, v) =>
|
||||||
|
Js.String2.concatMany(acc, ["<-", k, "(", InternalExpressionValue.toString(v), ")"])
|
||||||
|
)}`
|
||||||
|
| ItTypeOr({typeOr}) => `(${Js.Array2.map(typeOr, toString)->Js.Array2.joinWith(" | ")})`
|
||||||
|
| ItTypeFunction({inputs, output}) =>
|
||||||
|
`(${inputs->Js.Array2.map(toString)->Js.Array2.joinWith(" => ")} => ${toString(output)})`
|
||||||
|
| ItTypeArray({element}) => `[${toString(element)}]`
|
||||||
|
| ItTypeTuple({elements}) => `[${Js.Array2.map(elements, toString)->Js.Array2.joinWith(", ")}]`
|
||||||
|
| ItTypeRecord({properties}) =>
|
||||||
|
`{${properties
|
||||||
|
->Belt.Map.String.toArray
|
||||||
|
->Js.Array2.map(((k, v)) => Js.String2.concatMany(k, [": ", toString(v)]))
|
||||||
|
->Js.Array2.joinWith(", ")}}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let toStringResult = (rt: result<t, ErrorValue.t>) =>
|
||||||
|
switch rt {
|
||||||
|
| Ok(t) => toString(t)
|
||||||
|
| Error(e) => ErrorValue.errorToString(e)
|
||||||
|
}
|
||||||
|
|
||||||
let rec fromTypeMap = typeMap => {
|
let rec fromTypeMap = typeMap => {
|
||||||
let default = IEvString("")
|
let default = IEvString("")
|
||||||
let evTypeTag: InternalExpressionValue.t = Belt.Map.String.getWithDefault(
|
let evTypeTag: InternalExpressionValue.t = Belt.Map.String.getWithDefault(
|
||||||
|
@ -52,31 +81,39 @@ let rec fromTypeMap = typeMap => {
|
||||||
"properties",
|
"properties",
|
||||||
default,
|
default,
|
||||||
)
|
)
|
||||||
//TODO: map type modifiers
|
|
||||||
switch evTypeTag {
|
let contracts =
|
||||||
| IEvString("typeIdentifier") => ItModifiedType({modifiedType: fromIEvValue(evTypeIdentifier)})
|
typeMap->Belt.Map.String.keep((k, _v) => ["min", "max", "memberOf"]->Js.Array2.includes(k))
|
||||||
|
|
||||||
|
let makeIt = switch evTypeTag {
|
||||||
|
| IEvString("typeIdentifier") => fromIEvValue(evTypeIdentifier)
|
||||||
| IEvString("typeOr") => ItTypeOr({typeOr: fromIEvArray(evTypeOr)})
|
| IEvString("typeOr") => ItTypeOr({typeOr: fromIEvArray(evTypeOr)})
|
||||||
| IEvString("typeFunction") =>
|
| IEvString("typeFunction") =>
|
||||||
ItTypeFunction({inputs: fromIEvArray(evInputs), output: fromIEvValue(evOutput)})
|
ItTypeFunction({inputs: fromIEvArray(evInputs), output: fromIEvValue(evOutput)})
|
||||||
| IEvString("typeArray") => ItTypeArray({element: fromIEvValue(evElement)})
|
| IEvString("typeArray") => ItTypeArray({element: fromIEvValue(evElement)})
|
||||||
| IEvString("typeTuple") => ItTypeTuple({elements: fromIEvArray(evElements)})
|
| IEvString("typeTuple") => ItTypeTuple({elements: fromIEvArray(evElements)})
|
||||||
| IEvString("typeRecord") => ItTypeRecord({properties: fromIEvRecord(evProperties)})
|
| IEvString("typeRecord") => ItTypeRecord({properties: fromIEvRecord(evProperties)})
|
||||||
| _ => raise(Reducer_Exception.ImpossibleException)
|
| _ => raise(Reducer_Exception.ImpossibleException("Reducer_Type_T-evTypeTag"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Belt.Map.String.isEmpty(contracts)
|
||||||
|
? makeIt
|
||||||
|
: ItModifiedType({modifiedType: makeIt, contracts: contracts})
|
||||||
}
|
}
|
||||||
and fromIEvValue = (ievValue: InternalExpressionValue.t) =>
|
|
||||||
|
and fromIEvValue = (ievValue: InternalExpressionValue.t): iType =>
|
||||||
switch ievValue {
|
switch ievValue {
|
||||||
| IEvTypeIdentifier(typeIdentifier) => ItTypeIdentifier({typeIdentifier})
|
| IEvTypeIdentifier(typeIdentifier) => ItTypeIdentifier({typeIdentifier})
|
||||||
| IEvType(typeMap) => fromTypeMap(typeMap)
|
| IEvType(typeMap) => fromTypeMap(typeMap)
|
||||||
| _ => raise(Reducer_Exception.ImpossibleException)
|
| _ => raise(Reducer_Exception.ImpossibleException("Reducer_Type_T-ievValue"))
|
||||||
}
|
}
|
||||||
and fromIEvArray = (ievArray: InternalExpressionValue.t) =>
|
and fromIEvArray = (ievArray: InternalExpressionValue.t) =>
|
||||||
switch ievArray {
|
switch ievArray {
|
||||||
| IEvArray(array) => array->Belt.Array.map(fromIEvValue)
|
| IEvArray(array) => array->Belt.Array.map(fromIEvValue)
|
||||||
| _ => raise(Reducer_Exception.ImpossibleException)
|
| _ => raise(Reducer_Exception.ImpossibleException("Reducer_Type_T-ievArray"))
|
||||||
}
|
}
|
||||||
and fromIEvRecord = (ievRecord: InternalExpressionValue.t) =>
|
and fromIEvRecord = (ievRecord: InternalExpressionValue.t) =>
|
||||||
switch ievRecord {
|
switch ievRecord {
|
||||||
| IEvRecord(record) => record->Belt.Map.String.map(fromIEvValue)
|
| IEvRecord(record) => record->Belt.Map.String.map(fromIEvValue)
|
||||||
| _ => raise(Reducer_Exception.ImpossibleException)
|
| _ => raise(Reducer_Exception.ImpossibleException("Reducer_Type_T-ievRecord"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,7 @@ let typeFunction = anArray => {
|
||||||
|
|
||||||
let typeArray = element => {
|
let typeArray = element => {
|
||||||
let newRecord = Belt.Map.String.fromArray([
|
let newRecord = Belt.Map.String.fromArray([
|
||||||
("typeTag", IEvString("typeTuple")),
|
("typeTag", IEvString("typeArray")),
|
||||||
("element", element),
|
("element", element),
|
||||||
])
|
])
|
||||||
newRecord->IEvType->Ok
|
newRecord->IEvType->Ok
|
||||||
|
@ -64,22 +64,14 @@ let typeArray = element => {
|
||||||
|
|
||||||
let typeTuple = anArray => {
|
let typeTuple = anArray => {
|
||||||
let newRecord = Belt.Map.String.fromArray([
|
let newRecord = Belt.Map.String.fromArray([
|
||||||
("typeTag", IEvString("typeArray")),
|
("typeTag", IEvString("typeTuple")),
|
||||||
("elements", IEvArray(anArray)),
|
("elements", IEvArray(anArray)),
|
||||||
])
|
])
|
||||||
newRecord->IEvType->Ok
|
newRecord->IEvType->Ok
|
||||||
}
|
}
|
||||||
|
|
||||||
let typeRecord = arrayOfPairs => {
|
let typeRecord = propertyMap => {
|
||||||
let newProperties =
|
let newProperties = propertyMap->IEvRecord
|
||||||
Belt.Array.map(arrayOfPairs, pairValue =>
|
|
||||||
switch pairValue {
|
|
||||||
| IEvArray([IEvString(key), valueValue]) => (key, valueValue)
|
|
||||||
| _ => ("wrong key type", pairValue->toStringWithType->IEvString)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
->Belt.Map.String.fromArray
|
|
||||||
->IEvRecord
|
|
||||||
let newRecord = Belt.Map.String.fromArray([
|
let newRecord = Belt.Map.String.fromArray([
|
||||||
("typeTag", IEvString("typeRecord")),
|
("typeTag", IEvString("typeRecord")),
|
||||||
("properties", newProperties),
|
("properties", newProperties),
|
||||||
|
|
|
@ -1,81 +1,168 @@
|
||||||
module ExpressionT = Reducer_Expression_T
|
module ExpressionT = Reducer_Expression_T
|
||||||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||||
module T = Reducer_Type_T
|
module T = Reducer_Type_T
|
||||||
module TypeBuilder = Reducer_Type_TypeBuilder
|
module TypeContracts = Reducer_Type_Contracts
|
||||||
open InternalExpressionValue
|
open InternalExpressionValue
|
||||||
|
|
||||||
type typeErrorValue =
|
let rec isITypeOf = (anIType: T.iType, aValue): result<bool, T.typeErrorValue> => {
|
||||||
| TypeError(T.iType, InternalExpressionValue.t)
|
|
||||||
| TypeErrorWithPosition(T.iType, InternalExpressionValue.t, int)
|
|
||||||
| TypeErrorWithProperty(T.iType, InternalExpressionValue.t, string)
|
|
||||||
|
|
||||||
let rec isOfResolvedIType = (anIType: T.iType, aValue): result<bool, typeErrorValue> => {
|
|
||||||
let caseTypeIdentifier = (anUpperTypeName, aValue) => {
|
let caseTypeIdentifier = (anUpperTypeName, aValue) => {
|
||||||
let aTypeName = anUpperTypeName->Js.String2.toLowerCase
|
let aTypeName = anUpperTypeName->Js.String2.toLowerCase
|
||||||
let valueTypeName = aValue->valueToValueType->valueTypeToString->Js.String2.toLowerCase
|
let valueTypeName = aValue->valueToValueType->valueTypeToString->Js.String2.toLowerCase
|
||||||
switch aTypeName === valueTypeName {
|
switch aTypeName == valueTypeName {
|
||||||
| true => Ok(true)
|
| true => Ok(true)
|
||||||
| false => TypeError(anIType, aValue)->Error
|
| false => T.TypeMismatch(anIType, aValue)->Error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let _caseRecord = (anIType, evValue, propertyMap, map) => {
|
let caseRecord = (anIType, propertyMap: Belt.Map.String.t<T.iType>, evValue) =>
|
||||||
|
switch evValue {
|
||||||
|
| IEvRecord(aRecord) =>
|
||||||
|
if (
|
||||||
|
Js.Array2.length(propertyMap->Belt.Map.String.keysToArray) ==
|
||||||
|
Js.Array2.length(aRecord->Belt.Map.String.keysToArray)
|
||||||
|
) {
|
||||||
Belt.Map.String.reduce(propertyMap, Ok(true), (acc, property, propertyType) => {
|
Belt.Map.String.reduce(propertyMap, Ok(true), (acc, property, propertyType) => {
|
||||||
Belt.Result.flatMap(acc, _ =>
|
Belt.Result.flatMap(acc, _ =>
|
||||||
switch Belt.Map.String.get(map, property) {
|
switch Belt.Map.String.get(aRecord, property) {
|
||||||
| Some(propertyValue) => isOfResolvedIType(propertyType, propertyValue)
|
| Some(propertyValue) => isITypeOf(propertyType, propertyValue)
|
||||||
| None => TypeErrorWithProperty(anIType, evValue, property)->Error
|
| None => T.TypeMismatch(anIType, evValue)->Error
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
T.TypeMismatch(anIType, evValue)->Error
|
||||||
}
|
}
|
||||||
let _caseArray = (anIType, evValue, elementType, anArray) => {
|
|
||||||
Belt.Array.reduceWithIndex(anArray, Ok(true), (acc, element, index) => {
|
| _ => T.TypeMismatch(anIType, evValue)->Error
|
||||||
switch isOfResolvedIType(elementType, element) {
|
}
|
||||||
|
|
||||||
|
let caseArray = (anIType, elementType, evValue) =>
|
||||||
|
switch evValue {
|
||||||
|
| IEvArray(anArray) =>
|
||||||
|
Belt.Array.reduce(anArray, Ok(true), (acc, element) =>
|
||||||
|
Belt.Result.flatMap(acc, _ =>
|
||||||
|
switch isITypeOf(elementType, element) {
|
||||||
|
| Ok(_) => Ok(true)
|
||||||
|
| Error(error) => error->Error
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| _ => T.TypeMismatch(anIType, evValue)->Error
|
||||||
|
}
|
||||||
|
|
||||||
|
let caseTuple = (anIType, elementTypes, evValue) =>
|
||||||
|
switch evValue {
|
||||||
|
| IEvArray(anArray) =>
|
||||||
|
if Js.Array2.length(elementTypes) == Js.Array2.length(anArray) {
|
||||||
|
let zipped = Belt.Array.zip(elementTypes, anArray)
|
||||||
|
Belt.Array.reduce(zipped, Ok(true), (acc, (elementType, element)) =>
|
||||||
|
switch acc {
|
||||||
|
| Ok(_) =>
|
||||||
|
switch isITypeOf(elementType, element) {
|
||||||
| Ok(_) => acc
|
| Ok(_) => acc
|
||||||
| Error(_) => TypeErrorWithPosition(anIType, evValue, index)->Error
|
| Error(error) => Error(error)
|
||||||
|
}
|
||||||
|
| _ => acc
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
T.TypeMismatch(anIType, evValue)->Error
|
||||||
|
}
|
||||||
|
| _ => T.TypeMismatch(anIType, evValue)->Error
|
||||||
|
}
|
||||||
|
|
||||||
|
let caseOr = (anIType, anITypeArray, evValue) =>
|
||||||
|
switch Belt.Array.reduce(anITypeArray, Ok(false), (acc, anIType) =>
|
||||||
|
Belt.Result.flatMap(acc, _ =>
|
||||||
|
switch acc {
|
||||||
|
| Ok(false) =>
|
||||||
|
switch isITypeOf(anIType, evValue) {
|
||||||
|
| Ok(_) => Ok(true)
|
||||||
|
| Error(_) => acc
|
||||||
|
}
|
||||||
|
| _ => acc
|
||||||
|
}
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
| Ok(true) => Ok(true)
|
||||||
|
| Ok(false) => T.TypeMismatch(anIType, evValue)->Error
|
||||||
|
| Error(error) => Error(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
let caseModifiedType = (
|
||||||
|
anIType: T.iType,
|
||||||
|
modifiedType: T.iType,
|
||||||
|
contracts: Belt.Map.String.t<InternalExpressionValue.t>,
|
||||||
|
aValue: InternalExpressionValue.t,
|
||||||
|
) => {
|
||||||
|
isITypeOf(modifiedType, aValue)->Belt.Result.flatMap(_result => {
|
||||||
|
if TypeContracts.checkModifiers(contracts, aValue) {
|
||||||
|
Ok(true)
|
||||||
|
} else {
|
||||||
|
T.TypeMismatch(anIType, aValue)->Error
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
switch anIType {
|
switch anIType {
|
||||||
| ItTypeIdentifier(name) => caseTypeIdentifier(name, aValue)
|
| ItTypeIdentifier(name) => caseTypeIdentifier(name, aValue)
|
||||||
// TODO: Work in progress. Code is commented to make an a release of other features
|
| ItModifiedType({modifiedType, contracts}) =>
|
||||||
// | ItModifiedType({modifiedType: anIType}) => raise(Reducer_Exception.ImpossibleException)
|
caseModifiedType(anIType, modifiedType, contracts, aValue) //{modifiedType: iType, contracts: Belt.Map.String.t<InternalExpressionValue.t>}
|
||||||
// | ItTypeOr({typeOr: anITypeArray}) => raise(Reducer_Exception.ImpossibleException)
|
| ItTypeOr({typeOr}) => caseOr(anIType, typeOr, aValue)
|
||||||
// | ItTypeFunction({inputs: anITypeArray, output: anIType}) =>
|
| ItTypeFunction(_) =>
|
||||||
// raise(Reducer_Exception.ImpossibleException)
|
raise(
|
||||||
// | ItTypeArray({element: anIType}) => raise(Reducer_Exception.ImpossibleException)
|
Reducer_Exception.ImpossibleException(
|
||||||
// | ItTypeTuple({elements: anITypeArray}) => raise(Reducer_Exception.ImpossibleException)
|
"Reducer_TypeChecker-functions are without a type at the moment",
|
||||||
// | ItTypeRecord({properties: anITypeMap}) => raise(Reducer_Exception.ImpossibleException)
|
),
|
||||||
| _ => raise(Reducer_Exception.ImpossibleException)
|
)
|
||||||
|
| ItTypeArray({element}) => caseArray(anIType, element, aValue)
|
||||||
|
| ItTypeTuple({elements}) => caseTuple(anIType, elements, aValue)
|
||||||
|
| ItTypeRecord({properties}) => caseRecord(anIType, properties, aValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let isOfResolvedType = (aType: InternalExpressionValue.t, aValue): result<bool, typeErrorValue> =>
|
let isTypeOf = (
|
||||||
aType->T.fromIEvValue->isOfResolvedIType(aValue)
|
typeExpressionSourceCode: string,
|
||||||
|
aValue: InternalExpressionValue.t,
|
||||||
|
reducerFn: ExpressionT.reducerFn,
|
||||||
|
): result<InternalExpressionValue.t, ErrorValue.t> => {
|
||||||
|
switch typeExpressionSourceCode->Reducer_Type_Compile.fromTypeExpression(reducerFn) {
|
||||||
|
| Ok(anIType) =>
|
||||||
|
switch isITypeOf(anIType, aValue) {
|
||||||
|
| Ok(_) => Ok(aValue)
|
||||||
|
| Error(T.TypeMismatch(anIType, evValue)) =>
|
||||||
|
Error(
|
||||||
|
ErrorValue.REExpectedType(anIType->T.toString, evValue->InternalExpressionValue.toString),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
| Error(error) => Error(error) // Directly propagating - err => err - causes type mismatch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Work in progress. Code is commented to make an a release of other features
|
let checkITypeArguments = (anIType: T.iType, args: array<InternalExpressionValue.t>): result<
|
||||||
// let checkArguments = (
|
bool,
|
||||||
// evFunctionType: InternalExpressionValue.t,
|
T.typeErrorValue,
|
||||||
// args: array<InternalExpressionValue.t>,
|
> => {
|
||||||
// ) => {
|
switch anIType {
|
||||||
// let functionType = switch evFunctionType {
|
| T.ItTypeFunction({inputs}) => isITypeOf(T.ItTypeTuple({elements: inputs}), args->IEvArray)
|
||||||
// | IEvRecord(functionType) => functionType
|
| _ => T.TypeMismatch(anIType, args->IEvArray)->Error
|
||||||
// | _ => raise(Reducer_Exception.ImpossibleException)
|
}
|
||||||
// }
|
}
|
||||||
// let evInputs = functionType->Belt.Map.String.getWithDefault("inputs", []->IEvArray)
|
|
||||||
// let inputs = switch evInputs {
|
|
||||||
// | IEvArray(inputs) => inputs
|
|
||||||
// | _ => raise(Reducer_Exception.ImpossibleException)
|
|
||||||
// }
|
|
||||||
// let rTupleType = TypeBuilder.typeTuple(inputs)
|
|
||||||
// Belt.Result.flatMap(rTupleType, tuppleType => isOfResolvedType(tuppleType, args->IEvArray))
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let compileTypeExpression = (typeExpression: string, bindings: ExpressionT.bindings, reducerFn: ExpressionT.reducerFn) => {
|
let checkArguments = (
|
||||||
// statement = `type compiled=${typeExpression}`
|
typeExpressionSourceCode: string,
|
||||||
|
args: array<InternalExpressionValue.t>,
|
||||||
// }
|
reducerFn: ExpressionT.reducerFn,
|
||||||
|
): result<InternalExpressionValue.t, ErrorValue.t> => {
|
||||||
//TODO: asGuard
|
switch typeExpressionSourceCode->Reducer_Type_Compile.fromTypeExpression(reducerFn) {
|
||||||
|
| Ok(anIType) =>
|
||||||
|
switch checkITypeArguments(anIType, args) {
|
||||||
|
| Ok(_) => Ok(args->IEvArray)
|
||||||
|
| Error(T.TypeMismatch(anIType, evValue)) =>
|
||||||
|
Error(
|
||||||
|
ErrorValue.REExpectedType(anIType->T.toString, evValue->InternalExpressionValue.toString),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
| Error(error) => Error(error) // Directly propagating - err => err - causes type mismatch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
module IEV = ReducerInterface_InternalExpressionValue
|
module IEV = ReducerInterface_InternalExpressionValue
|
||||||
type internalExpressionValue = IEV.t
|
type internalExpressionValue = IEV.t
|
||||||
|
|
||||||
let dispatch = (call: IEV.functionCall, _: DistributionOperation.env): option<
|
let dispatch = (call: IEV.functionCall, _: GenericDist.env): option<
|
||||||
result<internalExpressionValue, QuriSquiggleLang.Reducer_ErrorValue.errorValue>,
|
result<internalExpressionValue, QuriSquiggleLang.Reducer_ErrorValue.errorValue>,
|
||||||
> => {
|
> => {
|
||||||
switch call {
|
switch call {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
module IEV = ReducerInterface_InternalExpressionValue
|
module IEV = ReducerInterface_InternalExpressionValue
|
||||||
type internalExpressionValue = IEV.t
|
type internalExpressionValue = IEV.t
|
||||||
|
|
||||||
let dispatch = (call: IEV.functionCall, _: DistributionOperation.env): option<
|
let dispatch = (call: IEV.functionCall, _: GenericDist.env): option<
|
||||||
result<internalExpressionValue, QuriSquiggleLang.Reducer_ErrorValue.errorValue>,
|
result<internalExpressionValue, QuriSquiggleLang.Reducer_ErrorValue.errorValue>,
|
||||||
> => {
|
> => {
|
||||||
switch call {
|
switch call {
|
||||||
|
|
|
@ -86,7 +86,7 @@ let toStringResult = x =>
|
||||||
}
|
}
|
||||||
|
|
||||||
@genType
|
@genType
|
||||||
type environment = DistributionOperation.env
|
type environment = GenericDist.env
|
||||||
|
|
||||||
@genType
|
@genType
|
||||||
let defaultEnvironment: environment = DistributionOperation.defaultEnv
|
let defaultEnvironment: environment = DistributionOperation.defaultEnv
|
||||||
|
|
|
@ -1,29 +1,10 @@
|
||||||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||||
|
|
||||||
type internalExpressionValue = InternalExpressionValue.t
|
type internalExpressionValue = InternalExpressionValue.t
|
||||||
|
|
||||||
// module Sample = {
|
|
||||||
// // In real life real libraries should be somewhere else
|
|
||||||
// /*
|
|
||||||
// For an example of mapping polymorphic custom functions. To be deleted after real integration
|
|
||||||
// */
|
|
||||||
// let customAdd = (a: float, b: float): float => {a +. b}
|
|
||||||
// }
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Map external calls of Reducer
|
Map external calls of Reducer
|
||||||
*/
|
*/
|
||||||
|
let dispatch = (call: InternalExpressionValue.functionCall, environment, reducer, chain): result<
|
||||||
// I expect that it's important to build this first, so it doesn't get recalculated for each tryRegistry() call.
|
|
||||||
let registry = FunctionRegistry_Library.registry
|
|
||||||
|
|
||||||
let tryRegistry = ((fnName, args): InternalExpressionValue.functionCall, env) => {
|
|
||||||
FunctionRegistry_Core.Registry.matchAndRun(~registry, ~fnName, ~args, ~env)->E.O2.fmap(
|
|
||||||
E.R2.errMap(_, s => Reducer_ErrorValue.RETodo(s)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
let dispatch = (call: InternalExpressionValue.functionCall, environment, chain): result<
|
|
||||||
internalExpressionValue,
|
internalExpressionValue,
|
||||||
'e,
|
'e,
|
||||||
> => {
|
> => {
|
||||||
|
@ -32,9 +13,10 @@ let dispatch = (call: InternalExpressionValue.functionCall, environment, chain):
|
||||||
() => ReducerInterface_Date.dispatch(call, environment),
|
() => ReducerInterface_Date.dispatch(call, environment),
|
||||||
() => ReducerInterface_Duration.dispatch(call, environment),
|
() => ReducerInterface_Duration.dispatch(call, environment),
|
||||||
() => ReducerInterface_Number.dispatch(call, environment),
|
() => ReducerInterface_Number.dispatch(call, environment),
|
||||||
() => tryRegistry(call, environment),
|
() => FunctionRegistry_Library.dispatch(call, environment),
|
||||||
])->E.O2.default(chain(call, environment))
|
])->E.O2.default(chain(call, environment, reducer))
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
If your dispatch is too big you can divide it into smaller dispatches and pass the call so that it gets called finally.
|
If your dispatch is too big you can divide it into smaller dispatches and pass the call so that it gets called finally.
|
||||||
|
|
||||||
|
|
|
@ -32,50 +32,38 @@ module Helpers = {
|
||||||
let toFloatFn = (
|
let toFloatFn = (
|
||||||
fnCall: DistributionTypes.DistributionOperation.toFloat,
|
fnCall: DistributionTypes.DistributionOperation.toFloat,
|
||||||
dist: DistributionTypes.genericDist,
|
dist: DistributionTypes.genericDist,
|
||||||
~env: DistributionOperation.env,
|
~env: GenericDist.env,
|
||||||
) => {
|
) => {
|
||||||
FromDist(DistributionTypes.DistributionOperation.ToFloat(fnCall), dist)
|
FromDist(#ToFloat(fnCall), dist)->DistributionOperation.run(~env)->Some
|
||||||
->DistributionOperation.run(~env)
|
|
||||||
->Some
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let toStringFn = (
|
let toStringFn = (
|
||||||
fnCall: DistributionTypes.DistributionOperation.toString,
|
fnCall: DistributionTypes.DistributionOperation.toString,
|
||||||
dist: DistributionTypes.genericDist,
|
dist: DistributionTypes.genericDist,
|
||||||
~env: DistributionOperation.env,
|
~env: GenericDist.env,
|
||||||
) => {
|
) => {
|
||||||
FromDist(DistributionTypes.DistributionOperation.ToString(fnCall), dist)
|
FromDist(#ToString(fnCall), dist)->DistributionOperation.run(~env)->Some
|
||||||
->DistributionOperation.run(~env)
|
|
||||||
->Some
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let toBoolFn = (
|
let toBoolFn = (
|
||||||
fnCall: DistributionTypes.DistributionOperation.toBool,
|
fnCall: DistributionTypes.DistributionOperation.toBool,
|
||||||
dist: DistributionTypes.genericDist,
|
dist: DistributionTypes.genericDist,
|
||||||
~env: DistributionOperation.env,
|
~env: GenericDist.env,
|
||||||
) => {
|
) => {
|
||||||
FromDist(DistributionTypes.DistributionOperation.ToBool(fnCall), dist)
|
FromDist(#ToBool(fnCall), dist)->DistributionOperation.run(~env)->Some
|
||||||
->DistributionOperation.run(~env)
|
|
||||||
->Some
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let toDistFn = (
|
let toDistFn = (
|
||||||
fnCall: DistributionTypes.DistributionOperation.toDist,
|
fnCall: DistributionTypes.DistributionOperation.toDist,
|
||||||
dist,
|
dist,
|
||||||
~env: DistributionOperation.env,
|
~env: GenericDist.env,
|
||||||
) => {
|
) => {
|
||||||
FromDist(DistributionTypes.DistributionOperation.ToDist(fnCall), dist)
|
FromDist(#ToDist(fnCall), dist)->DistributionOperation.run(~env)->Some
|
||||||
->DistributionOperation.run(~env)
|
|
||||||
->Some
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let twoDiststoDistFn = (direction, arithmetic, dist1, dist2, ~env: DistributionOperation.env) => {
|
let twoDiststoDistFn = (direction, arithmetic, dist1, dist2, ~env: GenericDist.env) => {
|
||||||
FromDist(
|
FromDist(
|
||||||
DistributionTypes.DistributionOperation.ToDistCombination(
|
#ToDistCombination(direction, arithmeticMap(arithmetic), #Dist(dist2)),
|
||||||
direction,
|
|
||||||
arithmeticMap(arithmetic),
|
|
||||||
#Dist(dist2),
|
|
||||||
),
|
|
||||||
dist1,
|
dist1,
|
||||||
)->DistributionOperation.run(~env)
|
)->DistributionOperation.run(~env)
|
||||||
}
|
}
|
||||||
|
@ -109,7 +97,7 @@ module Helpers = {
|
||||||
let mixtureWithGivenWeights = (
|
let mixtureWithGivenWeights = (
|
||||||
distributions: array<DistributionTypes.genericDist>,
|
distributions: array<DistributionTypes.genericDist>,
|
||||||
weights: array<float>,
|
weights: array<float>,
|
||||||
~env: DistributionOperation.env,
|
~env: GenericDist.env,
|
||||||
): DistributionOperation.outputType =>
|
): DistributionOperation.outputType =>
|
||||||
E.A.length(distributions) == E.A.length(weights)
|
E.A.length(distributions) == E.A.length(weights)
|
||||||
? Mixture(Belt.Array.zip(distributions, weights))->DistributionOperation.run(~env)
|
? Mixture(Belt.Array.zip(distributions, weights))->DistributionOperation.run(~env)
|
||||||
|
@ -119,7 +107,7 @@ module Helpers = {
|
||||||
|
|
||||||
let mixtureWithDefaultWeights = (
|
let mixtureWithDefaultWeights = (
|
||||||
distributions: array<DistributionTypes.genericDist>,
|
distributions: array<DistributionTypes.genericDist>,
|
||||||
~env: DistributionOperation.env,
|
~env: GenericDist.env,
|
||||||
): DistributionOperation.outputType => {
|
): DistributionOperation.outputType => {
|
||||||
let length = E.A.length(distributions)
|
let length = E.A.length(distributions)
|
||||||
let weights = Belt.Array.make(length, 1.0 /. Belt.Int.toFloat(length))
|
let weights = Belt.Array.make(length, 1.0 /. Belt.Int.toFloat(length))
|
||||||
|
@ -128,7 +116,7 @@ module Helpers = {
|
||||||
|
|
||||||
let mixture = (
|
let mixture = (
|
||||||
args: array<internalExpressionValue>,
|
args: array<internalExpressionValue>,
|
||||||
~env: DistributionOperation.env,
|
~env: GenericDist.env,
|
||||||
): DistributionOperation.outputType => {
|
): DistributionOperation.outputType => {
|
||||||
let error = (err: string): DistributionOperation.outputType =>
|
let error = (err: string): DistributionOperation.outputType =>
|
||||||
err->DistributionTypes.ArgumentError->GenDistError
|
err->DistributionTypes.ArgumentError->GenDistError
|
||||||
|
@ -167,20 +155,6 @@ module Helpers = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let klDivergenceWithPrior = (
|
|
||||||
prediction: DistributionTypes.genericDist,
|
|
||||||
answer: DistributionTypes.genericDist,
|
|
||||||
prior: DistributionTypes.genericDist,
|
|
||||||
env: DistributionOperation.env,
|
|
||||||
) => {
|
|
||||||
let term1 = DistributionOperation.Constructors.klDivergence(~env, prediction, answer)
|
|
||||||
let term2 = DistributionOperation.Constructors.klDivergence(~env, prior, answer)
|
|
||||||
switch E.R.merge(term1, term2)->E.R2.fmap(((a, b)) => a -. b) {
|
|
||||||
| Ok(x) => x->DistributionOperation.Float->Some
|
|
||||||
| Error(_) => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module SymbolicConstructors = {
|
module SymbolicConstructors = {
|
||||||
|
@ -199,7 +173,7 @@ module SymbolicConstructors = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let dispatchToGenericOutput = (call: IEV.functionCall, env: DistributionOperation.env): option<
|
let dispatchToGenericOutput = (call: IEV.functionCall, env: GenericDist.env): option<
|
||||||
DistributionOperation.outputType,
|
DistributionOperation.outputType,
|
||||||
> => {
|
> => {
|
||||||
let (fnName, args) = call
|
let (fnName, args) = call
|
||||||
|
@ -239,35 +213,6 @@ let dispatchToGenericOutput = (call: IEV.functionCall, env: DistributionOperatio
|
||||||
~env,
|
~env,
|
||||||
)->Some
|
)->Some
|
||||||
| ("normalize", [IEvDistribution(dist)]) => Helpers.toDistFn(Normalize, dist, ~env)
|
| ("normalize", [IEvDistribution(dist)]) => Helpers.toDistFn(Normalize, dist, ~env)
|
||||||
| ("klDivergence", [IEvDistribution(prediction), IEvDistribution(answer)]) =>
|
|
||||||
Some(DistributionOperation.run(FromDist(ToScore(KLDivergence(answer)), prediction), ~env))
|
|
||||||
| (
|
|
||||||
"klDivergence",
|
|
||||||
[IEvDistribution(prediction), IEvDistribution(answer), IEvDistribution(prior)],
|
|
||||||
) =>
|
|
||||||
Helpers.klDivergenceWithPrior(prediction, answer, prior, env)
|
|
||||||
| (
|
|
||||||
"logScoreWithPointAnswer",
|
|
||||||
[IEvDistribution(prediction), IEvNumber(answer), IEvDistribution(prior)],
|
|
||||||
)
|
|
||||||
| (
|
|
||||||
"logScoreWithPointAnswer",
|
|
||||||
[
|
|
||||||
IEvDistribution(prediction),
|
|
||||||
IEvDistribution(Symbolic(#Float(answer))),
|
|
||||||
IEvDistribution(prior),
|
|
||||||
],
|
|
||||||
) =>
|
|
||||||
DistributionOperation.run(
|
|
||||||
FromDist(ToScore(LogScore(answer, prior->Some)), prediction),
|
|
||||||
~env,
|
|
||||||
)->Some
|
|
||||||
| ("logScoreWithPointAnswer", [IEvDistribution(prediction), IEvNumber(answer)])
|
|
||||||
| (
|
|
||||||
"logScoreWithPointAnswer",
|
|
||||||
[IEvDistribution(prediction), IEvDistribution(Symbolic(#Float(answer)))],
|
|
||||||
) =>
|
|
||||||
DistributionOperation.run(FromDist(ToScore(LogScore(answer, None)), prediction), ~env)->Some
|
|
||||||
| ("isNormalized", [IEvDistribution(dist)]) => Helpers.toBoolFn(IsNormalized, dist, ~env)
|
| ("isNormalized", [IEvDistribution(dist)]) => Helpers.toBoolFn(IsNormalized, dist, ~env)
|
||||||
| ("toPointSet", [IEvDistribution(dist)]) => Helpers.toDistFn(ToPointSet, dist, ~env)
|
| ("toPointSet", [IEvDistribution(dist)]) => Helpers.toDistFn(ToPointSet, dist, ~env)
|
||||||
| ("scaleLog", [IEvDistribution(dist)]) =>
|
| ("scaleLog", [IEvDistribution(dist)]) =>
|
||||||
|
|
|
@ -15,7 +15,7 @@ type rec t =
|
||||||
| IEvDeclaration(lambdaDeclaration)
|
| IEvDeclaration(lambdaDeclaration)
|
||||||
| IEvDistribution(DistributionTypes.genericDist)
|
| IEvDistribution(DistributionTypes.genericDist)
|
||||||
| IEvLambda(lambdaValue)
|
| IEvLambda(lambdaValue)
|
||||||
| IEvModule(nameSpace)
|
| IEvBindings(nameSpace)
|
||||||
| IEvNumber(float)
|
| IEvNumber(float)
|
||||||
| IEvRecord(map)
|
| IEvRecord(map)
|
||||||
| IEvString(string)
|
| IEvString(string)
|
||||||
|
@ -52,7 +52,7 @@ let rec toString = aValue =>
|
||||||
| IEvDeclaration(d) => Declaration.toString(d, r => toString(IEvLambda(r)))
|
| IEvDeclaration(d) => Declaration.toString(d, r => toString(IEvLambda(r)))
|
||||||
| IEvDistribution(dist) => GenericDist.toString(dist)
|
| IEvDistribution(dist) => GenericDist.toString(dist)
|
||||||
| IEvLambda(lambdaValue) => `lambda(${Js.Array2.toString(lambdaValue.parameters)}=>internal code)`
|
| IEvLambda(lambdaValue) => `lambda(${Js.Array2.toString(lambdaValue.parameters)}=>internal code)`
|
||||||
| IEvModule(m) => `@${m->toStringNameSpace}`
|
| IEvBindings(m) => `@${m->toStringNameSpace}`
|
||||||
| IEvNumber(aNumber) => Js.String.make(aNumber)
|
| IEvNumber(aNumber) => Js.String.make(aNumber)
|
||||||
| IEvRecord(aMap) => aMap->toStringMap
|
| IEvRecord(aMap) => aMap->toStringMap
|
||||||
| IEvString(aString) => `'${aString}'`
|
| IEvString(aString) => `'${aString}'`
|
||||||
|
@ -84,7 +84,7 @@ let toStringWithType = aValue =>
|
||||||
| IEvDeclaration(_) => `Declaration::${toString(aValue)}`
|
| IEvDeclaration(_) => `Declaration::${toString(aValue)}`
|
||||||
| IEvDistribution(_) => `Distribution::${toString(aValue)}`
|
| IEvDistribution(_) => `Distribution::${toString(aValue)}`
|
||||||
| IEvLambda(_) => `Lambda::${toString(aValue)}`
|
| IEvLambda(_) => `Lambda::${toString(aValue)}`
|
||||||
| IEvModule(_) => `Module::${toString(aValue)}`
|
| IEvBindings(_) => `Bindings::${toString(aValue)}`
|
||||||
| IEvNumber(_) => `Number::${toString(aValue)}`
|
| IEvNumber(_) => `Number::${toString(aValue)}`
|
||||||
| IEvRecord(_) => `Record::${toString(aValue)}`
|
| IEvRecord(_) => `Record::${toString(aValue)}`
|
||||||
| IEvString(_) => `String::${toString(aValue)}`
|
| IEvString(_) => `String::${toString(aValue)}`
|
||||||
|
@ -150,7 +150,7 @@ let valueToValueType = value =>
|
||||||
| IEvDeclaration(_) => EvtDeclaration
|
| IEvDeclaration(_) => EvtDeclaration
|
||||||
| IEvDistribution(_) => EvtDistribution
|
| IEvDistribution(_) => EvtDistribution
|
||||||
| IEvLambda(_) => EvtLambda
|
| IEvLambda(_) => EvtLambda
|
||||||
| IEvModule(_) => EvtModule
|
| IEvBindings(_) => EvtModule
|
||||||
| IEvNumber(_) => EvtNumber
|
| IEvNumber(_) => EvtNumber
|
||||||
| IEvRecord(_) => EvtRecord
|
| IEvRecord(_) => EvtRecord
|
||||||
| IEvString(_) => EvtString
|
| IEvString(_) => EvtString
|
||||||
|
@ -160,6 +160,26 @@ let valueToValueType = value =>
|
||||||
| IEvTypeIdentifier(_) => EvtTypeIdentifier
|
| IEvTypeIdentifier(_) => EvtTypeIdentifier
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let externalValueToValueType = (value: ExternalExpressionValue.t) =>
|
||||||
|
switch value {
|
||||||
|
| EvArray(_) => EvtArray
|
||||||
|
| EvArrayString(_) => EvtArrayString
|
||||||
|
| EvBool(_) => EvtBool
|
||||||
|
| EvCall(_) => EvtCall
|
||||||
|
| EvDate(_) => EvtDate
|
||||||
|
| EvDeclaration(_) => EvtDeclaration
|
||||||
|
| EvDistribution(_) => EvtDistribution
|
||||||
|
| EvLambda(_) => EvtLambda
|
||||||
|
| EvModule(_) => EvtModule
|
||||||
|
| EvNumber(_) => EvtNumber
|
||||||
|
| EvRecord(_) => EvtRecord
|
||||||
|
| EvString(_) => EvtString
|
||||||
|
| EvSymbol(_) => EvtSymbol
|
||||||
|
| EvTimeDuration(_) => EvtTimeDuration
|
||||||
|
| EvType(_) => EvtType
|
||||||
|
| EvTypeIdentifier(_) => EvtTypeIdentifier
|
||||||
|
}
|
||||||
|
|
||||||
let functionCallToCallSignature = (functionCall: functionCall): functionCallSignature => {
|
let functionCallToCallSignature = (functionCall: functionCall): functionCallSignature => {
|
||||||
let (fn, args) = functionCall
|
let (fn, args) = functionCall
|
||||||
CallSignature(fn, args->Js.Array2.map(valueToValueType))
|
CallSignature(fn, args->Js.Array2.map(valueToValueType))
|
||||||
|
@ -211,7 +231,7 @@ let rec toExternal = (iev: t): ExternalExpressionValue.t => {
|
||||||
| IEvTimeDuration(v) => EvTimeDuration(v)
|
| IEvTimeDuration(v) => EvTimeDuration(v)
|
||||||
| IEvType(v) => v->mapToExternal->EvType
|
| IEvType(v) => v->mapToExternal->EvType
|
||||||
| IEvTypeIdentifier(v) => EvTypeIdentifier(v)
|
| IEvTypeIdentifier(v) => EvTypeIdentifier(v)
|
||||||
| IEvModule(v) => v->nameSpaceToTypeScriptBindings->EvModule
|
| IEvBindings(v) => v->nameSpaceToTypeScriptBindings->EvModule
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
and mapToExternal = v =>
|
and mapToExternal = v =>
|
||||||
|
@ -243,7 +263,7 @@ let rec toInternal = (ev: ExternalExpressionValue.t): t => {
|
||||||
}
|
}
|
||||||
| EvDistribution(v) => IEvDistribution(v)
|
| EvDistribution(v) => IEvDistribution(v)
|
||||||
| EvLambda(v) => IEvLambda(lambdaValueToInternal(v))
|
| EvLambda(v) => IEvLambda(lambdaValueToInternal(v))
|
||||||
| EvModule(v) => v->nameSpaceFromTypeScriptBindings->IEvModule
|
| EvModule(v) => v->nameSpaceFromTypeScriptBindings->IEvBindings
|
||||||
| EvNumber(v) => IEvNumber(v)
|
| EvNumber(v) => IEvNumber(v)
|
||||||
| EvRecord(v) => v->recordToInternal->IEvRecord
|
| EvRecord(v) => v->recordToInternal->IEvRecord
|
||||||
| EvString(v) => IEvString(v)
|
| EvString(v) => IEvString(v)
|
||||||
|
|
|
@ -24,7 +24,7 @@ module ScientificUnit = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let dispatch = (call: IEV.functionCall, _: DistributionOperation.env): option<
|
let dispatch = (call: IEV.functionCall, _: GenericDist.env): option<
|
||||||
result<internalExpressionValue, QuriSquiggleLang.Reducer_ErrorValue.errorValue>,
|
result<internalExpressionValue, QuriSquiggleLang.Reducer_ErrorValue.errorValue>,
|
||||||
> => {
|
> => {
|
||||||
switch call {
|
switch call {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
module Module = Reducer_Module
|
module Bindings = Reducer_Bindings
|
||||||
|
|
||||||
let internalStdLib = Module.emptyModule->SquiggleLibrary_Math.makeBindings
|
let internalStdLib = Bindings.emptyBindings->SquiggleLibrary_Math.makeBindings
|
||||||
|
|
||||||
@genType
|
@genType
|
||||||
let externalStdLib = internalStdLib->Module.toTypeScriptBindings
|
let externalStdLib = internalStdLib->Bindings.toTypeScriptBindings
|
||||||
|
|
|
@ -1,17 +1,16 @@
|
||||||
module Bindings = Reducer_Module
|
module Bindings = Reducer_Bindings
|
||||||
module Module = Reducer_Module
|
|
||||||
|
|
||||||
let availableNumbers: array<(string, float)> = [
|
let availableNumbers: array<(string, float)> = [
|
||||||
("pi", Js.Math._PI),
|
("Math.pi", Js.Math._PI),
|
||||||
("e", Js.Math._E),
|
("Math.e", Js.Math._E),
|
||||||
("ln2", Js.Math._LN2),
|
("Math.ln2", Js.Math._LN2),
|
||||||
("ln10", Js.Math._LN10),
|
("Math.ln10", Js.Math._LN10),
|
||||||
("log2e", Js.Math._LOG2E),
|
("Math.log2e", Js.Math._LOG2E),
|
||||||
("log10e", Js.Math._LOG10E),
|
("Math.log10e", Js.Math._LOG10E),
|
||||||
("sqrt2", Js.Math._SQRT2),
|
("Math.sqrt2", Js.Math._SQRT2),
|
||||||
("sqrt1_2", Js.Math._SQRT1_2),
|
("Math.sqrt1_2", Js.Math._SQRT1_2),
|
||||||
("phi", 1.618033988749895),
|
("Math.phi", 1.618033988749895),
|
||||||
("tau", 6.283185307179586),
|
("Math.tau", 6.283185307179586),
|
||||||
]
|
]
|
||||||
|
|
||||||
let mathBindings: Bindings.t =
|
let mathBindings: Bindings.t =
|
||||||
|
@ -20,4 +19,4 @@ let mathBindings: Bindings.t =
|
||||||
->Bindings.fromArray
|
->Bindings.fromArray
|
||||||
|
|
||||||
let makeBindings = (previousBindings: Bindings.t): Bindings.t =>
|
let makeBindings = (previousBindings: Bindings.t): Bindings.t =>
|
||||||
previousBindings->Bindings.defineModule("Math", mathBindings)
|
previousBindings->Bindings.merge(mathBindings)
|
||||||
|
|
|
@ -8,7 +8,7 @@ The below few seem to work fine. In the future there's definitely more work to d
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@genType
|
@genType
|
||||||
type samplingParams = DistributionOperation.env
|
type samplingParams = GenericDist.env
|
||||||
|
|
||||||
@genType
|
@genType
|
||||||
type genericDist = DistributionTypes.genericDist
|
type genericDist = DistributionTypes.genericDist
|
||||||
|
|
|
@ -547,6 +547,7 @@ module A = {
|
||||||
let init = Array.init
|
let init = Array.init
|
||||||
let reduce = Belt.Array.reduce
|
let reduce = Belt.Array.reduce
|
||||||
let reducei = Belt.Array.reduceWithIndex
|
let reducei = Belt.Array.reduceWithIndex
|
||||||
|
let some = Belt.Array.some
|
||||||
let isEmpty = r => length(r) < 1
|
let isEmpty = r => length(r) < 1
|
||||||
let stableSortBy = Belt.SortArray.stableSortBy
|
let stableSortBy = Belt.SortArray.stableSortBy
|
||||||
let toNoneIfEmpty = r => isEmpty(r) ? None : Some(r)
|
let toNoneIfEmpty = r => isEmpty(r) ? None : Some(r)
|
||||||
|
@ -630,6 +631,19 @@ module A = {
|
||||||
)
|
)
|
||||||
let filter = Js.Array.filter
|
let filter = Js.Array.filter
|
||||||
let joinWith = Js.Array.joinWith
|
let joinWith = Js.Array.joinWith
|
||||||
|
let transpose = (xs: array<array<'a>>): array<array<'a>> => {
|
||||||
|
let arr: array<array<'a>> = []
|
||||||
|
for i in 0 to length(xs) - 1 {
|
||||||
|
for j in 0 to length(xs[i]) - 1 {
|
||||||
|
if Js.Array.length(arr) <= j {
|
||||||
|
ignore(Js.Array.push([xs[i][j]], arr))
|
||||||
|
} else {
|
||||||
|
ignore(Js.Array.push(xs[i][j], arr[j]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
arr
|
||||||
|
}
|
||||||
|
|
||||||
let all = (p: 'a => bool, xs: array<'a>): bool => length(filter(p, xs)) == length(xs)
|
let all = (p: 'a => bool, xs: array<'a>): bool => length(filter(p, xs)) == length(xs)
|
||||||
let any = (p: 'a => bool, xs: array<'a>): bool => length(filter(p, xs)) > 0
|
let any = (p: 'a => bool, xs: array<'a>): bool => length(filter(p, xs)) > 0
|
||||||
|
@ -751,7 +765,7 @@ module A = {
|
||||||
let diff = (t: t): array<float> =>
|
let diff = (t: t): array<float> =>
|
||||||
Belt.Array.zipBy(t, Belt.Array.sliceToEnd(t, 1), (left, right) => right -. left)
|
Belt.Array.zipBy(t, Belt.Array.sliceToEnd(t, 1), (left, right) => right -. left)
|
||||||
|
|
||||||
let cumsum = (t: t): array<float> => accumulate((a, b) => a +. b, t)
|
let cumSum = (t: t): array<float> => accumulate((a, b) => a +. b, t)
|
||||||
let cumProd = (t: t): array<float> => accumulate((a, b) => a *. b, t)
|
let cumProd = (t: t): array<float> => accumulate((a, b) => a *. b, t)
|
||||||
|
|
||||||
exception RangeError(string)
|
exception RangeError(string)
|
||||||
|
|
|
@ -327,8 +327,8 @@ module Zipped = {
|
||||||
module PointwiseCombination = {
|
module PointwiseCombination = {
|
||||||
// t1Interpolator and t2Interpolator are functions from XYShape.XtoY, e.g. linearBetweenPointsExtrapolateFlat.
|
// t1Interpolator and t2Interpolator are functions from XYShape.XtoY, e.g. linearBetweenPointsExtrapolateFlat.
|
||||||
let combine: (
|
let combine: (
|
||||||
(float, float) => result<float, Operation.Error.t>,
|
|
||||||
interpolator,
|
interpolator,
|
||||||
|
(float, float) => result<float, Operation.Error.t>,
|
||||||
T.t,
|
T.t,
|
||||||
T.t,
|
T.t,
|
||||||
) => result<T.t, Operation.Error.t> = %raw(`
|
) => result<T.t, Operation.Error.t> = %raw(`
|
||||||
|
@ -337,7 +337,7 @@ module PointwiseCombination = {
|
||||||
// and interpolates the value on the other side, thus accumulating xs and ys.
|
// and interpolates the value on the other side, thus accumulating xs and ys.
|
||||||
// This is written in raw JS because this can still be a bottleneck, and using refs for the i and j indices is quite painful.
|
// This is written in raw JS because this can still be a bottleneck, and using refs for the i and j indices is quite painful.
|
||||||
|
|
||||||
function(fn, interpolator, t1, t2) {
|
function(interpolator, fn, t1, t2) {
|
||||||
let t1n = t1.xs.length;
|
let t1n = t1.xs.length;
|
||||||
let t2n = t2.xs.length;
|
let t2n = t2.xs.length;
|
||||||
let outX = [];
|
let outX = [];
|
||||||
|
@ -399,11 +399,11 @@ module PointwiseCombination = {
|
||||||
This is from an approach to kl divergence that was ultimately rejected. Leaving it in for now because it may help us factor `combine` out of raw javascript soon.
|
This is from an approach to kl divergence that was ultimately rejected. Leaving it in for now because it may help us factor `combine` out of raw javascript soon.
|
||||||
*/
|
*/
|
||||||
let combineAlongSupportOfSecondArgument0: (
|
let combineAlongSupportOfSecondArgument0: (
|
||||||
(float, float) => result<float, Operation.Error.t>,
|
|
||||||
interpolator,
|
interpolator,
|
||||||
|
(float, float) => result<float, Operation.Error.t>,
|
||||||
T.t,
|
T.t,
|
||||||
T.t,
|
T.t,
|
||||||
) => result<T.t, Operation.Error.t> = (fn, interpolator, t1, t2) => {
|
) => result<T.t, Operation.Error.t> = (interpolator, fn, t1, t2) => {
|
||||||
let newYs = []
|
let newYs = []
|
||||||
let newXs = []
|
let newXs = []
|
||||||
let (l1, l2) = (E.A.length(t1.xs), E.A.length(t2.xs))
|
let (l1, l2) = (E.A.length(t1.xs), E.A.length(t2.xs))
|
||||||
|
@ -496,29 +496,9 @@ module PointwiseCombination = {
|
||||||
let newYs = E.A.fmap(x => XtoY.linear(x, t), newXs)
|
let newYs = E.A.fmap(x => XtoY.linear(x, t), newXs)
|
||||||
{xs: newXs, ys: newYs}
|
{xs: newXs, ys: newYs}
|
||||||
}
|
}
|
||||||
// This function is used for klDivergence
|
|
||||||
let combineAlongSupportOfSecondArgument: (
|
|
||||||
(float, float) => result<float, Operation.Error.t>,
|
|
||||||
T.t,
|
|
||||||
T.t,
|
|
||||||
) => result<T.t, Operation.Error.t> = (fn, prediction, answer) => {
|
|
||||||
let combineWithFn = (answerX: float, i: int) => {
|
|
||||||
let answerY = answer.ys[i]
|
|
||||||
let predictionY = XtoY.linear(answerX, prediction)
|
|
||||||
fn(predictionY, answerY)
|
|
||||||
}
|
|
||||||
let newYsWithError = Js.Array.mapi((x, i) => combineWithFn(x, i), answer.xs)
|
|
||||||
let newYsOrError = E.A.R.firstErrorOrOpen(newYsWithError)
|
|
||||||
let result = switch newYsOrError {
|
|
||||||
| Ok(a) => Ok({xs: answer.xs, ys: a})
|
|
||||||
| Error(b) => Error(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
let addCombine = (interpolator: interpolator, t1: T.t, t2: T.t): T.t =>
|
let addCombine = (interpolator: interpolator, t1: T.t, t2: T.t): T.t =>
|
||||||
combine((a, b) => Ok(a +. b), interpolator, t1, t2)->E.R.toExn(
|
combine(interpolator, (a, b) => Ok(a +. b), t1, t2)->E.R.toExn(
|
||||||
"Add operation should never fail",
|
"Add operation should never fail",
|
||||||
_,
|
_,
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,21 +1,35 @@
|
||||||
# Squiggle For VS Code
|
# Squiggle For VS Code
|
||||||
|
|
||||||
_[marketplace](https://marketplace.visualstudio.com/items?itemName=QURI.vscode-squiggle)_
|
## About
|
||||||
|
|
||||||
This extension provides support for [Squiggle](https://www.squiggle-language.com/) in VS Code.
|
This extension provides support for [Squiggle](https://www.squiggle-language.com/) in VS Code. It can be found in the VS code _[marketplace](https://marketplace.visualstudio.com/items?itemName=QURI.vscode-squiggle)_
|
||||||
|
|
||||||
Features:
|
Features:
|
||||||
|
|
||||||
- Preview `.squiggle` files in a preview pane
|
- Preview `.squiggle` files in a preview pane
|
||||||
- Syntax highlighting for `.squiggle` and `.squiggleU` files
|
- Syntax highlighting for `.squiggle` and `.squiggleU` files
|
||||||
|
|
||||||
# Configuration
|
## Installation
|
||||||
|
|
||||||
Some preview settings, e.g. whether to show the summary table or types of outputs, can be configurable on in the VS Code settings and persist between different preview sessions.
|
You can install this extension by going to the "extensions" tab, searching for "Squiggle", and then installing it.
|
||||||
|
|
||||||
|
![](./images/vs-code-install.png)
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
After loading a `.squiggle` file, an "Open Preview" button will appear. If you click it, the squiggle model will be shown, and updated as you edit and save you file.
|
||||||
|
|
||||||
|
![](./images/extension-screenshot.png)
|
||||||
|
|
||||||
|
### Configuration (optional)
|
||||||
|
|
||||||
|
Some preview settings, e.g. whether to show the summary table or types of outputs, can be configurable on in the VS Code settings and persist between different preview sessions. The VS Code settings can be accessed with the shortcut `Ctrl+,` with `Ctrl+Shift+P` + searching "Open Settings", or by accessing a file like `$HOME/.config/Code/User/settings.json` in Linux (see [here](https://stackoverflow.com/questions/65908987/how-can-i-open-visual-studio-codes-settings-json-file)) for other operating systems.
|
||||||
|
|
||||||
|
![](./images/vs-code-settings.png)
|
||||||
|
|
||||||
Check out the full list of Squiggle settings in the main VS Code settings.
|
Check out the full list of Squiggle settings in the main VS Code settings.
|
||||||
|
|
||||||
# Build locally
|
## Build locally
|
||||||
|
|
||||||
We assume you ran `yarn` at the monorepo level for all dependencies.
|
We assume you ran `yarn` at the monorepo level for all dependencies.
|
||||||
|
|
||||||
|
|
BIN
packages/vscode-ext/images/extension-screenshot.png
Normal file
BIN
packages/vscode-ext/images/extension-screenshot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 233 KiB |
BIN
packages/vscode-ext/images/vs-code-install.png
Normal file
BIN
packages/vscode-ext/images/vs-code-install.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 187 KiB |
BIN
packages/vscode-ext/images/vs-code-settings.png
Normal file
BIN
packages/vscode-ext/images/vs-code-settings.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 77 KiB |
|
@ -133,18 +133,18 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/glob": "^7.2.0",
|
"@types/glob": "^7.2.0",
|
||||||
"@types/node": "18.x",
|
"@types/node": "18.x",
|
||||||
"@types/vscode": "^1.68.0",
|
"@types/vscode": "^1.69.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.30.4",
|
"@typescript-eslint/eslint-plugin": "^5.30.6",
|
||||||
"@typescript-eslint/parser": "^5.30.4",
|
"@typescript-eslint/parser": "^5.30.6",
|
||||||
"eslint": "^8.18.0",
|
"eslint": "^8.20.0",
|
||||||
"glob": "^8.0.3",
|
"glob": "^8.0.3",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"typescript": "^4.7.4",
|
"typescript": "^4.7.4",
|
||||||
"vsce-yarn-patch": "^1.66.2"
|
"vsce-yarn-patch": "^1.66.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"vscode-languageclient": "^8.0.1",
|
"vscode-languageclient": "^8.0.2",
|
||||||
"vscode-languageserver": "^8.0.1",
|
"vscode-languageserver": "^8.0.2",
|
||||||
"vscode-languageserver-textdocument": "^1.0.5",
|
"vscode-languageserver-textdocument": "^1.0.5",
|
||||||
"@quri/squiggle-lang": "^0.2.11"
|
"@quri/squiggle-lang": "^0.2.11"
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,11 +12,11 @@
|
||||||
"format": "prettier --write ."
|
"format": "prettier --write ."
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@docusaurus/core": "2.0.0-beta.21",
|
"@docusaurus/core": "2.0.0-rc.1",
|
||||||
"@docusaurus/preset-classic": "2.0.0-beta.21",
|
"@docusaurus/preset-classic": "2.0.0-rc.1",
|
||||||
"@quri/squiggle-components": "^0.2.20",
|
"@quri/squiggle-components": "^0.2.20",
|
||||||
"base64-js": "^1.5.1",
|
"base64-js": "^1.5.1",
|
||||||
"clsx": "^1.2.0",
|
"clsx": "^1.2.1",
|
||||||
"hast-util-is-element": "2.1.2",
|
"hast-util-is-element": "2.1.2",
|
||||||
"pako": "^2.0.4",
|
"pako": "^2.0.4",
|
||||||
"prism-react-renderer": "^1.3.5",
|
"prism-react-renderer": "^1.3.5",
|
||||||
|
|
Loading…
Reference in New Issue
Block a user