Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plot.vega #3139

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/ninety-hotels-thank.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@quri/squiggle-lang": patch
"@quri/squiggle-components": patch
---

Added Vega Plot option
3 changes: 3 additions & 0 deletions packages/components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,13 @@
"react-draggable": "^4.4.6",
"react-hook-form": "^7.50.0",
"react-markdown": "^9.0.1",
"react-vega": "^7.6.0",
"remark-gfm": "^4.0.0",
"shikiji": "^0.10.2",
"unist-util-visit": "^5.0.0",
"unist-util-visit-parents": "^6.0.1",
"vega": "^5.28.0",
"vega-lite": "^5.17.0",
"zod": "^3.22.4"
},
"devDependencies": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type { Meta, StoryObj } from "@storybook/react";

import { SquiggleChart } from "../../components/SquiggleChart.js";

const meta = {
component: SquiggleChart,
} satisfies Meta<typeof SquiggleChart>;
export default meta;
type Story = StoryObj<typeof meta>;

export const Basic: Story = {
args: {
code: `
Plot.vega(
{
data: {
values: [
{ date: "2010-01-01", value: 50 },
{ date: "2010-03-01", value: 33 },
{ date: "2010-05-01", value: 41 },
{ date: "2010-07-01", value: 37 },
{ date: "2010-09-01", value: 29 },
{ date: "2010-11-01", value: 35 },
{ date: "2011-01-01", value: 43 },
{ date: "2011-05-01", value: 56 },
{ date: "2011-07-01", value: 62 },
],
},
mark: { type: "line", point: true },
encoding: {
x: { field: "date", type: "temporal", timeUnit: "year" },
y: { field: "value", type: "quantitative" },
},
}
)
`,
},
};
4 changes: 4 additions & 0 deletions packages/components/src/styles/common.css
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,7 @@
.cm-tooltip-autocomplete .cm-tooltip {
background: white;
}

.vega-embed {
width: 100%;
}
21 changes: 17 additions & 4 deletions packages/components/src/widgets/PlotWidget/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { lazy, Suspense } from "react";
import { VisualizationSpec } from "react-vega";

import { DistributionsChart } from "../DistWidget/DistributionsChart.js";
import { CHART_TO_DIST_HEIGHT_ADJUSTMENT } from "../DistWidget/index.js";
import { DistFunctionChart } from "../LambdaWidget/FunctionChart/DistFunctionChart.js";
Expand All @@ -6,6 +9,16 @@ import { widgetRegistry } from "../registry.js";
import { RelativeValuesGridChart } from "./RelativeValuesGridChart/index.js";
import { ScatterChart } from "./ScatterChart/index.js";

const VegaLazy = lazy(() =>
import("react-vega").then((module) => ({ default: module.Vega }))
);

const vega = (spec: VisualizationSpec) => (
<Suspense fallback={<div>Loading...</div>}>
<VegaLazy spec={spec} />
</Suspense>
);

widgetRegistry.register("Plot", {
Chart: (value, settings) => {
const plot = value.value;
Expand All @@ -20,7 +33,7 @@ widgetRegistry.register("Plot", {
height={settings.chartHeight * CHART_TO_DIST_HEIGHT_ADJUSTMENT}
/>
);
case "numericFn": {
case "numericFn":
return (
<NumericFunctionChart
plot={plot}
Expand All @@ -29,8 +42,7 @@ widgetRegistry.register("Plot", {
xCount={settings.functionChartSettings.count}
/>
);
}
case "distFn": {
case "distFn":
return (
<DistFunctionChart
plot={plot}
Expand All @@ -43,7 +55,6 @@ widgetRegistry.register("Plot", {
xCount={settings.functionChartSettings.count}
/>
);
}
case "scatter":
return (
<ScatterChart
Expand All @@ -56,6 +67,8 @@ widgetRegistry.register("Plot", {
return (
<RelativeValuesGridChart plot={plot} environment={environment} />
);
case "vega":
return vega(JSON.parse(plot.spec));
default:
// can happen if squiggle-lang version is too fresh and we messed up the components -> squiggle-lang dependency
return `Unsupported plot ${plot satisfies never}`;
Expand Down
1 change: 1 addition & 0 deletions packages/hub/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"@quri/versioned-squiggle-components": "workspace:*",
"@vercel/analytics": "^1.2.2",
"base64-js": "^1.5.1",
"canvas": "^2.11.2",
"clsx": "^2.1.0",
"d3": "^7.8.5",
"date-fns": "^3.3.1",
Expand Down
4 changes: 2 additions & 2 deletions packages/squiggle-lang/src/fr/danger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,7 @@ Note: The Poisson distribution is a discrete distribution. When representing thi
],
definitions: [
makeDefinition([frAny()], frAny(), ([v]) => {
return simpleValueToValue(simpleValueFromValue(v));
return simpleValueToValue(simpleValueFromValue(v, false));
}),
],
}),
Expand All @@ -543,7 +543,7 @@ Note: The Poisson distribution is a discrete distribution. When representing thi
definitions: [
makeDefinition([frAny()], frString, ([v]) => {
return JSON.stringify(
simpleValueToJson(removeLambdas(simpleValueFromValue(v)))
simpleValueToJson(removeLambdas(simpleValueFromValue(v, false)))
);
}),
],
Expand Down
56 changes: 56 additions & 0 deletions packages/squiggle-lang/src/fr/plot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { REArgumentError, REOther } from "../errors/messages.js";
import { makeFnExample } from "../library/registry/core.js";
import { makeDefinition } from "../library/registry/fnDefinition.js";
import {
frAny,
frArray,
frBool,
frDeprecated,
Expand All @@ -27,6 +28,7 @@ import {
} from "../library/registry/helpers.js";
import { Lambda } from "../reducer/lambda.js";
import { clamp, sort, uniq } from "../utility/E_A_Floats.js";
import { simpleValueFromValue } from "../value/simpleValue.js";
import { VDomain } from "../value/VDomain.js";
import { LabeledDistribution, Plot } from "../value/VPlot.js";
import { Scale } from "../value/VScale.js";
Expand Down Expand Up @@ -544,4 +546,58 @@ Plot.scatter({
),
],
}),
maker.make({
name: "vega",
examples: [
makeFnExample(
`xDist = SampleSet.fromDist(2 to 5)
yDist = normal({p5:-3, p95:3}) * 5 - xDist ^ 2
Plot.scatter({
xDist: xDist,
yDist: yDist,
xScale: Scale.log({min: 1.5}),
})`,
{ isInteractive: true }
),
makeFnExample(
`xDist = SampleSet.fromDist(normal({p5:-2, p95:5}))
yDist = normal({p5:-3, p95:3}) * 5 - xDist
Plot.scatter({
xDist: xDist,
yDist: yDist,
xScale: Scale.symlog({title: "X Axis Title"}),
yScale: Scale.symlog({title: "Y Axis Title"}),
})`,
{ isInteractive: true }
),
],
definitions: [
makeDefinition(
[
frDict(
["data", frAny()],
["height", frOptional(frNumber)],
["config", frOptional(frAny())],
["mark", frOptional(frAny())],
["encoding", frOptional(frAny())],
["view", frOptional(frAny())],
["projection", frOptional(frAny())]
),
],
frPlot,
([{ data, config, mark, encoding, height, view, projection }]) => {
return {
type: "vega",
data: simpleValueFromValue(data, true),
config: config && simpleValueFromValue(config, true),
mark: mark && simpleValueFromValue(mark, true),
encoding: encoding && simpleValueFromValue(encoding, true),
height: height || undefined,
view: view && simpleValueFromValue(view, true),
projection: projection && simpleValueFromValue(projection, true),
};
}
),
],
}),
];
33 changes: 33 additions & 0 deletions packages/squiggle-lang/src/library/registry/frTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,39 @@ export function frDict<
[k in K4]: T4;
} & { [k in K5]: T5 } & { [k in K6]: T6 } & { [k in K7]: T7 }
>;
export function frDict<
K1 extends string,
T1,
K2 extends string,
T2,
K3 extends string,
T3,
K4 extends string,
T4,
K5 extends string,
T5,
K6 extends string,
T6,
K7 extends string,
T7,
K8 extends string,
T8,
>(
kv1: [K1, FRType<T1>],
kv2: [K2, FRType<T2>],
kv3: [K3, FRType<T3>],
kv4: [K4, FRType<T4>],
kv5: [K5, FRType<T5>],
kv6: [K6, FRType<T6>],
kv7: [K7, FRType<T7>],
kv8: [K8, FRType<T8>]
): FRType<
{ [k in K1]: T1 } & { [k in K2]: T2 } & { [k in K3]: T3 } & {
[k in K4]: T4;
} & { [k in K5]: T5 } & { [k in K6]: T6 } & {
[k in K7]: T7 & { [k in K8]: T8 };
}
>;

export function frDict<T extends object>(
...allKvs: [string, FRType<unknown>][]
Expand Down
20 changes: 19 additions & 1 deletion packages/squiggle-lang/src/public/SqValue/SqPlot.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import fromPairs from "lodash/fromPairs.js";

import { clamp, sort, uniq } from "../../utility/E_A_Floats.js";
import { vegaPlotToSimpleValues } from "../../value/simpleValue.js";
import { Plot, vPlot } from "../../value/VPlot.js";
import { SqValueContext } from "../SqValueContext.js";
import { SqValuePathEdge } from "../SqValuePath.js";
Expand Down Expand Up @@ -28,6 +31,8 @@ export function wrapPlot(value: Plot, context?: SqValueContext): SqPlot {
return new SqScatterPlot(value, context);
case "relativeValues":
return new SqRelativeValuesPlot(value, context);
case "vega":
return new SqVegaPlot(value, context);
}
}

Expand Down Expand Up @@ -304,9 +309,22 @@ export class SqRelativeValuesPlot extends SqAbstractPlot<"relativeValues"> {
}
}

export class SqVegaPlot extends SqAbstractPlot<"vega"> {
tag = "vega" as const;

get spec(): any {
return JSON.stringify({
width: "container",
height: 300,
...fromPairs(vegaPlotToSimpleValues(this._value)),
});
}
}

export type SqPlot =
| SqDistributionsPlot
| SqNumericFnPlot
| SqDistFnPlot
| SqScatterPlot
| SqRelativeValuesPlot;
| SqRelativeValuesPlot
| SqVegaPlot;
14 changes: 14 additions & 0 deletions packages/squiggle-lang/src/value/VPlot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,17 @@ export type CommonPlotArgs = {
title?: string;
};

export type VegaPlot = {
type: "vega";
data: any;
config?: any;
mark?: any;
encoding?: any;
height?: number;
view?: any;
projection?: any;
};

export type Plot = CommonPlotArgs &
(
| {
Expand Down Expand Up @@ -53,6 +64,7 @@ export type Plot = CommonPlotArgs &
fn: Lambda;
ids: readonly string[];
}
| VegaPlot
);

export class VPlot extends BaseValue implements Indexable {
Expand All @@ -76,6 +88,8 @@ export class VPlot extends BaseValue implements Indexable {
return `Scatter plot for distributions ${this.value.xDist} and ${this.value.yDist}`;
case "relativeValues":
return `Plot for relative values ${this.value.ids.join(", ")}`;
case "vega":
return `Vega plot`;
}
}

Expand Down
4 changes: 4 additions & 0 deletions packages/squiggle-lang/src/value/VVoid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ export class VVoid extends BaseValue {
valueToString() {
return "()";
}

isEqual(other: VVoid) {
return true;
}
}

export const vVoid = () => new VVoid();
3 changes: 1 addition & 2 deletions packages/squiggle-lang/src/value/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,8 @@ export function isEqual(a: Value, b: Value): boolean {
case "Array":
case "Specification":
case "Dict":
return a.isEqual(b as any);
case "Void":
return true;
return a.isEqual(b as any);
}

if (a.toString() !== b.toString()) {
Expand Down
Loading
Loading