From 140498783af0b6c4c47df9a08ec485eb81d84110 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Wed, 25 Oct 2023 16:52:19 -0700 Subject: [PATCH] Added sampleCount field to Calculator.make --- .changeset/hip-carrots-laugh.md | 6 ++++ .../src/components/Calculator/asyncActions.ts | 28 ++++++++++++++----- .../src/components/Calculator/index.tsx | 21 ++++++++++++++ .../src/components/PlaygroundSettings.tsx | 3 +- .../SquiggleViewer/ExpressionViewer.tsx | 22 ++++++++------- packages/components/src/lib/constants.ts | 2 ++ .../stories/SquigglePlayground.stories.tsx | 1 + packages/squiggle-lang/src/fr/calculator.ts | 5 +++- .../src/public/SqValue/SqCalculator.ts | 12 +++++++- packages/squiggle-lang/src/value/index.ts | 1 + .../website/src/pages/docs/Api/Calculator.mdx | 2 ++ 11 files changed, 83 insertions(+), 20 deletions(-) create mode 100644 .changeset/hip-carrots-laugh.md create mode 100644 packages/components/src/lib/constants.ts diff --git a/.changeset/hip-carrots-laugh.md b/.changeset/hip-carrots-laugh.md new file mode 100644 index 0000000000..53f9a84cf4 --- /dev/null +++ b/.changeset/hip-carrots-laugh.md @@ -0,0 +1,6 @@ +--- +"@quri/squiggle-lang": patch +"@quri/squiggle-components": patch +--- + +Added sampleCount field to Calculator.make diff --git a/packages/components/src/components/Calculator/asyncActions.ts b/packages/components/src/components/Calculator/asyncActions.ts index d7c75de9e4..816c18f393 100644 --- a/packages/components/src/components/Calculator/asyncActions.ts +++ b/packages/components/src/components/Calculator/asyncActions.ts @@ -17,14 +17,21 @@ import { allFieldValuesAreValid, } from "./calculatorReducer.js"; +const getEnvironment = ( + modelEnvironment: Env, + calculator: SqCalculator +): Env => ({ + sampleCount: calculator.sampleCount || modelEnvironment.sampleCount, + xyPointLength: modelEnvironment.xyPointLength, +}); + const runSquiggleCode = async ( code: string, - environment: Env + environment: Env, + calculator: SqCalculator ): Promise> => { const project = SqProject.create(); - if (environment) { - project.setEnvironment(environment); - } + project.setEnvironment(getEnvironment(environment, calculator)); const sourceId = "calculator"; project.setSource(sourceId, code); await project.run(sourceId); @@ -55,7 +62,10 @@ export const updateFnValue = ({ throw new Error("Invalid result encountered."); } }); - finalResult = calculator.run(results, environment); + finalResult = calculator.run( + results, + getEnvironment(environment, calculator) + ); } else { finalResult = undefined; } @@ -83,7 +93,11 @@ export async function processAllFieldCodes({ let _state = state; for (const name of state.fieldNames) { const field = state.fields[name]; - const valueResult = await runSquiggleCode(field.code, environment); + const valueResult = await runSquiggleCode( + field.code, + environment, + calculator + ); const _action: CalculatorAction = { type: "SET_FIELD_VALUE", payload: { name, value: valueResult, path }, @@ -119,7 +133,7 @@ export async function updateAndProcessFieldCode({ dispatch(setCodeAction); let _state = calculatorReducer(state, setCodeAction); - const valueResult = await runSquiggleCode(code, environment); + const valueResult = await runSquiggleCode(code, environment, calculator); const setValueAction: CalculatorAction = { type: "SET_FIELD_VALUE", payload: { path, name: name, value: valueResult }, diff --git a/packages/components/src/components/Calculator/index.tsx b/packages/components/src/components/Calculator/index.tsx index 4fb25e0b81..586374b1c2 100644 --- a/packages/components/src/components/Calculator/index.tsx +++ b/packages/components/src/components/Calculator/index.tsx @@ -21,6 +21,8 @@ import { initialCalculatorState, } from "./calculatorReducer.js"; import { CalculatorUI } from "./calculatorUI.js"; +import { ErrorAlert } from "../Alert.js"; +import { SAMPLE_COUNT_MAX, SAMPLE_COUNT_MIN } from "../../lib/constants.js"; type Props = { value: SqCalculator; @@ -163,3 +165,22 @@ export const Calculator: FC = ({ ) ); }; + +export const ShowErrorIfInvalid: React.FC<{ + calculator: SqCalculator; + children: React.ReactNode; +}> = ({ calculator, children }) => { + const { sampleCount } = calculator; + + const sampleCountIsInvalid = + sampleCount && + (sampleCount < SAMPLE_COUNT_MIN || sampleCount > SAMPLE_COUNT_MAX); + + return sampleCountIsInvalid ? ( + + ) : ( + children + ); +}; diff --git a/packages/components/src/components/PlaygroundSettings.tsx b/packages/components/src/components/PlaygroundSettings.tsx index 3f9a2968fa..fb67244071 100644 --- a/packages/components/src/components/PlaygroundSettings.tsx +++ b/packages/components/src/components/PlaygroundSettings.tsx @@ -12,9 +12,10 @@ import { CheckboxFormField, NumberFormField, RadioFormField } from "@quri/ui"; import { functionChartDefaults } from "./FunctionChart/utils.js"; import { FormComment } from "./ui/FormComment.js"; import { FormSection } from "./ui/FormSection.js"; +import { SAMPLE_COUNT_MAX, SAMPLE_COUNT_MIN } from "../lib/constants.js"; export const environmentSchema = z.object({ - sampleCount: z.number().int().gte(10).lte(1000000), + sampleCount: z.number().int().gte(SAMPLE_COUNT_MIN).lte(SAMPLE_COUNT_MAX), xyPointLength: z.number().int().gte(10).lte(10000), }); diff --git a/packages/components/src/components/SquiggleViewer/ExpressionViewer.tsx b/packages/components/src/components/SquiggleViewer/ExpressionViewer.tsx index f12a9ccdcc..b6cf17cb95 100644 --- a/packages/components/src/components/SquiggleViewer/ExpressionViewer.tsx +++ b/packages/components/src/components/SquiggleViewer/ExpressionViewer.tsx @@ -32,7 +32,7 @@ import { TableChart } from "../TableChart/index.js"; import { DistPreview } from "../DistributionsChart/DistPreview.js"; import { TableCellsIcon } from "@quri/ui"; import ReactMarkdown from "react-markdown"; -import { Calculator } from "../Calculator/index.js"; +import { Calculator, ShowErrorIfInvalid } from "../Calculator/index.js"; // We use an extra left margin for some elements to align them with parent variable name const leftMargin = "ml-1.5"; @@ -143,15 +143,17 @@ export const getBoxProps = ( const calculator: SqCalculator = value.value; return { children: (settings) => ( - - getBoxProps(value).children(settings) - } - /> + + + getBoxProps(value).children(settings) + } + /> + ), }; } diff --git a/packages/components/src/lib/constants.ts b/packages/components/src/lib/constants.ts new file mode 100644 index 0000000000..512c7a11c2 --- /dev/null +++ b/packages/components/src/lib/constants.ts @@ -0,0 +1,2 @@ +export const SAMPLE_COUNT_MAX = 1000000; +export const SAMPLE_COUNT_MIN = 10; diff --git a/packages/components/src/stories/SquigglePlayground.stories.tsx b/packages/components/src/stories/SquigglePlayground.stories.tsx index 0b490ea773..8fc9fc46ac 100644 --- a/packages/components/src/stories/SquigglePlayground.stories.tsx +++ b/packages/components/src/stories/SquigglePlayground.stories.tsx @@ -242,6 +242,7 @@ Calculator.make( { fn: f, description: a, + sampleCount: 10000, fields: [ { name: "Variable 1", diff --git a/packages/squiggle-lang/src/fr/calculator.ts b/packages/squiggle-lang/src/fr/calculator.ts index db7e60e431..9aa5024e6e 100644 --- a/packages/squiggle-lang/src/fr/calculator.ts +++ b/packages/squiggle-lang/src/fr/calculator.ts @@ -6,6 +6,7 @@ import { frString, frOptional, frNumberOrString, + frNumber, } from "../library/registry/frTypes.js"; import { FnFactory } from "../library/registry/helpers.js"; import { vCalculator } from "../value/index.js"; @@ -37,6 +38,7 @@ export const library = [ ["fn", frLambda], ["title", frOptional(frString)], ["description", frOptional(frString)], + ["sampleCount", frOptional(frNumber)], [ "fields", frArray( @@ -49,11 +51,12 @@ export const library = [ ] ), ], - ([{ fn, title, description, fields }]) => { + ([{ fn, title, description, sampleCount, fields }]) => { const calc = vCalculator({ fn, title: title || undefined, description: description || undefined, + sampleCount: sampleCount || undefined, fields: fields.map((vars) => ({ name: vars.name, default: convertFieldDefault(vars.default), diff --git a/packages/squiggle-lang/src/public/SqValue/SqCalculator.ts b/packages/squiggle-lang/src/public/SqValue/SqCalculator.ts index 77b200d491..074c955f74 100644 --- a/packages/squiggle-lang/src/public/SqValue/SqCalculator.ts +++ b/packages/squiggle-lang/src/public/SqValue/SqCalculator.ts @@ -38,13 +38,23 @@ export class SqCalculator { return this._value.description; } + get sampleCount(): number | undefined { + return this._value.sampleCount; + } + // This function is used to determine if a calculator has changed. // It's obviously not perfect - it doesn't capture changes within the calculator function, but this would be much more complicated. get hashString(): string { const rowData = JSON.stringify(this._value.fields); const paramData = this._value.fn.toString() || ""; - return rowData + paramData + this._value.description; + return ( + rowData + + paramData + + this._value.description + + this._value.title + + this._value.sampleCount + ); } get fields(): { diff --git a/packages/squiggle-lang/src/value/index.ts b/packages/squiggle-lang/src/value/index.ts index 4b52523905..c7656f71f2 100644 --- a/packages/squiggle-lang/src/value/index.ts +++ b/packages/squiggle-lang/src/value/index.ts @@ -439,6 +439,7 @@ export type Calculator = { fields: { name: string; default: string; description?: string }[]; description?: string; title?: string; + sampleCount?: number; }; class VCalculator extends BaseValue { diff --git a/packages/website/src/pages/docs/Api/Calculator.mdx b/packages/website/src/pages/docs/Api/Calculator.mdx index c464066eea..7b2e1d519b 100644 --- a/packages/website/src/pages/docs/Api/Calculator.mdx +++ b/packages/website/src/pages/docs/Api/Calculator.mdx @@ -17,6 +17,7 @@ Calculator.make: ({ fn: ...arguments => any, title?: string, description?: string, + sampleCount?: number, fields: list<{ name: string, default?: string | number, @@ -35,6 +36,7 @@ Examples: fn: {|a, b|a + b}, title: "Sum()" description: "This takes in two arguments, and outputs the sum of those two arguments.", + sampleCount: 1000, fields: [ { name: "First Param",