Merge branch 'develop' into fix-595

This commit is contained in:
Sam Nolan 2022-06-23 07:31:38 +00:00
commit 627211495e
17 changed files with 810 additions and 832 deletions

View File

@ -19,25 +19,38 @@ import {
} from "./DistributionVegaScales";
import { NumberShower } from "./NumberShower";
type DistributionChartProps = {
distribution: Distribution;
width?: number;
height: number;
export type DistributionPlottingSettings = {
/** Whether to show a summary of means, stdev, percentiles etc */
showSummary: boolean;
/** Whether to show the user graph controls (scale etc) */
showControls?: boolean;
showControls: boolean;
/** Set the x scale to be logarithmic by deault */
logX: boolean;
/** Set the y scale to be exponential by deault */
expY: boolean;
};
export type DistributionChartProps = {
distribution: Distribution;
width?: number;
height: number;
} & DistributionPlottingSettings;
export const DistributionChart: React.FC<DistributionChartProps> = ({
distribution,
height,
showSummary,
width,
showControls = false,
showControls,
logX,
expY,
}) => {
const [isLogX, setLogX] = React.useState(false);
const [isExpY, setExpY] = React.useState(false);
const [isLogX, setLogX] = React.useState(logX);
const [isExpY, setExpY] = React.useState(expY);
React.useEffect(() => setLogX(logX), [logX]);
React.useEffect(() => setExpY(expY), [expY]);
const shape = distribution.pointSet();
const [sized] = useSize((size) => {
if (shape.tag === "Error") {
@ -63,6 +76,7 @@ export const DistributionChart: React.FC<DistributionChartProps> = ({
return (
<div style={{ width: widthProp }}>
{!(isLogX && massBelow0) ? (
<Vega
spec={spec}
data={{ con: shape.value.continuous, dis: shape.value.discrete }}
@ -70,6 +84,11 @@ export const DistributionChart: React.FC<DistributionChartProps> = ({
height={height}
actions={false}
/>
) : (
<ErrorAlert heading="Log Domain Error">
Cannot graph distribution with negative values on logarithmic scale.
</ErrorAlert>
)}
<div className="flex justify-center">
{showSummary && <SummaryTable distribution={distribution} />}
</div>
@ -126,7 +145,7 @@ export const CheckBox: React.FC<CheckBoxProps> = ({
<span title={tooltip}>
<input
type="checkbox"
value={value + ""}
checked={value}
onChange={() => onChange(!value)}
disabled={disabled}
className="form-checkbox"

View File

@ -2,6 +2,7 @@ import * as React from "react";
import { lambdaValue, environment, runForeign } from "@quri/squiggle-lang";
import { FunctionChart1Dist } from "./FunctionChart1Dist";
import { FunctionChart1Number } from "./FunctionChart1Number";
import { DistributionPlottingSettings } from "./DistributionChart";
import { ErrorAlert, MessageAlert } from "./Alert";
export type FunctionChartSettings = {
@ -13,6 +14,7 @@ export type FunctionChartSettings = {
interface FunctionChartProps {
fn: lambdaValue;
chartSettings: FunctionChartSettings;
distributionPlotSettings: DistributionPlottingSettings;
environment: environment;
height: number;
}
@ -21,6 +23,7 @@ export const FunctionChart: React.FC<FunctionChartProps> = ({
fn,
chartSettings,
environment,
distributionPlotSettings,
height,
}) => {
if (fn.parameters.length > 1) {
@ -53,6 +56,7 @@ export const FunctionChart: React.FC<FunctionChartProps> = ({
chartSettings={chartSettings}
environment={environment}
height={height}
distributionPlotSettings={distributionPlotSettings}
/>
);
case "number":

View File

@ -13,7 +13,10 @@ import {
} from "@quri/squiggle-lang";
import { createClassFromSpec } from "react-vega";
import * as percentilesSpec from "../vega-specs/spec-percentiles.json";
import { DistributionChart } from "./DistributionChart";
import {
DistributionChart,
DistributionPlottingSettings,
} from "./DistributionChart";
import { NumberShower } from "./NumberShower";
import { ErrorAlert } from "./Alert";
@ -44,6 +47,7 @@ export type FunctionChartSettings = {
interface FunctionChart1DistProps {
fn: lambdaValue;
chartSettings: FunctionChartSettings;
distributionPlotSettings: DistributionPlottingSettings;
environment: environment;
height: number;
}
@ -150,6 +154,7 @@ export const FunctionChart1Dist: React.FC<FunctionChart1DistProps> = ({
fn,
chartSettings,
environment,
distributionPlotSettings,
height,
}) => {
let [mouseOverlay, setMouseOverlay] = React.useState(0);
@ -175,7 +180,7 @@ export const FunctionChart1Dist: React.FC<FunctionChart1DistProps> = ({
distribution={mouseItem.value.value}
width={400}
height={50}
showSummary={false}
{...distributionPlotSettings}
/>
) : null;

View File

@ -37,6 +37,10 @@ export interface SquiggleChartProps {
showTypes?: boolean;
/** Whether to show graph controls (scale etc)*/
showControls?: boolean;
/** Set the x scale to be logarithmic by deault */
logX?: boolean;
/** Set the y scale to be exponential by deault */
expY?: boolean;
}
const defaultOnChange = () => {};
@ -53,6 +57,8 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = ({
width,
showTypes = false,
showControls = false,
logX = false,
expY = false,
chartSettings = defaultChartSettings,
}) => {
const { result } = useSquiggle({
@ -67,14 +73,20 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = ({
return <SquiggleErrorAlert error={result.value} />;
}
let distributionPlotSettings = {
showControls,
showSummary,
logX,
expY,
};
return (
<SquiggleItem
expression={result.value}
width={width}
height={height}
showSummary={showSummary}
distributionPlotSettings={distributionPlotSettings}
showTypes={showTypes}
showControls={showControls}
chartSettings={chartSettings}
environment={environment ?? defaultEnvironment}
/>

View File

@ -54,6 +54,10 @@ export interface SquiggleEditorProps {
showControls?: boolean;
/** Whether to show a summary table */
showSummary?: boolean;
/** Whether to log the x coordinate on distribution charts */
logX?: boolean;
/** Whether to exp the y coordinate on distribution charts */
expY?: boolean;
}
export const SquiggleEditor: React.FC<SquiggleEditorProps> = ({
@ -69,8 +73,14 @@ export const SquiggleEditor: React.FC<SquiggleEditorProps> = ({
showTypes = false,
showControls = false,
showSummary = false,
logX = false,
expY = false,
}: SquiggleEditorProps) => {
const [code, setCode] = useState(initialSquiggleString);
React.useEffect(
() => setCode(initialSquiggleString),
[initialSquiggleString]
);
const { result, observableRef } = useSquiggle({
code,
@ -86,6 +96,13 @@ export const SquiggleEditor: React.FC<SquiggleEditorProps> = ({
count: diagramCount,
};
const distributionPlotSettings = {
showControls,
showSummary,
logX,
expY,
};
return (
<div ref={observableRef}>
<SquiggleContainer>
@ -95,9 +112,8 @@ export const SquiggleEditor: React.FC<SquiggleEditorProps> = ({
expression={result.value}
width={width}
height={200}
showSummary={showSummary}
distributionPlotSettings={distributionPlotSettings}
showTypes={showTypes}
showControls={showControls}
chartSettings={chartSettings}
environment={environment ?? defaultEnvironment}
/>
@ -136,6 +152,10 @@ export const SquigglePartial: React.FC<SquigglePartialProps> = ({
jsImports = defaultImports,
}: SquigglePartialProps) => {
const [code, setCode] = useState(initialSquiggleString);
React.useEffect(
() => setCode(initialSquiggleString),
[initialSquiggleString]
);
const { result, observableRef } = useSquigglePartial({
code,

View File

@ -5,7 +5,10 @@ import {
declaration,
} from "@quri/squiggle-lang";
import { NumberShower } from "./NumberShower";
import { DistributionChart } from "./DistributionChart";
import {
DistributionChart,
DistributionPlottingSettings,
} from "./DistributionChart";
import { FunctionChart, FunctionChartSettings } from "./FunctionChart";
function getRange<a>(x: declaration<a>) {
@ -61,12 +64,9 @@ export interface SquiggleItemProps {
expression: squiggleExpression;
width?: number;
height: number;
/** Whether to show a summary of statistics for distributions */
showSummary: boolean;
distributionPlotSettings: DistributionPlottingSettings;
/** Whether to show type information */
showTypes: boolean;
/** Whether to show users graph controls (scale etc) */
showControls: boolean;
/** Settings for displaying functions */
chartSettings: FunctionChartSettings;
/** Environment for further function executions */
@ -77,9 +77,8 @@ export const SquiggleItem: React.FC<SquiggleItemProps> = ({
expression,
width,
height,
showSummary,
distributionPlotSettings,
showTypes = false,
showControls = false,
chartSettings,
environment,
}) => {
@ -104,10 +103,9 @@ export const SquiggleItem: React.FC<SquiggleItemProps> = ({
) : null}
<DistributionChart
distribution={expression.value}
{...distributionPlotSettings}
height={height}
width={width}
showSummary={showSummary}
showControls={showControls}
/>
</VariableBox>
);
@ -155,11 +153,10 @@ export const SquiggleItem: React.FC<SquiggleItemProps> = ({
expression={r}
width={width !== undefined ? width - 20 : width}
height={50}
distributionPlotSettings={distributionPlotSettings}
showTypes={showTypes}
showControls={showControls}
chartSettings={chartSettings}
environment={environment}
showSummary={showSummary}
/>
</div>
</div>
@ -181,8 +178,7 @@ export const SquiggleItem: React.FC<SquiggleItemProps> = ({
width={width !== undefined ? width - 20 : width}
height={height / 3}
showTypes={showTypes}
showSummary={showSummary}
showControls={showControls}
distributionPlotSettings={distributionPlotSettings}
chartSettings={chartSettings}
environment={environment}
/>
@ -220,6 +216,7 @@ export const SquiggleItem: React.FC<SquiggleItemProps> = ({
<FunctionChart
fn={expression.value}
chartSettings={chartSettings}
distributionPlotSettings={distributionPlotSettings}
height={height}
environment={{
sampleCount: environment.sampleCount / 10,
@ -234,6 +231,7 @@ export const SquiggleItem: React.FC<SquiggleItemProps> = ({
<FunctionChart
fn={expression.value.fn}
chartSettings={getChartSettings(expression.value)}
distributionPlotSettings={distributionPlotSettings}
height={height}
environment={{
sampleCount: environment.sampleCount / 10,

View File

@ -1,4 +1,4 @@
import React, { FC, Fragment, useState } from "react";
import React, { FC, Fragment, useState, useEffect } from "react";
import ReactDOM from "react-dom";
import { Path, useForm, UseFormRegister, useWatch } from "react-hook-form";
import * as yup from "yup";
@ -32,9 +32,14 @@ interface PlaygroundProps {
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;
onSettingsChange?(settings: any): void;
/** Should we show the editor? */
showEditor?: boolean;
}
@ -71,6 +76,8 @@ const schema = yup
showControls: yup.boolean(),
showSummary: yup.boolean(),
showEditor: yup.boolean(),
logX: yup.boolean(),
expY: yup.boolean(),
showSettingsPage: yup.boolean().default(false),
diagramStart: yup
.number()
@ -203,8 +210,11 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
showTypes = false,
showControls = false,
showSummary = false,
logX = false,
expY = false,
code: controlledCode,
onCodeChange,
onSettingsChange,
showEditor = true,
}) => {
const [uncontrolledCode, setUncontrolledCode] = useState(
@ -219,10 +229,12 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
sampleCount: 1000,
xyPointLength: 1000,
chartHeight: 150,
showTypes: showTypes,
showControls: showControls,
showSummary: showSummary,
showEditor: showEditor,
showTypes,
showControls,
logX,
expY,
showSummary,
showEditor,
leftSizePercent: 50,
showSettingsPage: false,
diagramStart: 0,
@ -233,6 +245,11 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
const vars = useWatch({
control,
});
useEffect(() => {
onSettingsChange?.(vars);
}, [vars, onSettingsChange]);
const chartSettings = {
start: Number(vars.diagramStart),
stop: Number(vars.diagramStop),
@ -313,6 +330,16 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
<div className="pt-8">
<HeadedSection title="Distribution Display Settings">
<div className="space-y-2">
<Checkbox
register={register}
name="logX"
label="Show x scale logarithmically"
/>
<Checkbox
register={register}
name="expY"
label="Show y scale exponentially"
/>
<Checkbox
register={register}
name="showControls"
@ -403,6 +430,8 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
showTypes={vars.showTypes}
showControls={vars.showControls}
showSummary={vars.showSummary}
logX={vars.logX}
expY={vars.expY}
bindings={defaultBindings}
jsImports={imports}
/>

View File

@ -5,7 +5,7 @@ import { testRun } from "./TestHelpers";
describe("cumulative density function of a normal distribution", () => {
test("at 3 stdevs to the right of the mean is near 1", () => {
fc.assert(
fc.property(fc.float(), fc.float({ min: 1e-7 }), (mean, stdev) => {
fc.property(fc.integer(), fc.integer({ min: 1 }), (mean, stdev) => {
let threeStdevsAboveMean = mean + 3 * stdev;
let squiggleString = `cdf(normal(${mean}, ${stdev}), ${threeStdevsAboveMean})`;
let squiggleResult = testRun(squiggleString);
@ -16,7 +16,7 @@ describe("cumulative density function of a normal distribution", () => {
test("at 3 stdevs to the left of the mean is near 0", () => {
fc.assert(
fc.property(fc.float(), fc.float({ min: 1e-7 }), (mean, stdev) => {
fc.property(fc.integer(), fc.integer({ min: 1 }), (mean, stdev) => {
let threeStdevsBelowMean = mean - 3 * stdev;
let squiggleString = `cdf(normal(${mean}, ${stdev}), ${threeStdevsBelowMean})`;
let squiggleResult = testRun(squiggleString);

View File

@ -4,13 +4,16 @@ import * as fc from "fast-check";
// Beware: float64Array makes it appear in an infinite loop.
let arrayGen = () =>
fc.float32Array({
fc
.float32Array({
minLength: 10,
maxLength: 10000,
noDefaultInfinity: true,
noNaN: true,
});
})
.filter(
(xs_) => Math.min(...Array.from(xs_)) != Math.max(...Array.from(xs_))
);
describe("cumulative density function", () => {
let n = 10000;
@ -119,11 +122,7 @@ describe("cumulative density function", () => {
{ sampleCount: n, xyPointLength: 100 }
);
let cdfValue = dist.cdf(x).value;
if (x < Math.min(...xs)) {
expect(cdfValue).toEqual(0);
} else {
expect(cdfValue).toBeGreaterThan(0);
}
expect(cdfValue).toBeGreaterThanOrEqual(0);
})
);
});

View File

@ -5,15 +5,11 @@ import * as fc from "fast-check";
describe("Scalar manipulation is well-modeled by javascript math", () => {
test("in the case of natural logarithms", () => {
fc.assert(
fc.property(fc.float(), (x) => {
fc.property(fc.integer(), (x) => {
let squiggleString = `log(${x})`;
let squiggleResult = testRun(squiggleString);
if (x == 0) {
expect(squiggleResult.value).toEqual(-Infinity);
} else if (x < 0) {
expect(squiggleResult.value).toEqual(
"somemessage (confused why a test case hasn't pointed out to me that this message is bogus)"
);
} else {
expect(squiggleResult.value).toEqual(Math.log(x));
}
@ -23,7 +19,7 @@ describe("Scalar manipulation is well-modeled by javascript math", () => {
test("in the case of addition (with assignment)", () => {
fc.assert(
fc.property(fc.float(), fc.float(), fc.float(), (x, y, z) => {
fc.property(fc.integer(), fc.integer(), fc.integer(), (x, y, z) => {
let squiggleString = `x = ${x}; y = ${y}; z = ${z}; x + y + z`;
let squiggleResult = testRun(squiggleString);
expect(squiggleResult.value).toBeCloseTo(x + y + z);

View File

@ -1,11 +1,10 @@
import { errorValueToString } from "../../src/js/index";
import { testRun } from "./TestHelpers";
import * as fc from "fast-check";
describe("Symbolic mean", () => {
test("mean(triangular(x,y,z))", () => {
fc.assert(
fc.property(fc.float(), fc.float(), fc.float(), (x, y, z) => {
fc.property(fc.integer(), fc.integer(), fc.integer(), (x, y, z) => {
if (!(x < y && y < z)) {
try {
let squiggleResult = testRun(`mean(triangular(${x},${y},${z}))`);

View File

@ -19,7 +19,7 @@ do
fi
done
files=`ls src/rescript/**/**/*.resi src/rescript/**/*.resi` # src/rescript/*/resi
files=`ls src/rescript/**/*.resi` # src/rescript/*/resi
for file in $files
do
current=`cat $file`

View File

@ -51,7 +51,7 @@
"bisect_ppx": "^2.7.1",
"chalk": "^5.0.1",
"codecov": "^3.8.3",
"fast-check": "^2.25.0",
"fast-check": "^3.0.0",
"gentype": "^4.4.0",
"jest": "^27.5.1",
"moduleserve": "^0.9.1",

View File

@ -6,11 +6,13 @@ This website is built using [Docusaurus 2](https://docusaurus.io/), a modern sta
We assume you ran `yarn` at monorepo level.
The website depends on `squiggle-lang`, which you have to build manually.
The website depends on `squiggle-lang` and `components`, which you have to build manually.
```sh
cd ../squiggle-lang
yarn build
cd ../components
yarn build
```
Generate static content, to the `build` directory.

View File

@ -15,8 +15,10 @@
"@docusaurus/core": "2.0.0-beta.21",
"@docusaurus/preset-classic": "2.0.0-beta.21",
"@quri/squiggle-components": "^0.2.20",
"base64-js": "^1.5.1",
"clsx": "^1.1.1",
"hast-util-is-element": "2.1.2",
"pako": "^2.0.4",
"prism-react-renderer": "^1.3.3",
"react": "^18.1.0",
"react-dom": "^18.2.0",

View File

@ -1,8 +1,52 @@
import { deflate, inflate } from "pako";
import { toByteArray, fromByteArray } from "base64-js";
import React from "react";
import Layout from "@theme/Layout";
import { SquigglePlayground } from "../components/SquigglePlayground";
const HASH_PREFIX = "#code=";
function getHashData() {
if (typeof window === "undefined") {
return {};
}
const hash = window.location.hash;
if (!hash.startsWith(HASH_PREFIX)) {
return {};
}
try {
const compressed = toByteArray(
decodeURIComponent(hash.slice(HASH_PREFIX.length))
);
const text = inflate(compressed, { to: "string" });
return JSON.parse(text);
} catch (err) {
console.error(err);
return {};
}
}
function setHashData(data) {
const text = JSON.stringify({ ...getHashData(), ...data });
const compressed = deflate(text, { level: 9 });
window.history.replaceState(
undefined,
"",
HASH_PREFIX + encodeURIComponent(fromByteArray(compressed))
);
}
export default function PlaygroundPage() {
const playgroundProps = {
initialSquiggleString: "normal(0,1)",
height: 700,
showTypes: true,
...getHashData(),
onCodeChange: (code) => setHashData({ initialSquiggleString: code }),
onSettingsChange: (settings) => {
const { showTypes, showControls, showSummary, showEditor } = settings;
setHashData({ showTypes, showControls, showSummary, showEditor });
},
};
return (
<Layout title="Playground" description="Squiggle Playground">
<div
@ -10,11 +54,7 @@ export default function PlaygroundPage() {
maxWidth: 2000,
}}
>
<SquigglePlayground
initialSquiggleString="normal(0,1)"
height={700}
showTypes={true}
/>
<SquigglePlayground {...playgroundProps} />
</div>
</Layout>
);

1377
yarn.lock

File diff suppressed because it is too large Load Diff