Merge pull request #504 from quantified-uncertainty/function-charts
Function charting
This commit is contained in:
		
						commit
						3cca106079
					
				|  | @ -1,18 +1,24 @@ | ||||||
| import * as React from "react"; | import * as React from "react"; | ||||||
| import _ from "lodash"; | import _ from "lodash"; | ||||||
| import type { Spec } from "vega"; | import type { Spec } from "vega"; | ||||||
| import type { Distribution, errorValue, result } from "@quri/squiggle-lang"; | import { | ||||||
|  |   Distribution, | ||||||
|  |   result, | ||||||
|  |   lambdaValue, | ||||||
|  |   environment, | ||||||
|  |   runForeign, | ||||||
|  |   errorValueToString, | ||||||
|  | } from "@quri/squiggle-lang"; | ||||||
| import { createClassFromSpec } from "react-vega"; | import { createClassFromSpec } from "react-vega"; | ||||||
| import * as percentilesSpec from "../vega-specs/spec-percentiles.json"; | import * as percentilesSpec from "../vega-specs/spec-percentiles.json"; | ||||||
| import { DistributionChart } from "./DistributionChart"; | import { DistributionChart } from "./DistributionChart"; | ||||||
|  | import { NumberShower } from "./NumberShower"; | ||||||
| import { ErrorBox } from "./ErrorBox"; | import { ErrorBox } from "./ErrorBox"; | ||||||
| 
 | 
 | ||||||
| let SquigglePercentilesChart = createClassFromSpec({ | let SquigglePercentilesChart = createClassFromSpec({ | ||||||
|   spec: percentilesSpec as Spec, |   spec: percentilesSpec as Spec, | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| type distPlusFn = (a: number) => result<Distribution, errorValue>; |  | ||||||
| 
 |  | ||||||
| const _rangeByCount = (start: number, stop: number, count: number) => { | const _rangeByCount = (start: number, stop: number, count: number) => { | ||||||
|   const step = (stop - start) / (count - 1); |   const step = (stop - start) / (count - 1); | ||||||
|   const items = _.range(start, stop, step); |   const items = _.range(start, stop, step); | ||||||
|  | @ -27,38 +33,36 @@ function unwrap<a, b>(x: result<a, b>): a { | ||||||
|     throw Error("FAILURE TO UNWRAP"); |     throw Error("FAILURE TO UNWRAP"); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | export type FunctionChartSettings = { | ||||||
|  |   start: number; | ||||||
|  |   stop: number; | ||||||
|  |   count: number; | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| function mapFilter<a, b>(xs: a[], f: (x: a) => b | undefined): b[] { | interface FunctionChartProps { | ||||||
|   let initial: b[] = []; |   fn: lambdaValue; | ||||||
|   return xs.reduce((previous, current) => { |   chartSettings: FunctionChartSettings; | ||||||
|     let value: b | undefined = f(current); |   environment: environment; | ||||||
|     if (value !== undefined) { |  | ||||||
|       return previous.concat([value]); |  | ||||||
|     } else { |  | ||||||
|       return previous; |  | ||||||
|     } |  | ||||||
|   }, initial); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export const FunctionChart: React.FC<{ | export const FunctionChart: React.FC<FunctionChartProps> = ({ | ||||||
|   distPlusFn: distPlusFn; |   fn, | ||||||
|   diagramStart: number; |   chartSettings, | ||||||
|   diagramStop: number; |   environment, | ||||||
|   diagramCount: number; | }: FunctionChartProps) => { | ||||||
| }> = ({ distPlusFn, diagramStart, diagramStop, diagramCount }) => { |  | ||||||
|   let [mouseOverlay, setMouseOverlay] = React.useState(0); |   let [mouseOverlay, setMouseOverlay] = React.useState(0); | ||||||
|   function handleHover(...args) { |   function handleHover(_name: string, value: unknown) { | ||||||
|     setMouseOverlay(args[1]); |     setMouseOverlay(value as number); | ||||||
|   } |   } | ||||||
|   function handleOut() { |   function handleOut() { | ||||||
|     setMouseOverlay(NaN); |     setMouseOverlay(NaN); | ||||||
|   } |   } | ||||||
|   const signalListeners = { mousemove: handleHover, mouseout: handleOut }; |   const signalListeners = { mousemove: handleHover, mouseout: handleOut }; | ||||||
|   let mouseItem = distPlusFn(mouseOverlay); |   let mouseItem = runForeign(fn, [mouseOverlay], environment); | ||||||
|   let showChart = |   let showChart = | ||||||
|     mouseItem.tag === "Ok" ? ( |     mouseItem.tag === "Ok" && mouseItem.value.tag == "distribution" ? ( | ||||||
|       <DistributionChart |       <DistributionChart | ||||||
|         distribution={mouseItem.value} |         distribution={mouseItem.value.value} | ||||||
|         width={400} |         width={400} | ||||||
|         height={140} |         height={140} | ||||||
|         showSummary={false} |         showSummary={false} | ||||||
|  | @ -66,13 +70,49 @@ export const FunctionChart: React.FC<{ | ||||||
|     ) : ( |     ) : ( | ||||||
|       <></> |       <></> | ||||||
|     ); |     ); | ||||||
|   let data1 = _rangeByCount(diagramStart, diagramStop, diagramCount); |   let data1 = _rangeByCount( | ||||||
|   let valueData = mapFilter(data1, (x) => { |     chartSettings.start, | ||||||
|     let result = distPlusFn(x); |     chartSettings.stop, | ||||||
|  |     chartSettings.count | ||||||
|  |   ); | ||||||
|  |   type point = { x: number; value: result<Distribution, string> }; | ||||||
|  |   let valueData: point[] = data1.map((x) => { | ||||||
|  |     let result = runForeign(fn, [x], environment); | ||||||
|     if (result.tag === "Ok") { |     if (result.tag === "Ok") { | ||||||
|       return { x: x, value: result.value }; |       if (result.value.tag == "distribution") { | ||||||
|  |         return { x, value: { tag: "Ok", value: result.value.value } }; | ||||||
|  |       } else { | ||||||
|  |         return { | ||||||
|  |           x, | ||||||
|  |           value: { | ||||||
|  |             tag: "Error", | ||||||
|  |             value: | ||||||
|  |               "Cannot currently render functions that don't return distributions", | ||||||
|  |           }, | ||||||
|  |         }; | ||||||
|       } |       } | ||||||
|   }).map(({ x, value }) => { |     } else { | ||||||
|  |       return { | ||||||
|  |         x, | ||||||
|  |         value: { tag: "Error", value: errorValueToString(result.value) }, | ||||||
|  |       }; | ||||||
|  |     } | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   let initialPartition: [ | ||||||
|  |     { x: number; value: Distribution }[], | ||||||
|  |     { x: number; value: string }[] | ||||||
|  |   ] = [[], []]; | ||||||
|  |   let [functionImage, errors] = valueData.reduce((acc, current) => { | ||||||
|  |     if (current.value.tag === "Ok") { | ||||||
|  |       acc[0].push({ x: current.x, value: current.value.value }); | ||||||
|  |     } else { | ||||||
|  |       acc[1].push({ x: current.x, value: current.value.value }); | ||||||
|  |     } | ||||||
|  |     return acc; | ||||||
|  |   }, initialPartition); | ||||||
|  | 
 | ||||||
|  |   let percentiles = functionImage.map(({ x, value }) => { | ||||||
|     return { |     return { | ||||||
|       x: x, |       x: x, | ||||||
|       p1: unwrap(value.inv(0.01)), |       p1: unwrap(value.inv(0.01)), | ||||||
|  | @ -91,24 +131,25 @@ export const FunctionChart: React.FC<{ | ||||||
|     }; |     }; | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   let errorData = mapFilter(data1, (x) => { |   let groupedErrors = _.groupBy(errors, (x) => x.value); | ||||||
|     let result = distPlusFn(x); |  | ||||||
|     if (result.tag === "Error") { |  | ||||||
|       return { x: x, error: result.value }; |  | ||||||
|     } |  | ||||||
|   }); |  | ||||||
|   let error2 = _.groupBy(errorData, (x) => x.error); |  | ||||||
|   return ( |   return ( | ||||||
|     <> |     <> | ||||||
|       <SquigglePercentilesChart |       <SquigglePercentilesChart | ||||||
|         data={{ facet: valueData }} |         data={{ facet: percentiles }} | ||||||
|         actions={false} |         actions={false} | ||||||
|         signalListeners={signalListeners} |         signalListeners={signalListeners} | ||||||
|       /> |       /> | ||||||
|       {showChart} |       {showChart} | ||||||
|       {_.keysIn(error2).map((k) => ( |       {_.entries(groupedErrors).map(([errorName, errorPoints]) => ( | ||||||
|         <ErrorBox heading={k}> |         <ErrorBox heading={errorName}> | ||||||
|           {`Values: [${error2[k].map((r) => r.x.toFixed(2)).join(",")}]`} |           Values:{" "} | ||||||
|  |           {errorPoints | ||||||
|  |             .map((r) => <NumberShower number={r.x} />) | ||||||
|  |             .reduce((a, b) => ( | ||||||
|  |               <> | ||||||
|  |                 {a}, {b} | ||||||
|  |               </> | ||||||
|  |             ))} | ||||||
|         </ErrorBox> |         </ErrorBox> | ||||||
|       ))} |       ))} | ||||||
|     </> |     </> | ||||||
|  |  | ||||||
|  | @ -6,14 +6,16 @@ import { | ||||||
|   errorValueToString, |   errorValueToString, | ||||||
|   squiggleExpression, |   squiggleExpression, | ||||||
|   bindings, |   bindings, | ||||||
|   samplingParams, |   environment, | ||||||
|   jsImports, |   jsImports, | ||||||
|   defaultImports, |   defaultImports, | ||||||
|   defaultBindings, |   defaultBindings, | ||||||
|  |   defaultEnvironment, | ||||||
| } from "@quri/squiggle-lang"; | } from "@quri/squiggle-lang"; | ||||||
| import { NumberShower } from "./NumberShower"; | import { NumberShower } from "./NumberShower"; | ||||||
| import { DistributionChart } from "./DistributionChart"; | import { DistributionChart } from "./DistributionChart"; | ||||||
| import { ErrorBox } from "./ErrorBox"; | import { ErrorBox } from "./ErrorBox"; | ||||||
|  | import { FunctionChart, FunctionChartSettings } from "./FunctionChart"; | ||||||
| 
 | 
 | ||||||
| const variableBox = { | const variableBox = { | ||||||
|   Component: styled.div` |   Component: styled.div` | ||||||
|  | @ -36,7 +38,7 @@ const variableBox = { | ||||||
| interface VariableBoxProps { | interface VariableBoxProps { | ||||||
|   heading: string; |   heading: string; | ||||||
|   children: React.ReactNode; |   children: React.ReactNode; | ||||||
|   showTypes?: boolean; |   showTypes: boolean; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export const VariableBox: React.FC<VariableBoxProps> = ({ | export const VariableBox: React.FC<VariableBoxProps> = ({ | ||||||
|  | @ -68,9 +70,13 @@ export interface SquiggleItemProps { | ||||||
|   /** Whether to show a summary of statistics for distributions */ |   /** Whether to show a summary of statistics for distributions */ | ||||||
|   showSummary: boolean; |   showSummary: boolean; | ||||||
|   /** Whether to show type information */ |   /** Whether to show type information */ | ||||||
|   showTypes?: boolean; |   showTypes: boolean; | ||||||
|   /** Whether to show users graph controls (scale etc) */ |   /** Whether to show users graph controls (scale etc) */ | ||||||
|   showControls?: boolean; |   showControls: boolean; | ||||||
|  |   /** Settings for displaying functions */ | ||||||
|  |   chartSettings: FunctionChartSettings; | ||||||
|  |   /** Environment for further function executions */ | ||||||
|  |   environment: environment; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const SquiggleItem: React.FC<SquiggleItemProps> = ({ | const SquiggleItem: React.FC<SquiggleItemProps> = ({ | ||||||
|  | @ -80,6 +86,8 @@ const SquiggleItem: React.FC<SquiggleItemProps> = ({ | ||||||
|   showSummary, |   showSummary, | ||||||
|   showTypes = false, |   showTypes = false, | ||||||
|   showControls = false, |   showControls = false, | ||||||
|  |   chartSettings, | ||||||
|  |   environment, | ||||||
| }: SquiggleItemProps) => { | }: SquiggleItemProps) => { | ||||||
|   switch (expression.tag) { |   switch (expression.tag) { | ||||||
|     case "number": |     case "number": | ||||||
|  | @ -147,6 +155,8 @@ const SquiggleItem: React.FC<SquiggleItemProps> = ({ | ||||||
|               height={50} |               height={50} | ||||||
|               showTypes={showTypes} |               showTypes={showTypes} | ||||||
|               showControls={showControls} |               showControls={showControls} | ||||||
|  |               chartSettings={chartSettings} | ||||||
|  |               environment={environment} | ||||||
|               showSummary={showSummary} |               showSummary={showSummary} | ||||||
|             /> |             /> | ||||||
|           ))} |           ))} | ||||||
|  | @ -165,6 +175,8 @@ const SquiggleItem: React.FC<SquiggleItemProps> = ({ | ||||||
|                 showTypes={showTypes} |                 showTypes={showTypes} | ||||||
|                 showSummary={showSummary} |                 showSummary={showSummary} | ||||||
|                 showControls={showControls} |                 showControls={showControls} | ||||||
|  |                 chartSettings={chartSettings} | ||||||
|  |                 environment={environment} | ||||||
|               /> |               /> | ||||||
|             </> |             </> | ||||||
|           ))} |           ))} | ||||||
|  | @ -178,9 +190,11 @@ const SquiggleItem: React.FC<SquiggleItemProps> = ({ | ||||||
|       ); |       ); | ||||||
|     case "lambda": |     case "lambda": | ||||||
|       return ( |       return ( | ||||||
|         <ErrorBox heading="No Viewer"> |         <FunctionChart | ||||||
|           There is no viewer currently available for function types. |           fn={expression.value} | ||||||
|         </ErrorBox> |           chartSettings={chartSettings} | ||||||
|  |           environment={environment} | ||||||
|  |         /> | ||||||
|       ); |       ); | ||||||
|   } |   } | ||||||
| }; | }; | ||||||
|  | @ -191,15 +205,9 @@ export interface SquiggleChartProps { | ||||||
|   /** If the output requires monte carlo sampling, the amount of samples */ |   /** If the output requires monte carlo sampling, the amount of samples */ | ||||||
|   sampleCount?: number; |   sampleCount?: number; | ||||||
|   /** The amount of points returned to draw the distribution */ |   /** The amount of points returned to draw the distribution */ | ||||||
|   outputXYPoints?: number; |   environment?: environment; | ||||||
|   kernelWidth?: number; |   /** If the result is a function, where the function starts, ends and the amount of stops */ | ||||||
|   pointDistLength?: number; |   chartSettings?: FunctionChartSettings; | ||||||
|   /** If the result is a function, where the function starts */ |  | ||||||
|   diagramStart?: number; |  | ||||||
|   /** If the result is a function, where the function ends */ |  | ||||||
|   diagramStop?: number; |  | ||||||
|   /** If the result is a function, how many points along the function it samples */ |  | ||||||
|   diagramCount?: number; |  | ||||||
|   /** When the environment changes */ |   /** When the environment changes */ | ||||||
|   onChange?(expr: squiggleExpression): void; |   onChange?(expr: squiggleExpression): void; | ||||||
|   /** CSS width of the element */ |   /** CSS width of the element */ | ||||||
|  | @ -223,10 +231,10 @@ const ChartWrapper = styled.div` | ||||||
|     "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; |     "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; | ||||||
| `;
 | `;
 | ||||||
| 
 | 
 | ||||||
|  | let defaultChartSettings = { start: 0, stop: 10, count: 100 }; | ||||||
| export const SquiggleChart: React.FC<SquiggleChartProps> = ({ | export const SquiggleChart: React.FC<SquiggleChartProps> = ({ | ||||||
|   squiggleString = "", |   squiggleString = "", | ||||||
|   sampleCount = 1000, |   environment, | ||||||
|   outputXYPoints = 1000, |  | ||||||
|   onChange = () => {}, |   onChange = () => {}, | ||||||
|   height = 60, |   height = 60, | ||||||
|   bindings = defaultBindings, |   bindings = defaultBindings, | ||||||
|  | @ -235,17 +243,10 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = ({ | ||||||
|   width, |   width, | ||||||
|   showTypes = false, |   showTypes = false, | ||||||
|   showControls = false, |   showControls = false, | ||||||
|  |   chartSettings = defaultChartSettings, | ||||||
| }: SquiggleChartProps) => { | }: SquiggleChartProps) => { | ||||||
|   let samplingInputs: samplingParams = { |   let expressionResult = run(squiggleString, bindings, environment, jsImports); | ||||||
|     sampleCount: sampleCount, |   let e = environment ? environment : defaultEnvironment; | ||||||
|     xyPointLength: outputXYPoints, |  | ||||||
|   }; |  | ||||||
|   let expressionResult = run( |  | ||||||
|     squiggleString, |  | ||||||
|     bindings, |  | ||||||
|     samplingInputs, |  | ||||||
|     jsImports |  | ||||||
|   ); |  | ||||||
|   let internal: JSX.Element; |   let internal: JSX.Element; | ||||||
|   if (expressionResult.tag === "Ok") { |   if (expressionResult.tag === "Ok") { | ||||||
|     let expression = expressionResult.value; |     let expression = expressionResult.value; | ||||||
|  | @ -258,6 +259,8 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = ({ | ||||||
|         showSummary={showSummary} |         showSummary={showSummary} | ||||||
|         showTypes={showTypes} |         showTypes={showTypes} | ||||||
|         showControls={showControls} |         showControls={showControls} | ||||||
|  |         chartSettings={chartSettings} | ||||||
|  |         environment={e} | ||||||
|       /> |       /> | ||||||
|     ); |     ); | ||||||
|   } else { |   } else { | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ import { CodeEditor } from "./CodeEditor"; | ||||||
| import styled from "styled-components"; | import styled from "styled-components"; | ||||||
| import type { | import type { | ||||||
|   squiggleExpression, |   squiggleExpression, | ||||||
|   samplingParams, |   environment, | ||||||
|   bindings, |   bindings, | ||||||
|   jsImports, |   jsImports, | ||||||
| } from "@quri/squiggle-lang"; | } from "@quri/squiggle-lang"; | ||||||
|  | @ -21,11 +21,7 @@ export interface SquiggleEditorProps { | ||||||
|   /** The input string for squiggle */ |   /** The input string for squiggle */ | ||||||
|   initialSquiggleString?: string; |   initialSquiggleString?: string; | ||||||
|   /** If the output requires monte carlo sampling, the amount of samples */ |   /** If the output requires monte carlo sampling, the amount of samples */ | ||||||
|   sampleCount?: number; |   environment?: environment; | ||||||
|   /** The amount of points returned to draw the distribution */ |  | ||||||
|   outputXYPoints?: number; |  | ||||||
|   kernelWidth?: number; |  | ||||||
|   pointDistLength?: number; |  | ||||||
|   /** If the result is a function, where the function starts */ |   /** If the result is a function, where the function starts */ | ||||||
|   diagramStart?: number; |   diagramStart?: number; | ||||||
|   /** If the result is a function, where the function ends */ |   /** If the result is a function, where the function ends */ | ||||||
|  | @ -57,13 +53,10 @@ const Input = styled.div` | ||||||
| export let SquiggleEditor: React.FC<SquiggleEditorProps> = ({ | export let SquiggleEditor: React.FC<SquiggleEditorProps> = ({ | ||||||
|   initialSquiggleString = "", |   initialSquiggleString = "", | ||||||
|   width, |   width, | ||||||
|   sampleCount, |   environment, | ||||||
|   outputXYPoints, |   diagramStart = 0, | ||||||
|   kernelWidth, |   diagramStop = 10, | ||||||
|   pointDistLength, |   diagramCount = 100, | ||||||
|   diagramStart, |  | ||||||
|   diagramStop, |  | ||||||
|   diagramCount, |  | ||||||
|   onChange, |   onChange, | ||||||
|   bindings = defaultBindings, |   bindings = defaultBindings, | ||||||
|   jsImports = defaultImports, |   jsImports = defaultImports, | ||||||
|  | @ -72,6 +65,11 @@ export let SquiggleEditor: React.FC<SquiggleEditorProps> = ({ | ||||||
|   showSummary = false, |   showSummary = false, | ||||||
| }: SquiggleEditorProps) => { | }: SquiggleEditorProps) => { | ||||||
|   let [expression, setExpression] = React.useState(initialSquiggleString); |   let [expression, setExpression] = React.useState(initialSquiggleString); | ||||||
|  |   let chartSettings = { | ||||||
|  |     start: diagramStart, | ||||||
|  |     stop: diagramStop, | ||||||
|  |     count: diagramCount, | ||||||
|  |   }; | ||||||
|   return ( |   return ( | ||||||
|     <div> |     <div> | ||||||
|       <Input> |       <Input> | ||||||
|  | @ -85,14 +83,9 @@ export let SquiggleEditor: React.FC<SquiggleEditorProps> = ({ | ||||||
|       </Input> |       </Input> | ||||||
|       <SquiggleChart |       <SquiggleChart | ||||||
|         width={width} |         width={width} | ||||||
|  |         environment={environment} | ||||||
|         squiggleString={expression} |         squiggleString={expression} | ||||||
|         sampleCount={sampleCount} |         chartSettings={chartSettings} | ||||||
|         outputXYPoints={outputXYPoints} |  | ||||||
|         kernelWidth={kernelWidth} |  | ||||||
|         pointDistLength={pointDistLength} |  | ||||||
|         diagramStart={diagramStart} |  | ||||||
|         diagramStop={diagramStop} |  | ||||||
|         diagramCount={diagramCount} |  | ||||||
|         onChange={onChange} |         onChange={onChange} | ||||||
|         bindings={bindings} |         bindings={bindings} | ||||||
|         jsImports={jsImports} |         jsImports={jsImports} | ||||||
|  | @ -140,11 +133,7 @@ export interface SquigglePartialProps { | ||||||
|   /** The input string for squiggle */ |   /** The input string for squiggle */ | ||||||
|   initialSquiggleString?: string; |   initialSquiggleString?: string; | ||||||
|   /** If the output requires monte carlo sampling, the amount of samples */ |   /** If the output requires monte carlo sampling, the amount of samples */ | ||||||
|   sampleCount?: number; |   environment?: environment; | ||||||
|   /** The amount of points returned to draw the distribution */ |  | ||||||
|   outputXYPoints?: number; |  | ||||||
|   kernelWidth?: number; |  | ||||||
|   pointDistLength?: number; |  | ||||||
|   /** If the result is a function, where the function starts */ |   /** If the result is a function, where the function starts */ | ||||||
|   diagramStart?: number; |   diagramStart?: number; | ||||||
|   /** If the result is a function, where the function ends */ |   /** If the result is a function, where the function ends */ | ||||||
|  | @ -165,14 +154,9 @@ export let SquigglePartial: React.FC<SquigglePartialProps> = ({ | ||||||
|   initialSquiggleString = "", |   initialSquiggleString = "", | ||||||
|   onChange, |   onChange, | ||||||
|   bindings = defaultBindings, |   bindings = defaultBindings, | ||||||
|   sampleCount = 1000, |   environment, | ||||||
|   outputXYPoints = 1000, |  | ||||||
|   jsImports = defaultImports, |   jsImports = defaultImports, | ||||||
| }: SquigglePartialProps) => { | }: SquigglePartialProps) => { | ||||||
|   let samplingInputs: samplingParams = { |  | ||||||
|     sampleCount: sampleCount, |  | ||||||
|     xyPointLength: outputXYPoints, |  | ||||||
|   }; |  | ||||||
|   let [expression, setExpression] = React.useState(initialSquiggleString); |   let [expression, setExpression] = React.useState(initialSquiggleString); | ||||||
|   let [error, setError] = React.useState<string | null>(null); |   let [error, setError] = React.useState<string | null>(null); | ||||||
| 
 | 
 | ||||||
|  | @ -180,7 +164,7 @@ export let SquigglePartial: React.FC<SquigglePartialProps> = ({ | ||||||
|     let squiggleResult = runPartial( |     let squiggleResult = runPartial( | ||||||
|       expression, |       expression, | ||||||
|       bindings, |       bindings, | ||||||
|       samplingInputs, |       environment, | ||||||
|       jsImports |       jsImports | ||||||
|     ); |     ); | ||||||
|     if (squiggleResult.tag == "Ok") { |     if (squiggleResult.tag == "Ok") { | ||||||
|  |  | ||||||
|  | @ -4,6 +4,11 @@ import ReactDOM from "react-dom"; | ||||||
| import { SquiggleChart } from "./SquiggleChart"; | import { SquiggleChart } from "./SquiggleChart"; | ||||||
| import CodeEditor from "./CodeEditor"; | import CodeEditor from "./CodeEditor"; | ||||||
| import styled from "styled-components"; | import styled from "styled-components"; | ||||||
|  | import { | ||||||
|  |   defaultBindings, | ||||||
|  |   environment, | ||||||
|  |   defaultImports, | ||||||
|  | } from "@quri/squiggle-lang"; | ||||||
| 
 | 
 | ||||||
| interface FieldFloatProps { | interface FieldFloatProps { | ||||||
|   label: string; |   label: string; | ||||||
|  | @ -96,6 +101,15 @@ let SquigglePlayground: FC<PlaygroundProps> = ({ | ||||||
|   let [diagramStart, setDiagramStart] = useState(0); |   let [diagramStart, setDiagramStart] = useState(0); | ||||||
|   let [diagramStop, setDiagramStop] = useState(10); |   let [diagramStop, setDiagramStop] = useState(10); | ||||||
|   let [diagramCount, setDiagramCount] = useState(20); |   let [diagramCount, setDiagramCount] = useState(20); | ||||||
|  |   let chartSettings = { | ||||||
|  |     start: diagramStart, | ||||||
|  |     stop: diagramStop, | ||||||
|  |     count: diagramCount, | ||||||
|  |   }; | ||||||
|  |   let env: environment = { | ||||||
|  |     sampleCount: sampleCount, | ||||||
|  |     xyPointLength: outputXYPoints, | ||||||
|  |   }; | ||||||
|   return ( |   return ( | ||||||
|     <ShowBox height={height}> |     <ShowBox height={height}> | ||||||
|       <Row> |       <Row> | ||||||
|  | @ -112,15 +126,13 @@ let SquigglePlayground: FC<PlaygroundProps> = ({ | ||||||
|           <Display maxHeight={height - 3}> |           <Display maxHeight={height - 3}> | ||||||
|             <SquiggleChart |             <SquiggleChart | ||||||
|               squiggleString={squiggleString} |               squiggleString={squiggleString} | ||||||
|               sampleCount={sampleCount} |               environment={env} | ||||||
|               outputXYPoints={outputXYPoints} |               chartSettings={chartSettings} | ||||||
|               diagramStart={diagramStart} |  | ||||||
|               diagramStop={diagramStop} |  | ||||||
|               diagramCount={diagramCount} |  | ||||||
|               pointDistLength={pointDistLength} |  | ||||||
|               height={150} |               height={150} | ||||||
|               showTypes={showTypes} |               showTypes={showTypes} | ||||||
|               showControls={showControls} |               showControls={showControls} | ||||||
|  |               bindings={defaultBindings} | ||||||
|  |               jsImports={defaultImports} | ||||||
|               showSummary={showSummary} |               showSummary={showSummary} | ||||||
|             /> |             /> | ||||||
|           </Display> |           </Display> | ||||||
|  |  | ||||||
|  | @ -1,6 +1,5 @@ | ||||||
| import * as _ from "lodash"; | import * as _ from "lodash"; | ||||||
| import { | import { | ||||||
|   samplingParams, |  | ||||||
|   environment, |   environment, | ||||||
|   defaultEnvironment, |   defaultEnvironment, | ||||||
|   evaluatePartialUsingExternalBindings, |   evaluatePartialUsingExternalBindings, | ||||||
|  | @ -8,6 +7,7 @@ import { | ||||||
|   externalBindings, |   externalBindings, | ||||||
|   expressionValue, |   expressionValue, | ||||||
|   errorValue, |   errorValue, | ||||||
|  |   foreignFunctionInterface, | ||||||
| } from "../rescript/TypescriptInterface.gen"; | } from "../rescript/TypescriptInterface.gen"; | ||||||
| export { | export { | ||||||
|   makeSampleSetDist, |   makeSampleSetDist, | ||||||
|  | @ -15,25 +15,31 @@ export { | ||||||
|   distributionErrorToString, |   distributionErrorToString, | ||||||
|   distributionError, |   distributionError, | ||||||
| } from "../rescript/TypescriptInterface.gen"; | } from "../rescript/TypescriptInterface.gen"; | ||||||
| export type { | export type { errorValue, externalBindings as bindings, jsImports }; | ||||||
|   samplingParams, |  | ||||||
|   errorValue, |  | ||||||
|   externalBindings as bindings, |  | ||||||
|   jsImports, |  | ||||||
| }; |  | ||||||
| import { | import { | ||||||
|   jsValueToBinding, |   jsValueToBinding, | ||||||
|  |   jsValueToExpressionValue, | ||||||
|   jsValue, |   jsValue, | ||||||
|   rescriptExport, |   rescriptExport, | ||||||
|   squiggleExpression, |   squiggleExpression, | ||||||
|   convertRawToTypescript, |   convertRawToTypescript, | ||||||
|  |   lambdaValue, | ||||||
| } from "./rescript_interop"; | } from "./rescript_interop"; | ||||||
| import { result, resultMap, tag, tagged } from "./types"; | import { result, resultMap, tag, tagged } from "./types"; | ||||||
| import { Distribution, shape } from "./distribution"; | import { Distribution, shape } from "./distribution"; | ||||||
| 
 | 
 | ||||||
| export { Distribution, squiggleExpression, result, resultMap, shape }; | export { | ||||||
|  |   Distribution, | ||||||
|  |   squiggleExpression, | ||||||
|  |   result, | ||||||
|  |   resultMap, | ||||||
|  |   shape, | ||||||
|  |   lambdaValue, | ||||||
|  |   environment, | ||||||
|  |   defaultEnvironment, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| export let defaultSamplingInputs: samplingParams = { | export let defaultSamplingInputs: environment = { | ||||||
|   sampleCount: 10000, |   sampleCount: 10000, | ||||||
|   xyPointLength: 10000, |   xyPointLength: 10000, | ||||||
| }; | }; | ||||||
|  | @ -72,6 +78,20 @@ export function runPartial( | ||||||
|   ); |   ); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | export function runForeign( | ||||||
|  |   fn: lambdaValue, | ||||||
|  |   args: jsValue[], | ||||||
|  |   environment?: environment | ||||||
|  | ): result<squiggleExpression, errorValue> { | ||||||
|  |   let e = environment ? environment : defaultEnvironment; | ||||||
|  |   let res: result<expressionValue, errorValue> = foreignFunctionInterface( | ||||||
|  |     fn, | ||||||
|  |     args.map(jsValueToExpressionValue), | ||||||
|  |     e | ||||||
|  |   ); | ||||||
|  |   return resultMap(res, (x) => createTsExport(x, e)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| function mergeImportsWithBindings( | function mergeImportsWithBindings( | ||||||
|   bindings: externalBindings, |   bindings: externalBindings, | ||||||
|   imports: jsImports |   imports: jsImports | ||||||
|  |  | ||||||
|  | @ -1,5 +1,6 @@ | ||||||
| import * as _ from "lodash"; | import * as _ from "lodash"; | ||||||
| import { | import { | ||||||
|  |   expressionValue, | ||||||
|   mixedShape, |   mixedShape, | ||||||
|   sampleSetDist, |   sampleSetDist, | ||||||
|   genericDist, |   genericDist, | ||||||
|  | @ -87,6 +88,8 @@ export type squiggleExpression = | ||||||
|   | tagged<"number", number> |   | tagged<"number", number> | ||||||
|   | tagged<"record", { [key: string]: squiggleExpression }>; |   | tagged<"record", { [key: string]: squiggleExpression }>; | ||||||
| 
 | 
 | ||||||
|  | export { lambdaValue }; | ||||||
|  | 
 | ||||||
| export function convertRawToTypescript( | export function convertRawToTypescript( | ||||||
|   result: rescriptExport, |   result: rescriptExport, | ||||||
|   environment: environment |   environment: environment | ||||||
|  | @ -168,3 +171,21 @@ export function jsValueToBinding(value: jsValue): rescriptExport { | ||||||
|     return { TAG: 7, _0: _.mapValues(value, jsValueToBinding) }; |     return { TAG: 7, _0: _.mapValues(value, jsValueToBinding) }; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | export function jsValueToExpressionValue(value: jsValue): expressionValue { | ||||||
|  |   if (typeof value === "boolean") { | ||||||
|  |     return { tag: "EvBool", value: value as boolean }; | ||||||
|  |   } else if (typeof value === "string") { | ||||||
|  |     return { tag: "EvString", value: value as string }; | ||||||
|  |   } else if (typeof value === "number") { | ||||||
|  |     return { tag: "EvNumber", value: value as number }; | ||||||
|  |   } else if (Array.isArray(value)) { | ||||||
|  |     return { tag: "EvArray", value: value.map(jsValueToExpressionValue) }; | ||||||
|  |   } else { | ||||||
|  |     // Record
 | ||||||
|  |     return { | ||||||
|  |       tag: "EvRecord", | ||||||
|  |       value: _.mapValues(value, jsValueToExpressionValue), | ||||||
|  |     }; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -84,3 +84,6 @@ type environment = ReducerInterface_ExpressionValue.environment | ||||||
| 
 | 
 | ||||||
| @genType | @genType | ||||||
| let defaultEnvironment = ReducerInterface_ExpressionValue.defaultEnvironment | let defaultEnvironment = ReducerInterface_ExpressionValue.defaultEnvironment | ||||||
|  | 
 | ||||||
|  | @genType | ||||||
|  | let foreignFunctionInterface = Reducer.foreignFunctionInterface | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user