diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 000d73ec..9c9c86c3 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -9,3 +9,5 @@ updates: directory: "/" # Location of package manifests schedule: interval: "daily" + commit-message: + prefix: "⬆️" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1160c2db..acd96119 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,6 +9,7 @@ on: branches: - master - develop + - reducer-dev jobs: pre_check: @@ -71,12 +72,16 @@ jobs: run: cd ../../ && yarn - name: Build rescript codebase run: yarn build - - name: Run tests - run: yarn test + - name: Run rescript tests + run: yarn test:rescript + - name: Run typescript tests + run: yarn test:ts - name: Run webpack run: yarn bundle - - name: Upload coverage report - run: yarn coverage:ci + - name: Upload rescript coverage report + run: yarn coverage:rescript:ci + - name: Upload typescript coverage report + run: yarn coverage:ts:ci components-lint: name: Components lint diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index c7750177..2aef3ae3 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -18,13 +18,6 @@ on: - production - staging - develop - pull_request: - # The branches below must be a subset of the branches above - branches: - - master - - production - - staging - - develop schedule: - cron: "42 19 * * 0" diff --git a/.gitignore b/.gitignore index f5cb3583..5b48f91c 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ yarn-error.log .DS_Store **/.sync.ffs_db .direnv +.log diff --git a/.parcelrc b/.parcelrc deleted file mode 100644 index f5a5c2d5..00000000 --- a/.parcelrc +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": "@parcel/config-default", - "transformers": { - "*.res": ["@parcel/transformer-raw"] - } -} diff --git a/.prettierignore b/.prettierignore index 175742ce..54df33a4 100644 --- a/.prettierignore +++ b/.prettierignore @@ -7,3 +7,7 @@ node_modules packages/*/node_modules packages/website/.docusaurus packages/squiggle-lang/lib +packages/squiggle-lang/.nyc_output/ +packages/squiggle-lang/coverage/ +packages/squiggle-lang/.cache/ +packages/website/build/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f90fa60b..8a3b026a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,7 +8,7 @@ Squiggle is currently pre-alpha. # Quick links -- [Roadmap to the alpha](https://github.com/QURIresearch/squiggle/projects/2) +- [Roadmap to the alpha](https://github.com/orgs/quantified-uncertainty/projects/1) - The team presently communicates via the **EA Forecasting and Epistemics** slack (channels `#squiggle` and `#squiggle-ops`), you can track down an invite by reaching out to Ozzie Gooen - [Squiggle documentation](https://www.squiggle-language.com/docs/Language) - [Rescript documentation](https://rescript-lang.org/docs/manual/latest/introduction) @@ -20,10 +20,9 @@ Anyone (with a github account) can file an issue at any time. Please allow Quinn # Project structure -Squiggle is a **monorepo** with four **packages**. +Squiggle is a **monorepo** with three **packages**. - **components** is where we improve reactive interfacing with Squiggle -- **playground** is the site `playground.squiggle-language.com` - **squiggle-lang** is where the magic happens: probability distributions, the interpreter, etc. - **website** is the site `squiggle-language.com` @@ -41,13 +40,17 @@ We aspire for `ci.yaml` and `README.md`s to be in one-to-one correspondence. ## If you're on NixOS -You'll need to run a command like this in order to get `yarn build` to run, especially in `packages/squiggle-lang`. +You can't run `yarn` outside of a FHS shell. Additionally, you need to `patchelf` some things. A script does everything for you. ```sh -patchelf --set-interpreter $(patchelf --print-interpreter $(which mkdir)) ./node_modules/gentype/gentype.exe +./nixos.sh ``` -See [here](https://github.com/NixOS/nixpkgs/issues/107375) +Reasons for this are comments in the script. Then, you should be able to do all the package-level `yarn run` commands/scripts. + +# Try not to push directly to develop + +If you absolutely must, please prefix your commit message with `hotfix: `. # Pull request protocol diff --git a/README.md b/README.md index 108720fc..2f1de7f7 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ The playground depends on the components library which then depends on the langu # Develop -For any project in the repo, begin by running `yarn` in the top level (TODO: is this true?) +For any project in the repo, begin by running `yarn` in the top level ```sh yarn diff --git a/examples/decay.squiggle b/examples/decay.squiggle new file mode 100644 index 00000000..60f4c95e --- /dev/null +++ b/examples/decay.squiggle @@ -0,0 +1,20 @@ +# The following code was provided by Nuño Sempere, it comes directly from the post https://www.lesswrong.com/s/rDe8QE5NvXcZYzgZ3/p/j8o6sgRerE3tqNWdj +## Initial setup +yearly_probability_max = 0.95 +yearly_probability_min = 0.66 +period_probability_function(epsilon, yearly_probability) = 1 - (1 - yearly_probability) ^ (1 / epsilon) +probability_decayed(t, time_periods, period_probability) = 1 - (1 - period_probability) ^ (time_periods - t) + +## Monthly decomposition +months_in_a_year=12 + +monthly_probability_min = period_probability_function(months_in_a_year, yearly_probability_min) +monthly_probability_max = period_probability_function(months_in_a_year, yearly_probability_max) + +probability_decayed_monthly_min(t) = probability_decayed(t, months_in_a_year, monthly_probability_min) +probability_decayed_monthly_max(t) = probability_decayed(t, months_in_a_year, monthly_probability_max) +probability_decayed_monthly(t) = probability_decayed_monthly_min(t) to probability_decayed_monthly_max(t) + +probability_decayed_monthly +## probability_decayed_monthly(6) +## mean(probability_decayed_monthly(6)) diff --git a/examples/givedirectly.squiggle b/examples/givedirectly.squiggle new file mode 100644 index 00000000..16dda3a7 --- /dev/null +++ b/examples/givedirectly.squiggle @@ -0,0 +1,38 @@ +# This is a cost effectiveness analysis of givedirectly, originally done by givewell, and translated into Squiggle by Sam Nolan +donation_size = 10000 +proportion_of_funding_available = beta(10, 2) +total_funding_available = donation_size * proportion_of_funding_available +household_size = 3.7 to 5.7 +size_of_transfer = 800 to 1200 +size_of_transfer_per_person = size_of_transfer / household_size + +portion_invested = 0.3 to 0.5 +amount_invested = portion_invested * size_of_transfer_per_person +amount_consumed = (1 - portion_invested) * size_of_transfer_per_person +return_on_investment = 0.08 to 0.12 +increase_in_consumption_from_investments = return_on_investment * amount_invested +baseline_consumption = 200 to 350 +log_increase_in_consumption = log(amount_consumed + baseline_consumption) + log(baseline_consumption) +log_increase_in_consumption_from_investment = log(increase_in_consumption_from_investments + baseline_consumption) + log(baseline_consumption) +investment_duration = 8 to 12 +discount_rate = beta(1.004, 20) + +present_value_excluding_last_year = log_increase_in_consumption_from_investment * (1 - (1 + discount_rate) ^ (-investment_duration)) / (log(1 + discount_rate)) + +percent_of_investment_returned = 0.15 to 0.25 + +pv_consumption_last_year = (log(baseline_consumption + amount_invested * (return_on_investment + percent_of_investment_returned)) - log(baseline_consumption)) / (1 + discount_rate)^investment_duration + +total_pv_of_cash_transfer = pv_consumption_last_year + present_value_excluding_last_year + log_increase_in_consumption + +discount_negative_spoiler = 0.03 to 0.07 + +value_discounting_spoiler = discount_negative_spoiler * total_pv_of_cash_transfer + +consumption_increase_per_household = value_discounting_spoiler * household_size + +amount_of_transfers_made = total_funding_available / size_of_transfer + +total_increase_in_ln_consumption = amount_of_transfers_made * consumption_increase_per_household + +total_increase_in_ln_consumption diff --git a/examples/wholenumberassignmentevaluation.squiggle b/examples/wholenumberassignmentevaluation.squiggle new file mode 100644 index 00000000..f441c67e --- /dev/null +++ b/examples/wholenumberassignmentevaluation.squiggle @@ -0,0 +1,3 @@ +xY1 = 99 +aBa3 = xY1 * 2 + 1 +aBa3 * xY1 + aBa3 diff --git a/nixos.sh b/nixos.sh new file mode 100755 index 00000000..91aa754f --- /dev/null +++ b/nixos.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +# This script is only relevant if you're rolling nixos. + +# Esy (a bisect_ppx dependency/build tool) is borked on nixos without using an FHS shell. https://github.com/esy/esy/issues/858 +# We need to patchelf rescript executables. https://github.com/NixOS/nixpkgs/issues/107375 +set -x + +fhsShellName="squiggle-development" +fhsShellDotNix="{pkgs ? import {} }: (pkgs.buildFHSUserEnv { name = \"${fhsShellName}\"; targetPkgs = pkgs: [pkgs.yarn]; runScript = \"yarn\"; }).env" +nix-shell - <<<"$fhsShellDotNix" + +theLd=$(patchelf --print-interpreter $(which mkdir)) +patchelf --set-interpreter $theLd ./node_modules/gentype/gentype.exe +patchelf --set-interpreter $theLd ./node_modules/rescript/linux/*.exe +patchelf --set-interpreter $theLd ./node_modules/bisect_ppx/ppx +patchelf --set-interpreter $theLd ./node_moduels/bisect_ppx/bisect-ppx-report +theSo=$(find /nix/store/*$fhsShellName*/lib64 -name libstdc++.so.6 | grep $fhsShellName | head -n 1) +patchelf --replace-needed libstdc++.so.6 $theSo ./node_modules/rescript/linux/ninja.exe diff --git a/packages/components/.prettierignore b/packages/components/.prettierignore new file mode 100644 index 00000000..5e03c80e --- /dev/null +++ b/packages/components/.prettierignore @@ -0,0 +1,2 @@ +dist/ +storybook-static diff --git a/packages/components/package.json b/packages/components/package.json index 9b4e9fdd..d895d545 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,26 +1,45 @@ { "name": "@quri/squiggle-components", - "version": "0.1.8", + "version": "0.2.9", + "licence": "MIT", "dependencies": { - "@quri/squiggle-lang": "0.2.2", + "antd": "^4.20.1", + "react-ace": "10.1.0", + "react-dom": "^18.1.0", "@react-hook/size": "^2.1.2", + "styled-components": "^5.3.5" + }, + "devDependencies": { + "@babel/plugin-proposal-private-property-in-object": "^7.16.7", + "@storybook/addon-actions": "^6.4.22", + "@storybook/addon-essentials": "^6.4.22", + "@storybook/addon-links": "^6.4.22", + "@storybook/builder-webpack5": "^6.4.22", + "@storybook/manager-webpack5": "^6.4.22", + "@storybook/node-logger": "^6.4.22", + "@storybook/preset-create-react-app": "^4.1.0", + "@storybook/react": "^6.4.22", + "@types/styled-components": "^5.1.24", + "@types/webpack": "^5.28.0", + "style-loader": "^3.3.1", + "ts-loader": "^9.2.9", + "webpack": "^5.72.0", + "webpack-cli": "^4.9.2", + "webpack-dev-server": "^4.8.1", + "@quri/squiggle-lang": "0.2.5", "@testing-library/jest-dom": "^5.16.4", - "@testing-library/react": "^13.0.1", - "@testing-library/user-event": "^14.0.4", + "@testing-library/react": "^13.1.1", + "@testing-library/user-event": "^14.1.1", "@types/jest": "^27.4.0", - "@types/lodash": "^4.14.181", - "@types/node": "^17.0.24", + "@types/lodash": "^4.14.182", + "@types/node": "^17.0.29", "@types/react": "^18.0.3", - "@types/react-dom": "^18.0.1", - "antd": "^4.19.3", + "@types/react-dom": "^18.0.2", "cross-env": "^7.0.3", "lodash": "^4.17.21", - "react": "^18.0.0", - "react-ace": "10.0.0", - "react-dom": "^18.0.0", + "react": "^18.1.0", "react-scripts": "5.0.1", "react-vega": "^7.5.0", - "styled-components": "^5.3.5", "tsconfig-paths-webpack-plugin": "^3.5.2", "typescript": "^4.6.3", "vega": "^5.22.1", @@ -65,25 +84,6 @@ "last 1 safari version" ] }, - "devDependencies": { - "@babel/plugin-proposal-private-property-in-object": "^7.16.7", - "@storybook/addon-actions": "^6.4.22", - "@storybook/addon-essentials": "^6.4.22", - "@storybook/addon-links": "^6.4.22", - "@storybook/builder-webpack5": "^6.4.22", - "@storybook/manager-webpack5": "^6.4.22", - "@storybook/node-logger": "^6.4.22", - "@storybook/preset-create-react-app": "^4.1.0", - "@storybook/react": "^6.4.22", - "@types/styled-components": "^5.1.24", - "@types/webpack": "^5.28.0", - "react-codejar": "^1.1.2", - "style-loader": "^3.3.1", - "ts-loader": "^9.2.8", - "webpack": "^5.72.0", - "webpack-cli": "^4.9.2", - "webpack-dev-server": "^4.8.1" - }, "resolutions": { "@types/react": "17.0.43" }, diff --git a/packages/components/src/components/SquiggleChart.tsx b/packages/components/src/components/SquiggleChart.tsx index 03ed374d..534d164d 100644 --- a/packages/components/src/components/SquiggleChart.tsx +++ b/packages/components/src/components/SquiggleChart.tsx @@ -6,7 +6,7 @@ import { errorValueToString, squiggleExpression, } from "@quri/squiggle-lang"; -import type { samplingParams, exportEnv } from "@quri/squiggle-lang"; +import type { samplingParams } from "@quri/squiggle-lang"; import { NumberShower } from "./NumberShower"; import { DistributionChart } from "./DistributionChart"; import { ErrorBox } from "./ErrorBox"; @@ -129,9 +129,9 @@ export interface SquiggleChartProps { /** If the result is a function, how many points along the function it samples */ diagramCount?: number; /** variables declared before this expression */ - environment?: exportEnv; + environment?: unknown; /** When the environment changes */ - onEnvChange?(env: exportEnv): void; + onChange?(expr: squiggleExpression): void; /** CSS width of the element */ width?: number; height?: number; @@ -141,8 +141,7 @@ export const SquiggleChart: React.FC = ({ squiggleString = "", sampleCount = 1000, outputXYPoints = 1000, - environment = [], - onEnvChange = () => {}, + onChange = () => {}, height = 60, width = NaN, }: SquiggleChartProps) => { @@ -155,11 +154,11 @@ export const SquiggleChart: React.FC = ({ sampleCount: sampleCount, xyPointLength: outputXYPoints, }; - let expressionResult = run(squiggleString, samplingInputs, environment); + let expressionResult = run(squiggleString, samplingInputs); let internal: JSX.Element; if (expressionResult.tag === "Ok") { - onEnvChange(environment); let expression = expressionResult.value; + onChange(expression); internal = ( ); diff --git a/packages/components/src/components/SquiggleEditor.tsx b/packages/components/src/components/SquiggleEditor.tsx index c565b788..580db580 100644 --- a/packages/components/src/components/SquiggleEditor.tsx +++ b/packages/components/src/components/SquiggleEditor.tsx @@ -2,8 +2,8 @@ import * as React from "react"; import * as ReactDOM from "react-dom"; import { SquiggleChart } from "./SquiggleChart"; import { CodeEditor } from "./CodeEditor"; -import type { exportEnv } from "@quri/squiggle-lang"; import styled from "styled-components"; +import type { squiggleExpression } from "@quri/squiggle-lang"; export interface SquiggleEditorProps { /** The input string for squiggle */ @@ -21,9 +21,9 @@ export interface SquiggleEditorProps { /** If the result is a function, how many points along the function it samples */ diagramCount?: number; /** The environment, other variables that were already declared */ - environment?: exportEnv; + environment?: unknown; /** when the environment changes. Used again for notebook magic*/ - onEnvChange?(env: exportEnv): void; + onChange?(expr: squiggleExpression): void; /** The width of the element */ width: number; } @@ -44,7 +44,7 @@ export let SquiggleEditor: React.FC = ({ diagramStart, diagramStop, diagramCount, - onEnvChange, + onChange, environment, }: SquiggleEditorProps) => { let [expression, setExpression] = React.useState(initialSquiggleString); @@ -70,7 +70,7 @@ export let SquiggleEditor: React.FC = ({ diagramStop={diagramStop} diagramCount={diagramCount} environment={environment} - onEnvChange={onEnvChange} + onChange={onChange} /> ); @@ -81,7 +81,7 @@ export function renderSquiggleEditorToDom(props: SquiggleEditorProps) { ReactDOM.render( { + onChange={(expr) => { // Typescript complains on two levels here. // - Div elements don't have a value property // - Even if it did (like it was an input element), it would have to @@ -97,10 +97,10 @@ export function renderSquiggleEditorToDom(props: SquiggleEditorProps) { // viewof env = cell('normal(0,1)') // to work // @ts-ignore - parent.value = env; + parent.value = expr; parent.dispatchEvent(new CustomEvent("input")); - if (props.onEnvChange) props.onEnvChange(env); + if (props.onChange) props.onChange(expr); }} />, parent diff --git a/packages/squiggle-lang/.gitignore b/packages/squiggle-lang/.gitignore index 8375bb3b..4db5c5f4 100644 --- a/packages/squiggle-lang/.gitignore +++ b/packages/squiggle-lang/.gitignore @@ -19,3 +19,5 @@ yarn-error.log dist *.coverage _coverage +coverage +.nyc_output/ diff --git a/packages/squiggle-lang/.npmignore b/packages/squiggle-lang/.npmignore index 74c2c781..bfbe051d 100644 --- a/packages/squiggle-lang/.npmignore +++ b/packages/squiggle-lang/.npmignore @@ -9,3 +9,7 @@ examples yarn.nix bsconfig.json tsconfig.json +.nyc_outputs +*.coverage +_coverage +coverage diff --git a/packages/squiggle-lang/.prettierignore b/packages/squiggle-lang/.prettierignore index 30674e4d..1c242bb9 100644 --- a/packages/squiggle-lang/.prettierignore +++ b/packages/squiggle-lang/.prettierignore @@ -2,3 +2,6 @@ dist lib *.bs.js *.gen.tsx +.nyc_output/ +coverage/ +.cache/ \ No newline at end of file diff --git a/packages/squiggle-lang/__tests__/Distributions/AlgebraicShapeCombination_test.res b/packages/squiggle-lang/__tests__/Distributions/AlgebraicShapeCombination_test.res new file mode 100644 index 00000000..702b67a4 --- /dev/null +++ b/packages/squiggle-lang/__tests__/Distributions/AlgebraicShapeCombination_test.res @@ -0,0 +1,17 @@ +open Jest +open TestHelpers + +describe("Combining Continuous and Discrete Distributions", () => { + makeTest( + "keep order of xs when multiplying by negative number", + AlgebraicShapeCombination.isOrdered( + AlgebraicShapeCombination.combineShapesContinuousDiscrete( + #Multiply, + {xs: [0., 1.], ys: [1., 1.]}, + {xs: [-1.], ys: [1.]}, + ~discretePosition=Second, + ), + ), // Multiply distribution by -1 + true, + ) +}) diff --git a/packages/squiggle-lang/__tests__/Distributions/DistributionOperation_test.res b/packages/squiggle-lang/__tests__/Distributions/DistributionOperation_test.res index 078cad05..60e74d74 100644 --- a/packages/squiggle-lang/__tests__/Distributions/DistributionOperation_test.res +++ b/packages/squiggle-lang/__tests__/Distributions/DistributionOperation_test.res @@ -30,7 +30,7 @@ let toExt: option<'a> => 'a = E.O.toExt( describe("sparkline", () => { let runTest = ( name: string, - dist: GenericDist_Types.genericDist, + dist: DistributionTypes.genericDist, expected: DistributionOperation.outputType, ) => { test(name, () => { diff --git a/packages/squiggle-lang/__tests__/Distributions/GenericDist_Fixtures.res b/packages/squiggle-lang/__tests__/Distributions/GenericDist_Fixtures.res index a967b924..4d41930f 100644 --- a/packages/squiggle-lang/__tests__/Distributions/GenericDist_Fixtures.res +++ b/packages/squiggle-lang/__tests__/Distributions/GenericDist_Fixtures.res @@ -1,14 +1,14 @@ -let normalDist5: GenericDist_Types.genericDist = Symbolic(#Normal({mean: 5.0, stdev: 2.0})) -let normalDist10: GenericDist_Types.genericDist = Symbolic(#Normal({mean: 10.0, stdev: 2.0})) -let normalDist20: GenericDist_Types.genericDist = Symbolic(#Normal({mean: 20.0, stdev: 2.0})) -let normalDist: GenericDist_Types.genericDist = normalDist5 +let normalDist5: DistributionTypes.genericDist = Symbolic(#Normal({mean: 5.0, stdev: 2.0})) +let normalDist10: DistributionTypes.genericDist = Symbolic(#Normal({mean: 10.0, stdev: 2.0})) +let normalDist20: DistributionTypes.genericDist = Symbolic(#Normal({mean: 20.0, stdev: 2.0})) +let normalDist: DistributionTypes.genericDist = normalDist5 -let betaDist: GenericDist_Types.genericDist = Symbolic(#Beta({alpha: 2.0, beta: 5.0})) -let lognormalDist: GenericDist_Types.genericDist = Symbolic(#Lognormal({mu: 0.0, sigma: 1.0})) -let cauchyDist: GenericDist_Types.genericDist = Symbolic(#Cauchy({local: 1.0, scale: 1.0})) -let triangularDist: GenericDist_Types.genericDist = Symbolic( +let betaDist: DistributionTypes.genericDist = Symbolic(#Beta({alpha: 2.0, beta: 5.0})) +let lognormalDist: DistributionTypes.genericDist = Symbolic(#Lognormal({mu: 0.0, sigma: 1.0})) +let cauchyDist: DistributionTypes.genericDist = Symbolic(#Cauchy({local: 1.0, scale: 1.0})) +let triangularDist: DistributionTypes.genericDist = Symbolic( #Triangular({low: 1.0, medium: 2.0, high: 3.0}), ) -let exponentialDist: GenericDist_Types.genericDist = Symbolic(#Exponential({rate: 2.0})) -let uniformDist: GenericDist_Types.genericDist = Symbolic(#Uniform({low: 9.0, high: 10.0})) -let floatDist: GenericDist_Types.genericDist = Symbolic(#Float(1e1)) +let exponentialDist: DistributionTypes.genericDist = Symbolic(#Exponential({rate: 2.0})) +let uniformDist: DistributionTypes.genericDist = Symbolic(#Uniform({low: 9.0, high: 10.0})) +let floatDist: DistributionTypes.genericDist = Symbolic(#Float(1e1)) diff --git a/packages/squiggle-lang/__tests__/Distributions/Invariants/AlgebraicCombination_test.res b/packages/squiggle-lang/__tests__/Distributions/Invariants/AlgebraicCombination_test.res index c7e0ac46..b7e14f56 100644 --- a/packages/squiggle-lang/__tests__/Distributions/Invariants/AlgebraicCombination_test.res +++ b/packages/squiggle-lang/__tests__/Distributions/Invariants/AlgebraicCombination_test.res @@ -43,10 +43,10 @@ describe("(Algebraic) addition of distributions", () => { test("normal(mean=5) + normal(mean=20)", () => { normalDist5 ->algebraicAdd(normalDist20) - ->E.R2.fmap(GenericDist_Types.Constructors.UsingDists.mean) + ->E.R2.fmap(DistributionTypes.Constructors.UsingDists.mean) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) - ->E.R.toExn + ->E.R.toExn("Expected float", _) ->expect ->toBe(Some(2.5e1)) }) @@ -57,15 +57,15 @@ describe("(Algebraic) addition of distributions", () => { let received = uniformDist ->algebraicAdd(betaDist) - ->E.R2.fmap(GenericDist_Types.Constructors.UsingDists.mean) + ->E.R2.fmap(DistributionTypes.Constructors.UsingDists.mean) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) - ->E.R.toExn + ->E.R.toExn("Expected float", _) switch received { | None => "algebraicAdd has"->expect->toBe("failed") // This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad. // sometimes it works with ~digits=2. - | Some(x) => x->expect->toBeSoCloseTo(0.01927225696028752, ~digits=1) // (uniformMean +. betaMean) + | Some(x) => x->expect->toBeSoCloseTo(9.786831807237022, ~digits=1) // (uniformMean +. betaMean) } }) test("beta(alpha=2, beta=5) + uniform(low=9, high=10)", () => { @@ -74,15 +74,15 @@ describe("(Algebraic) addition of distributions", () => { let received = betaDist ->algebraicAdd(uniformDist) - ->E.R2.fmap(GenericDist_Types.Constructors.UsingDists.mean) + ->E.R2.fmap(DistributionTypes.Constructors.UsingDists.mean) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) - ->E.R.toExn + ->E.R.toExn("Expected float", _) switch received { | None => "algebraicAdd has"->expect->toBe("failed") // This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad. // sometimes it works with ~digits=2. - | Some(x) => x->expect->toBeSoCloseTo(0.019275414920485248, ~digits=1) // (uniformMean +. betaMean) + | Some(x) => x->expect->toBeSoCloseTo(9.784290207736126, ~digits=1) // (uniformMean +. betaMean) } }) }) @@ -95,7 +95,7 @@ describe("(Algebraic) addition of distributions", () => { let received = normalDist10 // this should be normal(10, sqrt(8)) ->Ok - ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.pdf(d, x)) + ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.pdf(d, x)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) ->E.R.toOption @@ -103,7 +103,7 @@ describe("(Algebraic) addition of distributions", () => { let calculated = normalDist5 ->algebraicAdd(normalDist5) - ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.pdf(d, x)) + ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.pdf(d, x)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) ->E.R.toOption @@ -126,7 +126,7 @@ describe("(Algebraic) addition of distributions", () => { let received = normalDist20 ->Ok - ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.pdf(d, 1.9e1)) + ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.pdf(d, 1.9e1)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) ->E.R.toOption @@ -134,7 +134,7 @@ describe("(Algebraic) addition of distributions", () => { let calculated = normalDist10 ->algebraicAdd(normalDist10) - ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.pdf(d, 1.9e1)) + ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.pdf(d, 1.9e1)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) ->E.R.toOption @@ -155,30 +155,30 @@ describe("(Algebraic) addition of distributions", () => { let received = uniformDist ->algebraicAdd(betaDist) - ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.pdf(d, 1e1)) + ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.pdf(d, 1e1)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) - ->E.R.toExn + ->E.R.toExn("Expected float", _) switch received { | None => "algebraicAdd has"->expect->toBe("failed") // This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad. // sometimes it works with ~digits=4. - | Some(x) => x->expect->toBeSoCloseTo(0.001978994877226945, ~digits=3) + // This value was calculated by a python script + | Some(x) => x->expect->toBeSoCloseTo(0.979023, ~digits=0) } }) test("(beta(alpha=2, beta=5) + uniform(low=9, high=10)).pdf(10)", () => { let received = betaDist ->algebraicAdd(uniformDist) - ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.pdf(d, 1e1)) + ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.pdf(d, 1e1)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) - ->E.R.toExn + ->E.R.toExn("Expected float", _) switch received { | None => "algebraicAdd has"->expect->toBe("failed") - // This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad. - // sometimes it works with ~digits=4. - | Some(x) => x->expect->toBeSoCloseTo(0.001978994877226945, ~digits=3) + // This is nondeterministic. + | Some(x) => x->expect->toBeSoCloseTo(0.979023, ~digits=0) } }) }) @@ -187,7 +187,7 @@ describe("(Algebraic) addition of distributions", () => { let received = normalDist10 ->Ok - ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.cdf(d, x)) + ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.cdf(d, x)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) ->E.R.toOption @@ -195,7 +195,7 @@ describe("(Algebraic) addition of distributions", () => { let calculated = normalDist5 ->algebraicAdd(normalDist5) - ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.cdf(d, x)) + ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.cdf(d, x)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) ->E.R.toOption @@ -217,7 +217,7 @@ describe("(Algebraic) addition of distributions", () => { let received = normalDist20 ->Ok - ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.cdf(d, 1.25e1)) + ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.cdf(d, 1.25e1)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) ->E.R.toOption @@ -225,7 +225,7 @@ describe("(Algebraic) addition of distributions", () => { let calculated = normalDist10 ->algebraicAdd(normalDist10) - ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.cdf(d, 1.25e1)) + ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.cdf(d, 1.25e1)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) ->E.R.toOption @@ -246,30 +246,30 @@ describe("(Algebraic) addition of distributions", () => { let received = uniformDist ->algebraicAdd(betaDist) - ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.cdf(d, 1e1)) + ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.cdf(d, 1e1)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) - ->E.R.toExn + ->E.R.toExn("Expected float", _) switch received { | None => "algebraicAdd has"->expect->toBe("failed") // This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad. - // sometimes it works with ~digits=4. - | Some(x) => x->expect->toBeSoCloseTo(0.0013961779932477507, ~digits=3) + // The value was calculated externally using a python script + | Some(x) => x->expect->toBeSoCloseTo(0.71148, ~digits=1) } }) test("(beta(alpha=2, beta=5) + uniform(low=9, high=10)).cdf(10)", () => { let received = betaDist ->algebraicAdd(uniformDist) - ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.cdf(d, 1e1)) + ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.cdf(d, 1e1)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) - ->E.R.toExn + ->E.R.toExn("Expected float", _) switch received { | None => "algebraicAdd has"->expect->toBe("failed") // This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad. - // sometimes it works with ~digits=4. - | Some(x) => x->expect->toBeSoCloseTo(0.001388898111625753, ~digits=3) + // The value was calculated externally using a python script + | Some(x) => x->expect->toBeSoCloseTo(0.71148, ~digits=1) } }) }) @@ -279,7 +279,7 @@ describe("(Algebraic) addition of distributions", () => { let received = normalDist10 ->Ok - ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.inv(d, x)) + ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.inv(d, x)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) ->E.R.toOption @@ -287,7 +287,7 @@ describe("(Algebraic) addition of distributions", () => { let calculated = normalDist5 ->algebraicAdd(normalDist5) - ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.inv(d, x)) + ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.inv(d, x)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) ->E.R.toOption @@ -309,7 +309,7 @@ describe("(Algebraic) addition of distributions", () => { let received = normalDist20 ->Ok - ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.inv(d, 1e-1)) + ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.inv(d, 1e-1)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) ->E.R.toOption @@ -317,7 +317,7 @@ describe("(Algebraic) addition of distributions", () => { let calculated = normalDist10 ->algebraicAdd(normalDist10) - ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.inv(d, 1e-1)) + ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.inv(d, 1e-1)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) ->E.R.toOption @@ -338,30 +338,30 @@ describe("(Algebraic) addition of distributions", () => { let received = uniformDist ->algebraicAdd(betaDist) - ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.inv(d, 2e-2)) + ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.inv(d, 2e-2)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) - ->E.R.toExn + ->E.R.toExn("Expected float", _) switch received { | None => "algebraicAdd has"->expect->toBe("failed") // This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad. // sometimes it works with ~digits=2. - | Some(x) => x->expect->toBeSoCloseTo(10.927078217530806, ~digits=0) + | Some(x) => x->expect->toBeSoCloseTo(9.179319623146968, ~digits=0) } }) test("(beta(alpha=2, beta=5) + uniform(low=9, high=10)).inv(2e-2)", () => { let received = betaDist ->algebraicAdd(uniformDist) - ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.inv(d, 2e-2)) + ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.inv(d, 2e-2)) ->E.R2.fmap(run) ->E.R2.fmap(toFloat) - ->E.R.toExn + ->E.R.toExn("Expected float", _) switch received { | None => "algebraicAdd has"->expect->toBe("failed") // This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad. // sometimes it works with ~digits=2. - | Some(x) => x->expect->toBeSoCloseTo(10.915396627014363, ~digits=0) + | Some(x) => x->expect->toBeSoCloseTo(9.190872365862756, ~digits=0) } }) }) diff --git a/packages/squiggle-lang/__tests__/Distributions/Invariants/Means_test.res b/packages/squiggle-lang/__tests__/Distributions/Invariants/Means_test.res index bcc461bc..99e3e5a3 100644 --- a/packages/squiggle-lang/__tests__/Distributions/Invariants/Means_test.res +++ b/packages/squiggle-lang/__tests__/Distributions/Invariants/Means_test.res @@ -15,7 +15,7 @@ open TestHelpers module Internals = { let epsilon = 5e1 - let mean = GenericDist_Types.Constructors.UsingDists.mean + let mean = DistributionTypes.Constructors.UsingDists.mean let expectImpossiblePath: string => assertion = algebraicOp => `${algebraicOp} has`->expect->toEqual("failed") @@ -50,7 +50,11 @@ module Internals = { let dist1 = dist1'->DistributionTypes.Symbolic let dist2 = dist2'->DistributionTypes.Symbolic let received = - distOp(dist1, dist2)->E.R2.fmap(mean)->E.R2.fmap(run)->E.R2.fmap(toFloat)->E.R.toExn + distOp(dist1, dist2) + ->E.R2.fmap(mean) + ->E.R2.fmap(run) + ->E.R2.fmap(toFloat) + ->E.R.toExn("Expected float", _) let expected = floatOp(runMean(dist1), runMean(dist2)) switch received { | None => expectImpossiblePath(description) @@ -80,14 +84,16 @@ let {testOperationMean, distributions, pairsOfDifferentDistributions, epsilon} = describe("Means are invariant", () => { describe("for addition", () => { let testAdditionMean = testOperationMean(algebraicAdd, "algebraicAdd", \"+.", ~epsilon) + let testAddInvariant = (t1, t2) => + E.R.liftM2(testAdditionMean, t1, t2)->E.R.toExn("Means were not invariant", _) testAll("with two of the same distribution", distributions, dist => { - E.R.liftM2(testAdditionMean, dist, dist)->E.R.toExn + testAddInvariant(dist, dist) }) testAll("with two different distributions", pairsOfDifferentDistributions, dists => { let (dist1, dist2) = dists - E.R.liftM2(testAdditionMean, dist1, dist2)->E.R.toExn + testAddInvariant(dist1, dist2) }) testAll( @@ -95,7 +101,7 @@ describe("Means are invariant", () => { pairsOfDifferentDistributions, dists => { let (dist1, dist2) = dists - E.R.liftM2(testAdditionMean, dist2, dist1)->E.R.toExn + testAddInvariant(dist1, dist2) }, ) }) @@ -107,14 +113,16 @@ describe("Means are invariant", () => { \"-.", ~epsilon, ) + let testSubtractInvariant = (t1, t2) => + E.R.liftM2(testSubtractionMean, t1, t2)->E.R.toExn("Means were not invariant", _) testAll("with two of the same distribution", distributions, dist => { - E.R.liftM2(testSubtractionMean, dist, dist)->E.R.toExn + testSubtractInvariant(dist, dist) }) testAll("with two different distributions", pairsOfDifferentDistributions, dists => { let (dist1, dist2) = dists - E.R.liftM2(testSubtractionMean, dist1, dist2)->E.R.toExn + testSubtractInvariant(dist1, dist2) }) testAll( @@ -122,7 +130,7 @@ describe("Means are invariant", () => { pairsOfDifferentDistributions, dists => { let (dist1, dist2) = dists - E.R.liftM2(testSubtractionMean, dist2, dist1)->E.R.toExn + testSubtractInvariant(dist1, dist2) }, ) }) @@ -134,14 +142,16 @@ describe("Means are invariant", () => { \"*.", ~epsilon, ) + let testMultiplicationInvariant = (t1, t2) => + E.R.liftM2(testMultiplicationMean, t1, t2)->E.R.toExn("Means were not invariant", _) testAll("with two of the same distribution", distributions, dist => { - E.R.liftM2(testMultiplicationMean, dist, dist)->E.R.toExn + testMultiplicationInvariant(dist, dist) }) testAll("with two different distributions", pairsOfDifferentDistributions, dists => { let (dist1, dist2) = dists - E.R.liftM2(testMultiplicationMean, dist1, dist2)->E.R.toExn + testMultiplicationInvariant(dist1, dist2) }) testAll( @@ -149,7 +159,7 @@ describe("Means are invariant", () => { pairsOfDifferentDistributions, dists => { let (dist1, dist2) = dists - E.R.liftM2(testMultiplicationMean, dist2, dist1)->E.R.toExn + testMultiplicationInvariant(dist1, dist2) }, ) }) diff --git a/packages/squiggle-lang/__tests__/Distributions/SampleSetDist_ToPointSet_test.res b/packages/squiggle-lang/__tests__/Distributions/SampleSetDist_ToPointSet_test.res new file mode 100644 index 00000000..a2c7baa1 --- /dev/null +++ b/packages/squiggle-lang/__tests__/Distributions/SampleSetDist_ToPointSet_test.res @@ -0,0 +1,20 @@ +open Jest +open Expect + +describe("Converting from a sample set distribution", () => { + test("Should be normalized", () => { + let outputXYShape = SampleSetDist_ToPointSet.Internals.KDE.normalSampling( + [1., 2., 3., 3., 4., 5., 5., 5., 6., 8., 9., 9.], + 50, + 2, + ) + let c: PointSetTypes.continuousShape = { + xyShape: outputXYShape, + interpolation: #Linear, + integralSumCache: None, + integralCache: None, + } + + expect(Continuous.isNormalized(c))->toBe(true) + }) +}) diff --git a/packages/squiggle-lang/__tests__/Distributions/SampleSetDist_test.res b/packages/squiggle-lang/__tests__/Distributions/SampleSetDist_test.res deleted file mode 100644 index ee36d191..00000000 --- a/packages/squiggle-lang/__tests__/Distributions/SampleSetDist_test.res +++ /dev/null @@ -1,40 +0,0 @@ -open Jest -open TestHelpers - -describe("Continuous and discrete splits", () => { - makeTest( - "splits (1)", - SampleSetDist_ToPointSet.Internals.T.splitContinuousAndDiscrete([1.432, 1.33455, 2.0]), - ([1.432, 1.33455, 2.0], E.FloatFloatMap.empty()), - ) - makeTest( - "splits (2)", - SampleSetDist_ToPointSet.Internals.T.splitContinuousAndDiscrete([ - 1.432, - 1.33455, - 2.0, - 2.0, - 2.0, - 2.0, - ]) |> (((c, disc)) => (c, disc |> E.FloatFloatMap.toArray)), - ([1.432, 1.33455], [(2.0, 4.0)]), - ) - - let makeDuplicatedArray = count => { - let arr = Belt.Array.range(1, count) |> E.A.fmap(float_of_int) - let sorted = arr |> Belt.SortArray.stableSortBy(_, compare) - E.A.concatMany([sorted, sorted, sorted, sorted]) |> Belt.SortArray.stableSortBy(_, compare) - } - - let (_, discrete1) = SampleSetDist_ToPointSet.Internals.T.splitContinuousAndDiscrete( - makeDuplicatedArray(10), - ) - let toArr1 = discrete1 |> E.FloatFloatMap.toArray - makeTest("splitMedium at count=10", toArr1 |> Belt.Array.length, 10) - - let (_c, discrete2) = SampleSetDist_ToPointSet.Internals.T.splitContinuousAndDiscrete( - makeDuplicatedArray(500), - ) - let toArr2 = discrete2 |> E.FloatFloatMap.toArray - makeTest("splitMedium at count=500", toArr2 |> Belt.Array.length, 500) -}) diff --git a/packages/squiggle-lang/__tests__/E/splitContinuousAndDiscrete_test.res b/packages/squiggle-lang/__tests__/E/splitContinuousAndDiscrete_test.res new file mode 100644 index 00000000..449787dd --- /dev/null +++ b/packages/squiggle-lang/__tests__/E/splitContinuousAndDiscrete_test.res @@ -0,0 +1,48 @@ +open Jest +open TestHelpers + +let prepareInputs = (ar, minWeight) => + E.A.Sorted.Floats.splitContinuousAndDiscreteForMinWeight(ar, ~minDiscreteWeight=minWeight) |> ( + ((c, disc)) => (c, disc |> E.FloatFloatMap.toArray) + ) + +describe("Continuous and discrete splits", () => { + makeTest( + "is empty, with no common elements", + prepareInputs([1.432, 1.33455, 2.0], 2), + ([1.33455, 1.432, 2.0], []), + ) + + makeTest( + "only stores 3.5 as discrete when minWeight is 3", + prepareInputs([1.432, 1.33455, 2.0, 2.0, 3.5, 3.5, 3.5], 3), + ([1.33455, 1.432, 2.0, 2.0], [(3.5, 3.0)]), + ) + + makeTest( + "doesn't store 3.5 as discrete when minWeight is 5", + prepareInputs([1.432, 1.33455, 2.0, 2.0, 3.5, 3.5, 3.5], 5), + ([1.33455, 1.432, 2.0, 2.0, 3.5, 3.5, 3.5], []), + ) + + let makeDuplicatedArray = count => { + let arr = Belt.Array.range(1, count) |> E.A.fmap(float_of_int) + let sorted = arr |> Belt.SortArray.stableSortBy(_, compare) + E.A.concatMany([sorted, sorted, sorted, sorted]) |> Belt.SortArray.stableSortBy(_, compare) + } + + let (_, discrete1) = E.A.Sorted.Floats.splitContinuousAndDiscreteForMinWeight( + makeDuplicatedArray(10), + ~minDiscreteWeight=2, + ) + let toArr1 = discrete1 |> E.FloatFloatMap.toArray + makeTest("splitMedium at count=10", toArr1 |> Belt.Array.length, 10) + + let (_c, discrete2) = E.A.Sorted.Floats.splitContinuousAndDiscreteForMinWeight( + makeDuplicatedArray(500), + ~minDiscreteWeight=2, + ) + let toArr2 = discrete2 |> E.FloatFloatMap.toArray + makeTest("splitMedium at count=500", toArr2 |> Belt.Array.length, 500) + // makeTest("foo", [] |> Belt.Array.length, 500) +}) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_TestHelpers.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_TestHelpers.res index e6183ba0..581f6c8a 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_TestHelpers.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_TestHelpers.res @@ -7,5 +7,69 @@ open Expect let expectParseToBe = (expr: string, answer: string) => Reducer.parse(expr)->Expression.toStringResult->expect->toBe(answer) +let expectParseOuterToBe = (expr: string, answer: string) => + Reducer.parseOuter(expr)->Expression.toStringResult->expect->toBe(answer) + +let expectParsePartialToBe = (expr: string, answer: string) => + Reducer.parsePartial(expr)->Expression.toStringResult->expect->toBe(answer) + let expectEvalToBe = (expr: string, answer: string) => Reducer.evaluate(expr)->ExpressionValue.toStringResult->expect->toBe(answer) + +let expectEvalBindingsToBe = (expr: string, bindings: Reducer.externalBindings, answer: string) => + Reducer.evaluateUsingExternalBindings(expr, bindings) + ->ExpressionValue.toStringResult + ->expect + ->toBe(answer) + +let expectEvalPartialBindingsToBe = ( + expr: string, + bindings: Reducer.externalBindings, + answer: string, +) => + Reducer.evaluatePartialUsingExternalBindings(expr, bindings) + ->ExpressionValue.toStringResultRecord + ->expect + ->toBe(answer) + +let testParseToBe = (expr, answer) => test(expr, () => expectParseToBe(expr, answer)) +let testParseOuterToBe = (expr, answer) => test(expr, () => expectParseOuterToBe(expr, answer)) +let testParsePartialToBe = (expr, answer) => test(expr, () => expectParsePartialToBe(expr, answer)) +let testDescriptionParseToBe = (desc, expr, answer) => + test(desc, () => expectParseToBe(expr, answer)) + +let testEvalToBe = (expr, answer) => test(expr, () => expectEvalToBe(expr, answer)) +let testDescriptionEvalToBe = (desc, expr, answer) => test(desc, () => expectEvalToBe(expr, answer)) +let testEvalBindingsToBe = (expr, bindingsList, answer) => + test(expr, () => expectEvalBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer)) +let testEvalPartialBindingsToBe = (expr, bindingsList, answer) => + test(expr, () => expectEvalPartialBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer)) + +module MySkip = { + let testParseToBe = (expr, answer) => Skip.test(expr, () => expectParseToBe(expr, answer)) + let testParseOuterToBe = (expr, answer) => + Skip.test(expr, () => expectParseOuterToBe(expr, answer)) + let testParsePartialToBe = (expr, answer) => + Skip.test(expr, () => expectParsePartialToBe(expr, answer)) + let testEvalToBe = (expr, answer) => Skip.test(expr, () => expectEvalToBe(expr, answer)) + let testEvalBindingsToBe = (expr, bindingsList, answer) => + Skip.test(expr, () => expectEvalBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer)) + let testEvalPartialBindingsToBe = (expr, bindingsList, answer) => + Skip.test(expr, () => + expectEvalPartialBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer) + ) +} +module MyOnly = { + let testParseToBe = (expr, answer) => Only.test(expr, () => expectParseToBe(expr, answer)) + let testParseOuterToBe = (expr, answer) => + Only.test(expr, () => expectParseOuterToBe(expr, answer)) + let testParsePartialToBe = (expr, answer) => + Only.test(expr, () => expectParsePartialToBe(expr, answer)) + let testEvalToBe = (expr, answer) => Only.test(expr, () => expectEvalToBe(expr, answer)) + let testEvalBindingsToBe = (expr, bindingsList, answer) => + Only.test(expr, () => expectEvalBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer)) + let testEvalPartialBindingsToBe = (expr, bindingsList, answer) => + Only.test(expr, () => + expectEvalPartialBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer) + ) +} diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_externalBindings_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_externalBindings_test.res new file mode 100644 index 00000000..ce834ed1 --- /dev/null +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_externalBindings_test.res @@ -0,0 +1,60 @@ +open Jest +open Reducer_TestHelpers + +describe("Parse for Bindings", () => { + testParseOuterToBe("x", "Ok((:$$bindExpression (:$$bindings) :x))") + testParseOuterToBe("x+1", "Ok((:$$bindExpression (:$$bindings) (:add :x 1)))") + testParseOuterToBe( + "y = x+1; y", + "Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :y (:add :x 1))) :y))", + ) +}) + +describe("Parse Partial", () => { + testParsePartialToBe( + "x", + "Ok((:$$bindExpression (:$$bindStatement (:$$bindings) :x) (:$exportVariablesExpression)))", + ) + testParsePartialToBe( + "y=x", + "Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :y :x)) (:$exportVariablesExpression)))", + ) + testParsePartialToBe( + "y=x+1", + "Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :y (:add :x 1))) (:$exportVariablesExpression)))", + ) + testParsePartialToBe( + "y = x+1; z = y", + "Ok((:$$bindExpression (:$$bindStatement (:$$bindStatement (:$$bindings) (:$let :y (:add :x 1))) (:$let :z :y)) (:$exportVariablesExpression)))", + ) +}) + +describe("Eval with Bindings", () => { + testEvalBindingsToBe("x", list{("x", ExpressionValue.EvNumber(1.))}, "Ok(1)") + testEvalBindingsToBe("x+1", list{("x", ExpressionValue.EvNumber(1.))}, "Ok(2)") + testEvalBindingsToBe("y = x+1; y", list{("x", ExpressionValue.EvNumber(1.))}, "Ok(2)") +}) + +/* + Partial code is a partial code fragment that is cut out from a larger code. + Therefore it does not end with an expression. +*/ +describe("Eval Partial", () => { + testEvalPartialBindingsToBe( + // A partial cannot end with an expression + "x", + list{("x", ExpressionValue.EvNumber(1.))}, + "Error(Assignment expected)", + ) + testEvalPartialBindingsToBe("y=x", list{("x", ExpressionValue.EvNumber(1.))}, "Ok({x: 1, y: 1})") + testEvalPartialBindingsToBe( + "y=x+1", + list{("x", ExpressionValue.EvNumber(1.))}, + "Ok({x: 1, y: 2})", + ) + testEvalPartialBindingsToBe( + "y = x+1; z = y", + list{("x", ExpressionValue.EvNumber(1.))}, + "Ok({x: 1, y: 2, z: 2})", + ) +}) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_ternaryOperator_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_ternaryOperator_test.res new file mode 100644 index 00000000..61bfc8c7 --- /dev/null +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_ternaryOperator_test.res @@ -0,0 +1,12 @@ +open Jest +open Reducer_TestHelpers + +Skip.describe("Parse ternary operator", () => { + testParseToBe("true ? 'YES' : 'NO'", "Ok('YES')") + testParseToBe("false ? 'YES' : 'NO'", "Ok('NO')") +}) + +Skip.describe("Evaluate ternary operator", () => { + testEvalToBe("true ? 'YES' : 'NO'", "Ok('YES')") + testEvalToBe("false ? 'YES' : 'NO'", "Ok('NO')") +}) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_test.res index bd9101a2..5514b67c 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_test.res @@ -1,15 +1,6 @@ open Jest open Reducer_TestHelpers -let testParseToBe = (expr, answer) => test(expr, () => expectParseToBe(expr, answer)) - -let testDescriptionParseToBe = (desc, expr, answer) => - test(desc, () => expectParseToBe(expr, answer)) - -let testEvalToBe = (expr, answer) => test(expr, () => expectEvalToBe(expr, answer)) - -let testDescriptionEvalToBe = (desc, expr, answer) => test(desc, () => expectEvalToBe(expr, answer)) - describe("reducer using mathjs parse", () => { // Test the MathJs parser compatibility // Those tests toString that there is a semantic mapping from MathJs to Expression diff --git a/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res b/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res index 1b5f8bfd..4df843bb 100644 --- a/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res +++ b/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res @@ -20,9 +20,10 @@ describe("eval on distribution functions", () => { }) describe("unaryMinus", () => { testEval("mean(-normal(5,2))", "Ok(-5)") + testEval("-normal(5,2)", "Ok(Normal(-5,2))") }) describe("to", () => { - testEval("5 to 2", "Error(TODO: Low value must be less than high value.)") + testEval("5 to 2", "Error(Distribution Math Error: Low value must be less than high value.)") testEval("to(2,5)", "Ok(Lognormal(1.1512925464970227,0.27853260523016377))") testEval("to(-2,2)", "Ok(Normal(0,1.2159136638235384))") }) @@ -53,6 +54,7 @@ describe("eval on distribution functions", () => { describe("subtract", () => { testEval("10 - normal(5, 1)", "Ok(Normal(5,1))") testEval("normal(5, 1) - 10", "Ok(Normal(-5,1))") + testEval("mean(1 - toPointSet(normal(5, 2)))", "Ok(-4.002309896304692)") }) describe("multiply", () => { testEval("normal(10, 2) * 2", "Ok(Normal(20,4))") @@ -67,7 +69,7 @@ describe("eval on distribution functions", () => { testEval("lognormal(10,2) / lognormal(5,2)", "Ok(Lognormal(5,2.8284271247461903))") testEval("lognormal(5, 2) / 2", "Ok(Lognormal(4.306852819440055,2))") testEval("2 / lognormal(5, 2)", "Ok(Lognormal(-4.306852819440055,2))") - testEval("2 / normal(10, 2)", "Ok(Point Set Distribution)") + testEval("2 / normal(10, 2)", "Ok(Sample Set Distribution)") testEval("normal(10, 2) / 2", "Ok(Normal(5,1))") }) describe("truncate", () => { @@ -77,27 +79,27 @@ describe("eval on distribution functions", () => { }) describe("exp", () => { - testEval("exp(normal(5,2))", "Ok(Point Set Distribution)") + testEval("exp(normal(5,2))", "Ok(Sample Set Distribution)") }) describe("pow", () => { - testEval("pow(3, uniform(5,8))", "Ok(Point Set Distribution)") - testEval("pow(uniform(5,8), 3)", "Ok(Point Set Distribution)") + testEval("pow(3, uniform(5,8))", "Ok(Sample Set Distribution)") + testEval("pow(uniform(5,8), 3)", "Ok(Sample Set Distribution)") testEval("pow(uniform(5,8), uniform(9, 10))", "Ok(Sample Set Distribution)") }) describe("log", () => { - testEval("log(2, uniform(5,8))", "Ok(Point Set Distribution)") - testEval("log(normal(5,2), 3)", "Ok(Point Set Distribution)") - testEval("log(normal(5,2), normal(10,1))", "Ok(Sample Set Distribution)") - testEval("log(uniform(5,8))", "Ok(Point Set Distribution)") - testEval("log10(uniform(5,8))", "Ok(Point Set Distribution)") - }) - - describe("dotLog", () => { - testEval("dotLog(normal(5,2), 3)", "Ok(Point Set Distribution)") - testEval("dotLog(normal(5,2), 3)", "Ok(Point Set Distribution)") - testEval("dotLog(normal(5,2), normal(10,1))", "Ok(Point Set Distribution)") + testEval("log(2, uniform(5,8))", "Ok(Sample Set Distribution)") + testEval( + "log(normal(5,2), 3)", + "Error(Distribution Math Error: Logarithm of input error: First input must be completely greater than 0)", + ) + testEval( + "log(normal(5,2), normal(10,1))", + "Error(Distribution Math Error: Logarithm of input error: First input must be completely greater than 0)", + ) + testEval("log(uniform(5,8))", "Ok(Sample Set Distribution)") + testEval("log10(uniform(5,8))", "Ok(Sample Set Distribution)") }) describe("dotAdd", () => { diff --git a/packages/squiggle-lang/__tests__/JS__Test.ts b/packages/squiggle-lang/__tests__/TS/JS_test.ts similarity index 77% rename from packages/squiggle-lang/__tests__/JS__Test.ts rename to packages/squiggle-lang/__tests__/TS/JS_test.ts index 72f68ba4..1974dee6 100644 --- a/packages/squiggle-lang/__tests__/JS__Test.ts +++ b/packages/squiggle-lang/__tests__/TS/JS_test.ts @@ -4,7 +4,7 @@ import { resultMap, squiggleExpression, errorValueToString, -} from "../src/js/index"; +} from "../../src/js/index"; let testRun = (x: string): squiggleExpression => { let result = run(x, { sampleCount: 100, xyPointLength: 100 }); @@ -46,6 +46,8 @@ describe("Distribution", () => { //It's important that sampleCount is less than 9. If it's more, than that will create randomness //Also, note, the value should be created using makeSampleSetDist() later on. let env = { sampleCount: 8, xyPointLength: 100 }; + let dist1Samples = [3, 4, 5, 6, 6, 7, 10, 15, 30]; + let dist1SampleCount = dist1Samples.length; let dist = new Distribution( { tag: "SampleSet", value: [3, 4, 5, 6, 6, 7, 10, 15, 30] }, env @@ -56,16 +58,19 @@ describe("Distribution", () => { ); test("mean", () => { - expect(dist.mean().value).toBeCloseTo(3.737); + expect(dist.mean().value).toBeCloseTo(9.5555555); }); test("pdf", () => { - expect(dist.pdf(5.0).value).toBeCloseTo(0.0431); + expect(dist.pdf(5.0).value).toBeCloseTo(0.10499097598222966, 1); }); test("cdf", () => { - expect(dist.cdf(5.0).value).toBeCloseTo(0.155); + expect(dist.cdf(5.0).value).toBeCloseTo( + dist1Samples.filter((x) => x <= 5).length / dist1SampleCount, + 1 + ); }); test("inv", () => { - expect(dist.inv(0.5).value).toBeCloseTo(9.458); + expect(dist.inv(0.5).value).toBeCloseTo(6); }); test("toPointSet", () => { expect( @@ -73,7 +78,7 @@ describe("Distribution", () => { ).toEqual(Ok("Point Set Distribution")); }); test("toSparkline", () => { - expect(dist.toSparkline(20).value).toEqual("▁▁▃▅███▆▄▃▂▁▁▂▂▃▂▁▁▁"); + expect(dist.toSparkline(20).value).toEqual("▁▁▃▇█▇▄▂▂▂▁▁▁▁▁▂▂▁▁▁"); }); test("algebraicAdd", () => { expect( @@ -87,6 +92,6 @@ describe("Distribution", () => { resultMap(dist.pointwiseAdd(dist2), (r: Distribution) => r.toSparkline(20) ).value - ).toEqual(Ok("▁▂▅██▅▅▅▆▇█▆▅▃▃▂▂▁▁▁")); + ).toEqual(Ok("▁▂██▃▃▃▃▄▅▄▃▃▂▂▂▁▁▁▁")); }); }); diff --git a/packages/squiggle-lang/__tests__/TS/Jstat_test.ts b/packages/squiggle-lang/__tests__/TS/Jstat_test.ts new file mode 100644 index 00000000..16cb5f6e --- /dev/null +++ b/packages/squiggle-lang/__tests__/TS/Jstat_test.ts @@ -0,0 +1,27 @@ +// import { errorValueToString } from "../../src/js/index"; +import * as fc from "fast-check"; +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) => { + let threeStdevsAboveMean = mean + 3 * stdev; + let squiggleString = `cdf(normal(${mean}, ${stdev}), ${threeStdevsAboveMean})`; + let squiggleResult = testRun(squiggleString); + expect(squiggleResult.value).toBeCloseTo(1); + }) + ); + }); + + 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) => { + let threeStdevsBelowMean = mean - 3 * stdev; + let squiggleString = `cdf(normal(${mean}, ${stdev}), ${threeStdevsBelowMean})`; + let squiggleResult = testRun(squiggleString); + expect(squiggleResult.value).toBeCloseTo(0); + }) + ); + }); +}); diff --git a/packages/squiggle-lang/__tests__/TS/Parser_test.ts b/packages/squiggle-lang/__tests__/TS/Parser_test.ts new file mode 100644 index 00000000..0120ff30 --- /dev/null +++ b/packages/squiggle-lang/__tests__/TS/Parser_test.ts @@ -0,0 +1,52 @@ +import { + run, + squiggleExpression, + errorValue, + result, +} from "../../src/js/index"; +import { testRun } from "./TestHelpers"; +import * as fc from "fast-check"; + +describe("Squiggle's parser is whitespace insensitive", () => { + test("when assigning a distribution to a name and calling that name", () => { + // intersperse varying amounts of whitespace in a squiggle string + let squiggleString = ( + a: string, + b: string, + c: string, + d: string, + e: string, + f: string, + g: string, + h: string + ): string => { + return `theDist${a}=${b}beta(${c}4${d},${e}5e1)${f};${g}theDist${h}`; + }; + let squiggleOutput = testRun( + squiggleString("", "", "", "", "", "", "", "") + ); + + // Add "\n" to this when multiline is introduced. + let whitespaceGen = () => { + return fc.constantFrom("", " ", "\t", " ", " ", " ", " "); + }; + + fc.assert( + fc.property( + whitespaceGen(), + whitespaceGen(), + whitespaceGen(), + whitespaceGen(), + whitespaceGen(), + whitespaceGen(), + whitespaceGen(), + whitespaceGen(), + (a, b, c, d, e, f, g, h) => { + expect(testRun(squiggleString(a, b, c, d, e, f, g, h))).toEqual( + squiggleOutput + ); + } + ) + ); + }); +}); diff --git a/packages/squiggle-lang/__tests__/TS/PointSet_test.ts b/packages/squiggle-lang/__tests__/TS/PointSet_test.ts new file mode 100644 index 00000000..3e74f039 --- /dev/null +++ b/packages/squiggle-lang/__tests__/TS/PointSet_test.ts @@ -0,0 +1,37 @@ +// import { errorValueToString } from "../../src/js/index"; +import { testRun, expectErrorToBeBounded } from "./TestHelpers"; +import * as fc from "fast-check"; + +describe("Mean of mixture is weighted average of means", () => { + test.skip("mx(beta(a,b), lognormal(m,s), [x,y])", () => { + fc.assert( + fc.property( + fc.float({ min: 1e-1 }), // alpha + fc.float({ min: 1 }), // beta + fc.float(), // mu + fc.float({ min: 1e-1 }), // sigma + fc.float({ min: 1e-7 }), + fc.float({ min: 1e-7 }), + (a, b, m, s, x, y) => { + let squiggleString = `mean(mixture(beta(${a},${b}), lognormal(${m},${s}), [${x}, ${y}]))`; + let res = testRun(squiggleString); + let weightDenom = x + y; + let betaWeight = x / weightDenom; + let lognormalWeight = y / weightDenom; + let betaMean = 1 / (1 + b / a); + let lognormalMean = m + s ** 2 / 2; + if (res.tag == "number") { + expectErrorToBeBounded( + res.value, + betaWeight * betaMean + lognormalWeight * lognormalMean, + 1, + 2 + ); + } else { + expect(res.value).toEqual("some error message"); + } + } + ) + ); + }); +}); diff --git a/packages/squiggle-lang/__tests__/TS/SampleSet_test.ts b/packages/squiggle-lang/__tests__/TS/SampleSet_test.ts new file mode 100644 index 00000000..a617010b --- /dev/null +++ b/packages/squiggle-lang/__tests__/TS/SampleSet_test.ts @@ -0,0 +1,214 @@ +import { Distribution } from "../../src/js/index"; +import { expectErrorToBeBounded, failDefault } from "./TestHelpers"; +import * as fc from "fast-check"; + +// Beware: float64Array makes it appear in an infinite loop. +let arrayGen = () => + fc.float32Array({ + minLength: 10, + maxLength: 10000, + noDefaultInfinity: true, + noNaN: true, + }); + +describe("cumulative density function", () => { + let n = 10000; + + // We should fix this. + test.skip("'s codomain is bounded above", () => { + fc.assert( + fc.property(arrayGen(), fc.float(), (xs_, x) => { + let xs = Array.from(xs_); + // Should compute with squiggle strings once interpreter has `sample` + let dist = new Distribution( + { tag: "SampleSet", value: xs }, + { sampleCount: n, xyPointLength: 100 } + ); + let cdfValue = dist.cdf(x).value; + let epsilon = 5e-7; + expect(cdfValue).toBeLessThanOrEqual(1 + epsilon); + }) + ); + }); + + test("'s codomain is bounded below", () => { + fc.assert( + fc.property(arrayGen(), fc.float(), (xs_, x) => { + let xs = Array.from(xs_); + // Should compute with squiggle strings once interpreter has `sample` + let dist = new Distribution( + { tag: "SampleSet", value: xs }, + { sampleCount: n, xyPointLength: 100 } + ); + let cdfValue = dist.cdf(x).value; + expect(cdfValue).toBeGreaterThanOrEqual(0); + }) + ); + }); + + // This may not be true due to KDE estimating there to be mass above the + // highest value. These tests fail + test.skip("at the highest number in the sample is close to 1", () => { + fc.assert( + fc.property(arrayGen(), (xs_) => { + let xs = Array.from(xs_); + let max = Math.max(...xs); + // Should compute with squiggle strings once interpreter has `sample` + let dist = new Distribution( + { tag: "SampleSet", value: xs }, + { sampleCount: n, xyPointLength: 100 } + ); + let cdfValue = dist.cdf(max).value; + expect(cdfValue).toBeCloseTo(1.0, 2); + }) + ); + }); + + // I may simply be mistaken about the math here. + test.skip("at the lowest number in the distribution is within epsilon of 0", () => { + fc.assert( + fc.property(arrayGen(), (xs_) => { + let xs = Array.from(xs_); + let min = Math.min(...xs); + // Should compute with squiggle strings once interpreter has `sample` + let dist = new Distribution( + { tag: "SampleSet", value: xs }, + { sampleCount: n, xyPointLength: 100 } + ); + let cdfValue = dist.cdf(min).value; + let max = Math.max(...xs); + let epsilon = 5e-3; + if (max - min < epsilon) { + expect(cdfValue).toBeGreaterThan(4 * epsilon); + } else { + expect(cdfValue).toBeLessThan(4 * epsilon); + } + }) + ); + }); + + // I believe this is true, but due to bugs can't get the test to pass. + test.skip("is <= 1 everywhere with equality when x is higher than the max", () => { + fc.assert( + fc.property(arrayGen(), fc.float(), (xs_, x) => { + let xs = Array.from(xs_); + let dist = new Distribution( + { tag: "SampleSet", value: xs }, + { sampleCount: n, xyPointLength: 100 } + ); + let cdfValue = dist.cdf(x).value; + let max = Math.max(...xs); + if (x > max) { + let epsilon = (x - max) / x; + expect(cdfValue).toBeGreaterThan(1 * (1 - epsilon)); + } else if (typeof cdfValue == "number") { + expect(Math.round(1e5 * cdfValue) / 1e5).toBeLessThanOrEqual(1); + } else { + failDefault(); + } + }) + ); + }); + + test("is non-negative everywhere with zero when x is lower than the min", () => { + fc.assert( + fc.property(arrayGen(), fc.float(), (xs_, x) => { + let xs = Array.from(xs_); + let dist = new Distribution( + { tag: "SampleSet", value: xs }, + { sampleCount: n, xyPointLength: 100 } + ); + let cdfValue = dist.cdf(x).value; + if (x < Math.min(...xs)) { + expect(cdfValue).toEqual(0); + } else { + expect(cdfValue).toBeGreaterThan(0); + } + }) + ); + }); +}); + +// I no longer believe this is true. +describe("probability density function", () => { + let n = 1000; + + test.skip("assigns to the max at most the weight of the mean", () => { + fc.assert( + fc.property(arrayGen(), (xs_) => { + let xs = Array.from(xs_); + let max = Math.max(...xs); + let mean = xs.reduce((a, b) => a + b, 0.0) / xs.length; + // Should be from squiggleString once interpreter exposes sampleset + let dist = new Distribution( + { tag: "SampleSet", value: xs }, + { sampleCount: n, xyPointLength: 100 } + ); + let pdfValueMean = dist.pdf(mean).value; + let pdfValueMax = dist.pdf(max).value; + if (typeof pdfValueMean == "number" && typeof pdfValueMax == "number") { + expect(pdfValueMax).toBeLessThanOrEqual(pdfValueMean); + } else { + expect(pdfValueMax).toEqual(pdfValueMean); + } + }) + ); + }); +}); + +// // This should be true, but I can't get it to work. +describe("mean is mean", () => { + test.skip("when sampling twice as widely as the input", () => { + fc.assert( + fc.property( + fc.float64Array({ minLength: 10, maxLength: 100000 }), + (xs_) => { + let xs = Array.from(xs_); + let n = xs.length; + let dist = new Distribution( + { tag: "SampleSet", value: xs }, + { sampleCount: 2 * n, xyPointLength: 4 * n } + ); + let mean = dist.mean(); + if (typeof mean.value == "number") { + expectErrorToBeBounded( + mean.value, + xs.reduce((a, b) => a + b, 0.0) / n, + 5e-1, + 1 + ); + } else { + failDefault(); + } + } + ) + ); + }); + + test.skip("when sampling half as widely as the input", () => { + fc.assert( + fc.property( + fc.float64Array({ minLength: 10, maxLength: 100000 }), + (xs_) => { + let xs = Array.from(xs_); + let n = xs.length; + let dist = new Distribution( + { tag: "SampleSet", value: xs }, + { sampleCount: Math.floor(n / 2), xyPointLength: 4 * n } + ); + let mean = dist.mean(); + if (typeof mean.value == "number") { + expectErrorToBeBounded( + mean.value, + xs.reduce((a, b) => a + b, 0.0) / n, + 5e-1, + 1 + ); + } else { + failDefault(); + } + } + ) + ); + }); +}); diff --git a/packages/squiggle-lang/__tests__/TS/Scalars_test.ts b/packages/squiggle-lang/__tests__/TS/Scalars_test.ts new file mode 100644 index 00000000..261b74d1 --- /dev/null +++ b/packages/squiggle-lang/__tests__/TS/Scalars_test.ts @@ -0,0 +1,33 @@ +// import { errorValueToString } from "../../src/js/index"; +import { testRun } from "./TestHelpers"; +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) => { + 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)); + } + }) + ); + }); + + test("in the case of addition (with assignment)", () => { + fc.assert( + fc.property(fc.float(), fc.float(), fc.float(), (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); + }) + ); + }); +}); diff --git a/packages/squiggle-lang/__tests__/TS/Symbolic_test.ts b/packages/squiggle-lang/__tests__/TS/Symbolic_test.ts new file mode 100644 index 00000000..b949a513 --- /dev/null +++ b/packages/squiggle-lang/__tests__/TS/Symbolic_test.ts @@ -0,0 +1,22 @@ +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) => { + if (!(x < y && y < z)) { + try { + let squiggleResult = testRun(`mean(triangular(${x},${y},${z}))`); + expect(squiggleResult.value).toBeCloseTo((x + y + z) / 3); + } catch (err) { + expect((err as Error).message).toEqual( + "Expected squiggle expression to evaluate but got error: Distribution Math Error: Triangular values must be increasing order." + ); + } + } + }) + ); + }); +}); diff --git a/packages/squiggle-lang/__tests__/TS/TestHelpers.ts b/packages/squiggle-lang/__tests__/TS/TestHelpers.ts new file mode 100644 index 00000000..3d4153ef --- /dev/null +++ b/packages/squiggle-lang/__tests__/TS/TestHelpers.ts @@ -0,0 +1,45 @@ +import { + run, + // Distribution, + squiggleExpression, + errorValueToString, + // errorValue, + // result, +} from "../../src/js/index"; + +export function testRun(x: string): squiggleExpression { + let squiggleResult = run(x, { sampleCount: 1000, xyPointLength: 100 }); + // return squiggleResult.value + if (squiggleResult.tag === "Ok") { + return squiggleResult.value; + } else { + throw new Error( + `Expected squiggle expression to evaluate but got error: ${errorValueToString( + squiggleResult.value + )}` + ); + } +} + +export function failDefault() { + expect("be reached").toBe("codepath should never"); +} + +/** + * This appears also in `TestHelpers.res`. According to https://www.math.net/percent-error, it computes + * absolute error when numerical stability concerns make me not want to compute relative error. + * */ +export function expectErrorToBeBounded( + received: number, + expected: number, + epsilon: number, + digits: number +) { + let distance = Math.abs(received - expected); + let expectedAbs = Math.abs(expected); + let normalizingDenom = Math.max(expectedAbs, 1); + let error = distance / normalizingDenom; + expect(Math.round(10 ** digits * error) / 10 ** digits).toBeLessThanOrEqual( + epsilon + ); +} diff --git a/packages/squiggle-lang/__tests__/TestHelpers.res b/packages/squiggle-lang/__tests__/TestHelpers.res index 5147857b..cbb3258d 100644 --- a/packages/squiggle-lang/__tests__/TestHelpers.res +++ b/packages/squiggle-lang/__tests__/TestHelpers.res @@ -30,8 +30,8 @@ let {toFloat, toDist, toString, toError, fmap} = module(DistributionOperation.Ou let fnImage = (theFn, inps) => Js.Array.map(theFn, inps) let env: DistributionOperation.env = { - sampleCount: 10000, - xyPointLength: 1000, + sampleCount: MagicNumbers.Environment.defaultSampleCount, + xyPointLength: MagicNumbers.Environment.defaultXYPointLength, } let run = DistributionOperation.run(~env) diff --git a/packages/squiggle-lang/benchmark/conversion_tests.ts b/packages/squiggle-lang/benchmark/conversion_tests.ts new file mode 100644 index 00000000..7cdd72cc --- /dev/null +++ b/packages/squiggle-lang/benchmark/conversion_tests.ts @@ -0,0 +1,46 @@ +import { distributions, generateInt, generateFloatRange } from "./generators"; +import { test, expectEqual } from "./lib"; + +let checkDistributionSame = ( + distribution: string, + operation: (arg: string) => string +): void => { + expectEqual( + operation(distribution), + operation(`toPointSet(${distribution})`) + ); + expectEqual( + operation(distribution), + operation(`toSampleSet(${distribution})`) + ); +}; + +Object.entries(distributions).map(([key, generator]) => { + let distribution = generator(); + test(`mean is the same for ${key} distribution under all distribution types`, () => + checkDistributionSame(distribution, (d: string) => `mean(${d})`)); + + test(`cdf is the same for ${key} distribution under all distribution types`, () => { + let cdf_value = generateInt(); + checkDistributionSame( + distribution, + (d: string) => `cdf(${d}, ${cdf_value})` + ); + }); + + test(`pdf is the same for ${key} distribution under all distribution types`, () => { + let pdf_value = generateInt(); + checkDistributionSame( + distribution, + (d: string) => `pdf(${d}, ${pdf_value})` + ); + }); + + test(`inv is the same for ${key} distribution under all distribution types`, () => { + let inv_value = generateFloatRange(0, 1); + checkDistributionSame( + distribution, + (d: string) => `inv(${d}, ${inv_value})` + ); + }); +}); diff --git a/packages/squiggle-lang/benchmark/generators.ts b/packages/squiggle-lang/benchmark/generators.ts new file mode 100644 index 00000000..e3949e9b --- /dev/null +++ b/packages/squiggle-lang/benchmark/generators.ts @@ -0,0 +1,48 @@ +export let generateFloatRange = (min: number, max: number): number => + Math.random() * (max - min) + min; +export let generateIntRange = (min: number, max: number): number => + Math.floor(generateFloatRange(min, max)); + +export let generateIntMin = (min: number): number => generateIntRange(min, 100); + +export let generateInt = (): number => generateIntMin(-100); + +let generatePositiveInt = (): number => generateIntMin(1); + +export let generateNormal = (): string => + `normal(${generateInt()}, ${generatePositiveInt()})`; + +export let generateBeta = (): string => + `beta(${generatePositiveInt()}, ${generatePositiveInt()})`; + +export let generateLognormal = (): string => + `lognormal(${generateInt()}, ${generatePositiveInt()})`; + +export let generateExponential = (): string => + `exponential(${generatePositiveInt()})`; + +export let generateUniform = (): string => { + let a = generateInt(); + let b = generateIntMin(a + 1); + return `uniform(${a}, ${b})`; +}; +export let generateCauchy = (): string => { + return `cauchy(${generateInt()}, ${generatePositiveInt()})`; +}; + +export let generateTriangular = (): string => { + let a = generateInt(); + let b = generateIntMin(a + 1); + let c = generateIntMin(b + 1); + return `triangular(${a}, ${b}, ${c})`; +}; + +export let distributions: { [key: string]: () => string } = { + normal: generateNormal, + beta: generateBeta, + lognormal: generateLognormal, + exponential: generateExponential, + triangular: generateTriangular, + cauchy: generateCauchy, + uniform: generateUniform, +}; diff --git a/packages/squiggle-lang/benchmark/lib.ts b/packages/squiggle-lang/benchmark/lib.ts new file mode 100644 index 00000000..1133640c --- /dev/null +++ b/packages/squiggle-lang/benchmark/lib.ts @@ -0,0 +1,42 @@ +import { run, squiggleExpression, errorValueToString } from "../src/js/index"; +import * as chalk from "chalk"; + +let testRun = (x: string): squiggleExpression => { + let result = run(x, { sampleCount: 100, xyPointLength: 100 }); + if (result.tag === "Ok") { + return result.value; + } else { + throw Error( + "Expected squiggle expression to evaluate but got error: " + + errorValueToString(result.value) + ); + } +}; + +export function test(name: string, fn: () => void) { + console.log(chalk.cyan.bold(name)); + fn(); +} + +export function expectEqual(expression1: string, expression2: string) { + let result1 = testRun(expression1); + let result2 = testRun(expression2); + if (result1.tag === "number" && result2.tag === "number") { + let logloss = Math.log(Math.abs(result1.value - result2.value)); + let isBadLogless = logloss > 1; + console.log(chalk.blue(`${expression1} = ${expression2}`)); + console.log(`${result1.value} = ${result2.value}`); + console.log( + `logloss = ${ + isBadLogless + ? chalk.red(logloss.toFixed(2)) + : chalk.green(logloss.toFixed(2)) + }` + ); + console.log(); + } else { + throw Error( + `Expected both to be number, but got ${result1.tag} and ${result2.tag}` + ); + } +} diff --git a/packages/squiggle-lang/bsconfig.json b/packages/squiggle-lang/bsconfig.json index 4ff06737..e98b3822 100644 --- a/packages/squiggle-lang/bsconfig.json +++ b/packages/squiggle-lang/bsconfig.json @@ -20,12 +20,7 @@ ], "suffix": ".bs.js", "namespace": true, - "bs-dependencies": [ - "@glennsl/rescript-jest", - "@glennsl/bs-json", - "rationale", - "bisect_ppx" - ], + "bs-dependencies": ["@glennsl/rescript-jest", "bisect_ppx"], "gentypeconfig": { "language": "typescript", "module": "commonjs", @@ -37,7 +32,7 @@ }, "refmt": 3, "warnings": { - "number": "+A-42-48-9-30-4-102-20-27-41" + "number": "+A-42-48-9-30-4" }, "ppx-flags": [ ["../../node_modules/bisect_ppx/ppx", "--exclude-files", ".*_test\\.res$$"] diff --git a/packages/squiggle-lang/jest.config.js b/packages/squiggle-lang/jest.config.js index 3347de7c..71babb3c 100644 --- a/packages/squiggle-lang/jest.config.js +++ b/packages/squiggle-lang/jest.config.js @@ -9,5 +9,6 @@ module.exports = { ".*Fixtures.bs.js", "/node_modules/", ".*Helpers.bs.js", + ".*Helpers.ts", ], }; diff --git a/packages/squiggle-lang/lint.sh b/packages/squiggle-lang/lint.sh index 071823c9..2c1f53e5 100755 --- a/packages/squiggle-lang/lint.sh +++ b/packages/squiggle-lang/lint.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Hat tip to @dfalling # https://forum.rescript-lang.org/t/rescript-9-1-how-can-we-format-to-standard-out/1590/2?u=quinn-dougherty @@ -38,4 +38,4 @@ then exit 1 else echo "All files pass lint" -fi \ No newline at end of file +fi diff --git a/packages/squiggle-lang/package.json b/packages/squiggle-lang/package.json index ad6265f8..75583bfc 100644 --- a/packages/squiggle-lang/package.json +++ b/packages/squiggle-lang/package.json @@ -1,22 +1,29 @@ { "name": "@quri/squiggle-lang", - "version": "0.2.2", + "version": "0.2.5", "homepage": "https://squiggle-language.com", + "licence": "MIT", "scripts": { "build": "rescript build -with-deps", "bundle": "webpack", "start": "rescript build -w -with-deps", "clean": "rescript clean", - "test:reducer": "jest --testPathPattern '.*__tests__/Reducer.*'", + "test:reducer": "jest __tests__/Reducer*/", + "benchmark": "ts-node benchmark/conversion_tests.ts", "test": "jest", + "test:ts": "jest __tests__/TS/", + "test:rescript": "jest --modulePathIgnorePatterns=__tests__/TS/*", "test:watch": "jest --watchAll", - "test:quick": "jest --modulePathIgnorePatterns=__tests__/Distributions/Invariants/*", - "coverage": "rm -f *.coverage; yarn clean; BISECT_ENABLE=yes yarn build; yarn test; bisect-ppx-report html", - "coverage:ci": "yarn clean; BISECT_ENABLE=yes yarn build; yarn test; bisect-ppx-report send-to Codecov", + "coverage:rescript": "rm -f *.coverage; yarn clean; BISECT_ENABLE=yes yarn build; yarn test:rescript; bisect-ppx-report html", + "coverage:ts": "yarn clean; yarn build; nyc --reporter=lcov yarn test:ts", + "coverage:rescript:ci": "yarn clean; BISECT_ENABLE=yes yarn build; yarn test:rescript; bisect-ppx-report send-to Codecov", + "coverage:ts:ci": "yarn coverage:ts && codecov", "lint:rescript": "./lint.sh", "lint:prettier": "prettier --check .", "lint": "yarn lint:rescript && yarn lint:prettier", - "format": "rescript format -all && prettier --write .", + "format:rescript": "rescript format -all", + "format:prettier": "prettier --write .", + "format": "yarn format:rescript && yarn format:prettier", "all": "yarn build && yarn bundle && yarn test" }, "keywords": [ @@ -24,26 +31,29 @@ ], "author": "Quantified Uncertainty Research Institute", "license": "MIT", - "dependencies": { - "@glennsl/bs-json": "^5.0.2", + "devDependencies": { + "bisect_ppx": "^2.7.1", "jstat": "^1.9.5", "lodash": "4.17.21", - "mathjs": "10.4.3", - "pdfast": "^0.2.0", - "rationale": "0.2.0", "rescript": "^9.1.4", - "bisect_ppx": "^2.7.1" - }, - "devDependencies": { + "rescript-fast-check": "^1.1.1", "@glennsl/rescript-jest": "^0.9.0", + "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/jest": "^27.4.0", "babel-plugin-transform-es2015-modules-commonjs": "^6.26.2", - "docsify": "^4.12.2", + "chalk": "^5.0.1", + "codecov": "3.8.3", + "fast-check": "2.25.0", "gentype": "^4.3.0", "jest": "^27.5.1", + "mathjs": "10.5.0", "moduleserve": "0.9.1", + "nyc": "^15.1.0", + "pdfast": "^0.2.0", + "reanalyze": "^2.19.0", "ts-jest": "^27.1.4", "ts-loader": "^9.2.8", + "ts-node": "^10.7.0", "typescript": "^4.6.3", "webpack": "^5.72.0", "webpack-cli": "^4.9.2" diff --git a/packages/squiggle-lang/src/js/index.ts b/packages/squiggle-lang/src/js/index.ts index e4c80537..6ff022e0 100644 --- a/packages/squiggle-lang/src/js/index.ts +++ b/packages/squiggle-lang/src/js/index.ts @@ -1,9 +1,4 @@ import * as _ from "lodash"; -import type { - exportEnv, - exportDistribution, -} from "../rescript/ProgramEvaluator.gen"; -export type { exportEnv, exportDistribution }; import { genericDist, samplingParams, @@ -48,7 +43,6 @@ import { Constructors_pointwiseLogarithm, Constructors_pointwisePower, } from "../rescript/Distributions/DistributionOperation/DistributionOperation.gen"; -import { pointSetDistFn } from "../rescript/OldInterpreter/DistPlus.bs"; export type { samplingParams, errorValue }; export let defaultSamplingInputs: samplingParams = { @@ -98,8 +92,7 @@ export type squiggleExpression = | tagged<"record", { [key: string]: squiggleExpression }>; export function run( squiggleString: string, - samplingInputs?: samplingParams, - _environment?: exportEnv + samplingInputs?: samplingParams ): result { let si: samplingParams = samplingInputs ? samplingInputs diff --git a/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.res b/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.res index 44584791..18ee2d6a 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.res +++ b/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.res @@ -1,4 +1,4 @@ -type functionCallInfo = GenericDist_Types.Operation.genericFunctionCallInfo +type functionCallInfo = DistributionTypes.DistributionOperation.genericFunctionCallInfo type genericDist = DistributionTypes.genericDist type error = DistributionTypes.error @@ -120,7 +120,10 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => { (), )->OutputLocal.toDistR - let fromDistFn = (subFnName: GenericDist_Types.Operation.fromDist, dist: genericDist) => { + let fromDistFn = ( + subFnName: DistributionTypes.DistributionOperation.fromDist, + dist: genericDist, + ) => { let response = switch subFnName { | ToFloat(distToFloatOperation) => GenericDist.toFloatOperation(dist, ~toPointSetFn, ~distToFloatOperation) @@ -151,20 +154,26 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => { ->GenericDist.toPointSet(~xyPointLength, ~sampleCount, ()) ->E.R2.fmap(r => Dist(PointSet(r))) ->OutputLocal.fromResult - | ToDistCombination(Algebraic, _, #Float(_)) => GenDistError(NotYetImplemented) - | ToDistCombination(Algebraic, arithmeticOperation, #Dist(t2)) => + | ToDistCombination(Algebraic(_), _, #Float(_)) => GenDistError(NotYetImplemented) + | ToDistCombination(Algebraic(strategy), arithmeticOperation, #Dist(t2)) => dist - ->GenericDist.algebraicCombination(~toPointSetFn, ~toSampleSetFn, ~arithmeticOperation, ~t2) + ->GenericDist.algebraicCombination( + ~strategy, + ~toPointSetFn, + ~toSampleSetFn, + ~arithmeticOperation, + ~t2, + ) ->E.R2.fmap(r => Dist(r)) ->OutputLocal.fromResult - | ToDistCombination(Pointwise, arithmeticOperation, #Dist(t2)) => + | ToDistCombination(Pointwise, algebraicCombination, #Dist(t2)) => dist - ->GenericDist.pointwiseCombination(~toPointSetFn, ~arithmeticOperation, ~t2) + ->GenericDist.pointwiseCombination(~toPointSetFn, ~algebraicCombination, ~t2) ->E.R2.fmap(r => Dist(r)) ->OutputLocal.fromResult - | ToDistCombination(Pointwise, arithmeticOperation, #Float(float)) => + | ToDistCombination(Pointwise, algebraicCombination, #Float(f)) => dist - ->GenericDist.pointwiseCombinationFloat(~toPointSetFn, ~arithmeticOperation, ~float) + ->GenericDist.pointwiseCombinationFloat(~toPointSetFn, ~algebraicCombination, ~f) ->E.R2.fmap(r => Dist(r)) ->OutputLocal.fromResult } @@ -192,24 +201,24 @@ module Output = { let fmap = ( ~env, input: outputType, - functionCallInfo: GenericDist_Types.Operation.singleParamaterFunction, + functionCallInfo: DistributionTypes.DistributionOperation.singleParamaterFunction, ): outputType => { let newFnCall: result = switch (functionCallInfo, input) { | (FromDist(fromDist), Dist(o)) => Ok(FromDist(fromDist, o)) | (FromFloat(fromDist), Float(o)) => Ok(FromFloat(fromDist, o)) | (_, GenDistError(r)) => Error(r) - | (FromDist(_), _) => Error(Other("Expected dist, got something else")) - | (FromFloat(_), _) => Error(Other("Expected float, got something else")) + | (FromDist(_), _) => Error(OtherError("Expected dist, got something else")) + | (FromFloat(_), _) => Error(OtherError("Expected float, got something else")) } newFnCall->E.R2.fmap(run(~env))->OutputLocal.fromResult } } -// See comment above GenericDist_Types.Constructors to explain the purpose of this module. +// See comment above DistributionTypes.Constructors to explain the purpose of this module. // I tried having another internal module called UsingDists, similar to how its done in -// GenericDist_Types.Constructors. However, this broke GenType for me, so beware. +// DistributionTypes.Constructors. However, this broke GenType for me, so beware. module Constructors = { - module C = GenericDist_Types.Constructors.UsingDists + module C = DistributionTypes.Constructors.UsingDists open OutputLocal let mean = (~env, dist) => C.mean(dist)->run(~env)->toFloatR let sample = (~env, dist) => C.sample(dist)->run(~env)->toFloatR diff --git a/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.resi b/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.resi index 9b21a04e..5ad34354 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.resi +++ b/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.resi @@ -4,7 +4,7 @@ type env = { xyPointLength: int, } -open GenericDist_Types +open DistributionTypes @genType type outputType = @@ -15,15 +15,15 @@ type outputType = | GenDistError(error) @genType -let run: (~env: env, GenericDist_Types.Operation.genericFunctionCallInfo) => outputType +let run: (~env: env, DistributionTypes.DistributionOperation.genericFunctionCallInfo) => outputType let runFromDist: ( ~env: env, - ~functionCallInfo: GenericDist_Types.Operation.fromDist, + ~functionCallInfo: DistributionTypes.DistributionOperation.fromDist, genericDist, ) => outputType let runFromFloat: ( ~env: env, - ~functionCallInfo: GenericDist_Types.Operation.fromDist, + ~functionCallInfo: DistributionTypes.DistributionOperation.fromDist, float, ) => outputType @@ -38,7 +38,7 @@ module Output: { let toBool: t => option let toBoolR: t => result let toError: t => option - let fmap: (~env: env, t, GenericDist_Types.Operation.singleParamaterFunction) => t + let fmap: (~env: env, t, DistributionTypes.DistributionOperation.singleParamaterFunction) => t } module Constructors: { diff --git a/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res b/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res index 4082cb6e..e27a138d 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res +++ b/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res @@ -4,38 +4,62 @@ type genericDist = | SampleSet(SampleSetDist.t) | Symbolic(SymbolicDistTypes.symbolicDist) +type asAlgebraicCombinationStrategy = AsDefault | AsSymbolic | AsMonteCarlo | AsConvolution + @genType type error = | NotYetImplemented | Unreachable | DistributionVerticalShiftIsInvalid + | TooFewSamples | ArgumentError(string) - | Other(string) + | OperationError(Operation.Error.t) + | PointSetConversionError(SampleSetDist.pointsetConversionError) + | SparklineError(PointSetTypes.sparklineError) // This type of error is for when we find a sparkline of a discrete distribution. This should probably at some point be actually implemented + | RequestedStrategyInvalidError(string) + | LogarithmOfDistributionError(string) + | OtherError(string) -module Operation = { - type direction = - | Algebraic - | Pointwise +@genType +module Error = { + type t = error - type arithmeticOperation = [ - | #Add - | #Multiply - | #Subtract - | #Divide - | #Power - | #Logarithm - ] + let fromString = (s: string): t => OtherError(s) - let arithmeticToFn = (arithmetic: arithmeticOperation) => - switch arithmetic { - | #Add => \"+." - | #Multiply => \"*." - | #Subtract => \"-." - | #Power => \"**" - | #Divide => \"/." - | #Logarithm => (a, b) => log(a) /. log(b) + @genType + let toString = (err: error): string => + switch err { + | NotYetImplemented => "Function Not Yet Implemented" + | Unreachable => "Unreachable" + | DistributionVerticalShiftIsInvalid => "Distribution Vertical Shift is Invalid" + | ArgumentError(s) => `Argument Error ${s}` + | LogarithmOfDistributionError(s) => `Logarithm of input error: ${s}` + | TooFewSamples => "Too Few Samples" + | OperationError(err) => Operation.Error.toString(err) + | PointSetConversionError(err) => SampleSetDist.pointsetConversionErrorToString(err) + | SparklineError(err) => PointSetTypes.sparklineErrorToString(err) + | RequestedStrategyInvalidError(err) => `Requested strategy invalid: ${err}` + | OtherError(s) => s } + let resultStringToResultError: result<'a, string> => result<'a, error> = n => + n->E.R2.errMap(r => r->fromString) + + let sampleErrorToDistErr = (err: SampleSetDist.sampleSetError): error => + switch err { + | TooFewSamples => TooFewSamples + } +} + +@genType +module DistributionOperation = { + @genType + type pointsetXSelection = [#Linear | #ByWeight] + + type direction = + | Algebraic(asAlgebraicCombinationStrategy) + | Pointwise + type toFloat = [ | #Cdf(float) | #Inv(float) @@ -43,9 +67,7 @@ module Operation = { | #Mean | #Sample ] -} -module DistributionOperation = { type toDist = | Normalize | ToPointSet @@ -55,15 +77,18 @@ module DistributionOperation = { type toFloatArray = Sample(int) - type fromDist = - | ToFloat(Operation.toFloat) - | ToDist(toDist) - | ToDistCombination( - Operation.direction, - Operation.arithmeticOperation, - [#Dist(genericDist) | #Float(float)], - ) + type toBool = IsNormalized + + type toString = | ToString + | ToSparkline(int) + + type fromDist = + | ToFloat(toFloat) + | ToDist(toDist) + | ToDistCombination(direction, Operation.Algebraic.t, [#Dist(genericDist) | #Float(float)]) + | ToString(toString) + | ToBool(toBool) type singleParamaterFunction = | FromDist(fromDist) @@ -86,8 +111,10 @@ module DistributionOperation = { | ToDist(ToSampleSet(r)) => `toSampleSet(${E.I.toString(r)})` | ToDist(Truncate(_, _)) => `truncate` | ToDist(Inspect) => `inspect` - | ToString => `toString` - | ToDistCombination(Algebraic, _, _) => `algebraic` + | ToString(ToString) => `toString` + | ToString(ToSparkline(n)) => `toSparkline(${E.I.toString(n)})` + | ToBool(IsNormalized) => `isNormalized` + | ToDistCombination(Algebraic(_), _, _) => `algebraic` | ToDistCombination(Pointwise, _, _) => `pointwise` } @@ -97,3 +124,71 @@ module DistributionOperation = { | Mixture(_) => `mixture` } } +module Constructors = { + type t = DistributionOperation.genericFunctionCallInfo + + module UsingDists = { + @genType + let mean = (dist): t => FromDist(ToFloat(#Mean), dist) + let sample = (dist): t => FromDist(ToFloat(#Sample), dist) + let cdf = (dist, x): t => FromDist(ToFloat(#Cdf(x)), dist) + let inv = (dist, x): t => FromDist(ToFloat(#Inv(x)), dist) + let pdf = (dist, x): t => FromDist(ToFloat(#Pdf(x)), dist) + let normalize = (dist): t => FromDist(ToDist(Normalize), dist) + let isNormalized = (dist): t => FromDist(ToBool(IsNormalized), dist) + let toPointSet = (dist): t => FromDist(ToDist(ToPointSet), dist) + let toSampleSet = (dist, r): t => FromDist(ToDist(ToSampleSet(r)), dist) + let truncate = (dist, left, right): t => FromDist(ToDist(Truncate(left, right)), dist) + let inspect = (dist): t => FromDist(ToDist(Inspect), dist) + let toString = (dist): t => FromDist(ToString(ToString), dist) + let toSparkline = (dist, n): t => FromDist(ToString(ToSparkline(n)), dist) + let algebraicAdd = (dist1, dist2: genericDist): t => FromDist( + ToDistCombination(Algebraic(AsDefault), #Add, #Dist(dist2)), + dist1, + ) + let algebraicMultiply = (dist1, dist2): t => FromDist( + ToDistCombination(Algebraic(AsDefault), #Multiply, #Dist(dist2)), + dist1, + ) + let algebraicDivide = (dist1, dist2): t => FromDist( + ToDistCombination(Algebraic(AsDefault), #Divide, #Dist(dist2)), + dist1, + ) + let algebraicSubtract = (dist1, dist2): t => FromDist( + ToDistCombination(Algebraic(AsDefault), #Subtract, #Dist(dist2)), + dist1, + ) + let algebraicLogarithm = (dist1, dist2): t => FromDist( + ToDistCombination(Algebraic(AsDefault), #Logarithm, #Dist(dist2)), + dist1, + ) + let algebraicPower = (dist1, dist2): t => FromDist( + ToDistCombination(Algebraic(AsDefault), #Power, #Dist(dist2)), + dist1, + ) + let pointwiseAdd = (dist1, dist2): t => FromDist( + ToDistCombination(Pointwise, #Add, #Dist(dist2)), + dist1, + ) + let pointwiseMultiply = (dist1, dist2): t => FromDist( + ToDistCombination(Pointwise, #Multiply, #Dist(dist2)), + dist1, + ) + let pointwiseDivide = (dist1, dist2): t => FromDist( + ToDistCombination(Pointwise, #Divide, #Dist(dist2)), + dist1, + ) + let pointwiseSubtract = (dist1, dist2): t => FromDist( + ToDistCombination(Pointwise, #Subtract, #Dist(dist2)), + dist1, + ) + let pointwiseLogarithm = (dist1, dist2): t => FromDist( + ToDistCombination(Pointwise, #Logarithm, #Dist(dist2)), + dist1, + ) + let pointwisePower = (dist1, dist2): t => FromDist( + ToDistCombination(Pointwise, #Power, #Dist(dist2)), + dist1, + ) + } +} diff --git a/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.res b/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.res index beb27980..c19bdf7f 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.res +++ b/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.res @@ -6,6 +6,24 @@ type toSampleSetFn = t => result type scaleMultiplyFn = (t, float) => result type pointwiseAddFn = (t, t) => result +let isPointSet = (t: t) => + switch t { + | PointSet(_) => true + | _ => false + } + +let isSampleSetSet = (t: t) => + switch t { + | SampleSet(_) => true + | _ => false + } + +let isSymbolic = (t: t) => + switch t { + | Symbolic(_) => true + | _ => false + } + let sampleN = (t: t, n) => switch t { | PointSet(r) => PointSetDist.sampleNRendered(n, r) @@ -14,7 +32,7 @@ let sampleN = (t: t, n) => } let toSampleSetDist = (t: t, n) => - SampleSetDist.make(sampleN(t, n))->GenericDist_Types.Error.resultStringToResultError + SampleSetDist.make(sampleN(t, n))->E.R2.errMap(DistributionTypes.Error.sampleErrorToDistErr) let fromFloat = (f: float): t => Symbolic(SymbolicDist.Float.make(f)) @@ -46,18 +64,25 @@ let toFloatOperation = ( ~toPointSetFn: toPointSetFn, ~distToFloatOperation: Operation.distToFloatOperation, ) => { - let symbolicSolution = switch (t: t) { - | Symbolic(r) => - switch SymbolicDist.T.operate(distToFloatOperation, r) { - | Ok(f) => Some(f) - | _ => None - } + let trySymbolicSolution = switch (t: t) { + | Symbolic(r) => SymbolicDist.T.operate(distToFloatOperation, r)->E.R.toOption | _ => None } - switch symbolicSolution { + let trySampleSetSolution = switch ((t: t), distToFloatOperation) { + | (SampleSet(sampleSet), #Mean) => SampleSetDist.mean(sampleSet)->Some + | (SampleSet(sampleSet), #Sample) => SampleSetDist.sample(sampleSet)->Some + | (SampleSet(sampleSet), #Inv(r)) => SampleSetDist.percentile(sampleSet, r)->Some + | _ => None + } + + switch trySymbolicSolution { | Some(r) => Ok(r) - | None => toPointSetFn(t)->E.R2.fmap(PointSetDist.operate(distToFloatOperation)) + | None => + switch trySampleSetSolution { + | Some(r) => Ok(r) + | None => toPointSetFn(t)->E.R2.fmap(PointSetDist.operate(distToFloatOperation)) + } } } @@ -68,8 +93,8 @@ let toPointSet = ( t, ~xyPointLength, ~sampleCount, - ~xSelection: GenericDist_Types.Operation.pointsetXSelection=#ByWeight, - unit, + ~xSelection: DistributionTypes.DistributionOperation.pointsetXSelection=#ByWeight, + (), ): result => { switch (t: t) { | PointSet(pointSet) => Ok(pointSet) @@ -83,7 +108,7 @@ let toPointSet = ( pointSetDistLength: xyPointLength, kernelWidth: None, }, - )->GenericDist_Types.Error.resultStringToResultError + )->E.R2.errMap(x => DistributionTypes.PointSetConversionError(x)) } } @@ -93,18 +118,24 @@ let toPointSet = ( xyPointLength to be a bit longer than the eventual toSparkline downsampling. I chose 3 fairly arbitrarily. */ -let toSparkline = (t: t, ~sampleCount: int, ~bucketCount: int=20, unit): result => +let toSparkline = (t: t, ~sampleCount: int, ~bucketCount: int=20, ()): result => t ->toPointSet(~xSelection=#Linear, ~xyPointLength=bucketCount * 3, ~sampleCount, ()) ->E.R.bind(r => - r->PointSetDist.toSparkline(bucketCount)->GenericDist_Types.Error.resultStringToResultError + r->PointSetDist.toSparkline(bucketCount)->E.R2.errMap(x => DistributionTypes.SparklineError(x)) ) module Truncate = { - let trySymbolicSimplification = (leftCutoff, rightCutoff, t: t): option => + let trySymbolicSimplification = ( + leftCutoff: option, + rightCutoff: option, + t: t, + ): option => switch (leftCutoff, rightCutoff, t) { | (None, None, _) => None - | (lc, rc, Symbolic(#Uniform(u))) if lc < rc => + | (Some(lc), Some(rc), Symbolic(#Uniform(u))) if lc < rc => + Some(Symbolic(#Uniform(SymbolicDist.Uniform.truncate(Some(lc), Some(rc), u)))) + | (lc, rc, Symbolic(#Uniform(u))) => Some(Symbolic(#Uniform(SymbolicDist.Uniform.truncate(lc, rc, u)))) | _ => None } @@ -137,85 +168,190 @@ let truncate = Truncate.run of a new variable that is the result of the operation on A and B. For instance, normal(0, 1) + normal(1, 1) -> normal(1, 2). In general, this is implemented via convolution. - - TODO: It would be useful to be able to pass in a paramater to get this to run either with convolution or monte carlo. */ module AlgebraicCombination = { - let tryAnalyticalSimplification = ( - arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation, - t1: t, - t2: t, - ): option> => - switch (arithmeticOperation, t1, t2) { - | (arithmeticOperation, Symbolic(d1), Symbolic(d2)) => - switch SymbolicDist.T.tryAnalyticalSimplification(d1, d2, arithmeticOperation) { - | #AnalyticalSolution(symbolicDist) => Some(Ok(symbolicDist)) - | #Error(er) => Some(Error(er)) - | #NoSolution => None + module InputValidator = { + /* + It would be good to also do a check to make sure that probability mass for the second + operand, at value 1.0, is 0 (or approximately 0). However, we'd ideally want to check + that both the probability mass and the probability density are greater than zero. + Right now we don't yet have a way of getting probability mass, so I'll leave this for later. + */ + let getLogarithmInputError = (t1: t, t2: t, ~toPointSetFn: toPointSetFn): option => { + let firstOperandIsGreaterThanZero = + toFloatOperation( + t1, + ~toPointSetFn, + ~distToFloatOperation=#Cdf(MagicNumbers.Epsilon.ten), + ) |> E.R.fmap(r => r > 0.) + let secondOperandIsGreaterThanZero = + toFloatOperation( + t2, + ~toPointSetFn, + ~distToFloatOperation=#Cdf(MagicNumbers.Epsilon.ten), + ) |> E.R.fmap(r => r > 0.) + let items = E.A.R.firstErrorOrOpen([ + firstOperandIsGreaterThanZero, + secondOperandIsGreaterThanZero, + ]) + switch items { + | Error(r) => Some(r) + | Ok([true, _]) => + Some(LogarithmOfDistributionError("First input must be completely greater than 0")) + | Ok([false, true]) => + Some(LogarithmOfDistributionError("Second input must be completely greater than 0")) + | Ok([false, false]) => None + | Ok(_) => Some(Unreachable) } - | _ => None } - let runConvolution = ( - toPointSet: toPointSetFn, - arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation, - t1: t, - t2: t, - ) => - E.R.merge(toPointSet(t1), toPointSet(t2))->E.R2.fmap(((a, b)) => - PointSetDist.combineAlgebraically(arithmeticOperation, a, b) - ) - - let runMonteCarlo = ( - toSampleSet: toSampleSetFn, - arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation, - t1: t, - t2: t, - ) => { - let fn = Operation.Algebraic.toFn(arithmeticOperation) - E.R.merge(toSampleSet(t1), toSampleSet(t2)) - ->E.R.bind(((t1, t2)) => { - SampleSetDist.map2(~fn, ~t1, ~t2)->GenericDist_Types.Error.resultStringToResultError - }) - ->E.R2.fmap(r => DistributionTypes.SampleSet(r)) + let run = (t1: t, t2: t, ~toPointSetFn: toPointSetFn, ~arithmeticOperation): option => { + if arithmeticOperation == #Logarithm { + getLogarithmInputError(t1, t2, ~toPointSetFn) + } else { + None + } + } } - //I'm (Ozzie) really just guessing here, very little idea what's best - let expectedConvolutionCost: t => int = x => - switch x { - | Symbolic(#Float(_)) => 1 - | Symbolic(_) => 1000 - | PointSet(Discrete(m)) => m.xyShape->XYShape.T.length - | PointSet(Mixed(_)) => 1000 - | PointSet(Continuous(_)) => 1000 - | _ => 1000 + module StrategyCallOnValidatedInputs = { + let convolution = ( + toPointSet: toPointSetFn, + arithmeticOperation: Operation.convolutionOperation, + t1: t, + t2: t, + ): result => + E.R.merge(toPointSet(t1), toPointSet(t2)) + ->E.R2.fmap(((a, b)) => PointSetDist.combineAlgebraically(arithmeticOperation, a, b)) + ->E.R2.fmap(r => DistributionTypes.PointSet(r)) + + let monteCarlo = ( + toSampleSet: toSampleSetFn, + arithmeticOperation: Operation.algebraicOperation, + t1: t, + t2: t, + ): result => { + let fn = Operation.Algebraic.toFn(arithmeticOperation) + E.R.merge(toSampleSet(t1), toSampleSet(t2)) + ->E.R.bind(((t1, t2)) => { + SampleSetDist.map2(~fn, ~t1, ~t2)->E.R2.errMap(x => DistributionTypes.OperationError(x)) + }) + ->E.R2.fmap(r => DistributionTypes.SampleSet(r)) } - let chooseConvolutionOrMonteCarlo = (t2: t, t1: t) => - expectedConvolutionCost(t1) * expectedConvolutionCost(t2) > 10000 - ? #CalculateWithMonteCarlo - : #CalculateWithConvolution + let symbolic = ( + arithmeticOperation: Operation.algebraicOperation, + t1: t, + t2: t, + ): SymbolicDistTypes.analyticalSimplificationResult => { + switch (t1, t2) { + | (DistributionTypes.Symbolic(d1), DistributionTypes.Symbolic(d2)) => + SymbolicDist.T.tryAnalyticalSimplification(d1, d2, arithmeticOperation) + | _ => #NoSolution + } + } + } + + module StrategyChooser = { + type specificStrategy = [#AsSymbolic | #AsMonteCarlo | #AsConvolution] + + //I'm (Ozzie) really just guessing here, very little idea what's best + let expectedConvolutionCost: t => int = x => + switch x { + | Symbolic(#Float(_)) => MagicNumbers.OpCost.floatCost + | Symbolic(_) => MagicNumbers.OpCost.symbolicCost + | PointSet(Discrete(m)) => m.xyShape->XYShape.T.length + | PointSet(Mixed(_)) => MagicNumbers.OpCost.mixedCost + | PointSet(Continuous(_)) => MagicNumbers.OpCost.continuousCost + | _ => MagicNumbers.OpCost.wildcardCost + } + + let hasSampleSetDist = (t1: t, t2: t): bool => isSampleSetSet(t1) || isSampleSetSet(t2) + + let convolutionIsFasterThanMonteCarlo = (t1: t, t2: t): bool => + expectedConvolutionCost(t1) * expectedConvolutionCost(t2) < MagicNumbers.OpCost.monteCarloCost + + let preferConvolutionToMonteCarlo = (t1, t2, arithmeticOperation) => { + !hasSampleSetDist(t1, t2) && + Operation.Convolution.canDoAlgebraicOperation(arithmeticOperation) && + convolutionIsFasterThanMonteCarlo(t1, t2) + } + + let run = (~t1: t, ~t2: t, ~arithmeticOperation): specificStrategy => { + switch StrategyCallOnValidatedInputs.symbolic(arithmeticOperation, t1, t2) { + | #AnalyticalSolution(_) + | #Error(_) => + #AsSymbolic + | #NoSolution => + preferConvolutionToMonteCarlo(t1, t2, arithmeticOperation) ? #AsConvolution : #AsMonteCarlo + } + } + } + + let runStrategyOnValidatedInputs = ( + ~t1: t, + ~t2: t, + ~arithmeticOperation, + ~strategy: StrategyChooser.specificStrategy, + ~toPointSetFn: toPointSetFn, + ~toSampleSetFn: toSampleSetFn, + ): result => { + switch strategy { + | #AsMonteCarlo => + StrategyCallOnValidatedInputs.monteCarlo(toSampleSetFn, arithmeticOperation, t1, t2) + | #AsSymbolic => + switch StrategyCallOnValidatedInputs.symbolic(arithmeticOperation, t1, t2) { + | #AnalyticalSolution(symbolicDist) => Ok(Symbolic(symbolicDist)) + | #Error(e) => Error(OperationError(e)) + | #NoSolution => Error(Unreachable) + } + | #AsConvolution => + switch Operation.Convolution.fromAlgebraicOperation(arithmeticOperation) { + | Some(convOp) => StrategyCallOnValidatedInputs.convolution(toPointSetFn, convOp, t1, t2) + | None => Error(Unreachable) + } + } + } let run = ( + ~strategy: DistributionTypes.asAlgebraicCombinationStrategy, t1: t, ~toPointSetFn: toPointSetFn, ~toSampleSetFn: toSampleSetFn, - ~arithmeticOperation, + ~arithmeticOperation: Operation.algebraicOperation, ~t2: t, ): result => { - switch tryAnalyticalSimplification(arithmeticOperation, t1, t2) { - | Some(Ok(symbolicDist)) => Ok(Symbolic(symbolicDist)) - | Some(Error(e)) => Error(Other(e)) - | None => - switch chooseConvolutionOrMonteCarlo(t1, t2) { - | #CalculateWithMonteCarlo => runMonteCarlo(toSampleSetFn, arithmeticOperation, t1, t2) - | #CalculateWithConvolution => - runConvolution( - toPointSetFn, - arithmeticOperation, - t1, - t2, - )->E.R2.fmap(r => DistributionTypes.PointSet(r)) + let invalidOperationError = InputValidator.run(t1, t2, ~arithmeticOperation, ~toPointSetFn) + switch (invalidOperationError, strategy) { + | (Some(e), _) => Error(e) + | (None, AsDefault) => { + let chooseStrategy = StrategyChooser.run(~arithmeticOperation, ~t1, ~t2) + runStrategyOnValidatedInputs( + ~t1, + ~t2, + ~strategy=chooseStrategy, + ~arithmeticOperation, + ~toPointSetFn, + ~toSampleSetFn, + ) + } + | (None, AsMonteCarlo) => + StrategyCallOnValidatedInputs.monteCarlo(toSampleSetFn, arithmeticOperation, t1, t2) + | (None, AsSymbolic) => + switch StrategyCallOnValidatedInputs.symbolic(arithmeticOperation, t1, t2) { + | #AnalyticalSolution(symbolicDist) => Ok(Symbolic(symbolicDist)) + | #NoSolution => Error(RequestedStrategyInvalidError(`No analytic solution for inputs`)) + | #Error(err) => Error(OperationError(err)) + } + | (None, AsConvolution) => + switch Operation.Convolution.fromAlgebraicOperation(arithmeticOperation) { + | None => { + let errString = `Convolution not supported for ${Operation.Algebraic.toString( + arithmeticOperation, + )}` + Error(RequestedStrategyInvalidError(errString)) + } + | Some(convOp) => StrategyCallOnValidatedInputs.convolution(toPointSetFn, convOp, t1, t2) } } } @@ -227,40 +363,36 @@ let algebraicCombination = AlgebraicCombination.run let pointwiseCombination = ( t1: t, ~toPointSetFn: toPointSetFn, - ~arithmeticOperation, + ~algebraicCombination: Operation.algebraicOperation, ~t2: t, ): result => { - E.R.merge(toPointSetFn(t1), toPointSetFn(t2)) - ->E.R2.fmap(((t1, t2)) => - PointSetDist.combinePointwise( - GenericDist_Types.Operation.arithmeticToFn(arithmeticOperation), - t1, - t2, - ) + E.R.merge(toPointSetFn(t1), toPointSetFn(t2))->E.R.bind(((t1, t2)) => + PointSetDist.combinePointwise(Operation.Algebraic.toFn(algebraicCombination), t1, t2) + ->E.R2.fmap(r => DistributionTypes.PointSet(r)) + ->E.R2.errMap(err => DistributionTypes.OperationError(err)) ) - ->E.R2.fmap(r => DistributionTypes.PointSet(r)) } let pointwiseCombinationFloat = ( t: t, ~toPointSetFn: toPointSetFn, - ~arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation, - ~float: float, + ~algebraicCombination: Operation.algebraicOperation, + ~f: float, ): result => { - let m = switch arithmeticOperation { + let m = switch algebraicCombination { | #Add | #Subtract => Error(DistributionTypes.DistributionVerticalShiftIsInvalid) | (#Multiply | #Divide | #Power | #Logarithm) as arithmeticOperation => - toPointSetFn(t)->E.R2.fmap(t => { + toPointSetFn(t)->E.R.bind(t => { //TODO: Move to PointSet codebase let fn = (secondary, main) => Operation.Scale.toFn(arithmeticOperation, main, secondary) let integralSumCacheFn = Operation.Scale.toIntegralSumCacheFn(arithmeticOperation) let integralCacheFn = Operation.Scale.toIntegralCacheFn(arithmeticOperation) - PointSetDist.T.mapY( - ~integralSumCacheFn=integralSumCacheFn(float), - ~integralCacheFn=integralCacheFn(float), - ~fn=fn(float), + PointSetDist.T.mapYResult( + ~integralSumCacheFn=integralSumCacheFn(f), + ~integralCacheFn=integralCacheFn(f), + ~fn=fn(f), t, - ) + )->E.R2.errMap(x => DistributionTypes.OperationError(x)) }) } m->E.R2.fmap(r => DistributionTypes.PointSet(r)) @@ -274,7 +406,7 @@ let mixture = ( ~pointwiseAddFn: pointwiseAddFn, ) => { if E.A.length(values) == 0 { - Error(DistributionTypes.Other("Mixture error: mixture must have at least 1 element")) + Error(DistributionTypes.OtherError("Mixture error: mixture must have at least 1 element")) } else { let totalWeight = values->E.A2.fmap(E.Tuple2.second)->E.A.Floats.sum let properlyWeightedValues = diff --git a/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.resi b/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.resi index c49d18b5..e91803e2 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.resi +++ b/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.resi @@ -1,5 +1,5 @@ -type t = GenericDist_Types.genericDist -type error = GenericDist_Types.error +type t = DistributionTypes.genericDist +type error = DistributionTypes.error type toPointSetFn = t => result type toSampleSetFn = t => result type scaleMultiplyFn = (t, float) => result @@ -28,7 +28,7 @@ let toPointSet: ( t, ~xyPointLength: int, ~sampleCount: int, - ~xSelection: GenericDist_Types.Operation.pointsetXSelection=?, + ~xSelection: DistributionTypes.DistributionOperation.pointsetXSelection=?, unit, ) => result let toSparkline: (t, ~sampleCount: int, ~bucketCount: int=?, unit) => result @@ -42,25 +42,26 @@ let truncate: ( ) => result let algebraicCombination: ( + ~strategy: DistributionTypes.asAlgebraicCombinationStrategy, t, ~toPointSetFn: toPointSetFn, ~toSampleSetFn: toSampleSetFn, - ~arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation, + ~arithmeticOperation: Operation.algebraicOperation, ~t2: t, ) => result let pointwiseCombination: ( t, ~toPointSetFn: toPointSetFn, - ~arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation, + ~algebraicCombination: Operation.algebraicOperation, ~t2: t, ) => result let pointwiseCombinationFloat: ( t, ~toPointSetFn: toPointSetFn, - ~arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation, - ~float: float, + ~algebraicCombination: Operation.algebraicOperation, + ~f: float, ) => result let mixture: ( @@ -68,3 +69,6 @@ let mixture: ( ~scaleMultiplyFn: scaleMultiplyFn, ~pointwiseAddFn: pointwiseAddFn, ) => result + +let isSymbolic: t => bool +let isPointSet: t => bool diff --git a/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist_Types.res b/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist_Types.res deleted file mode 100644 index b045a6d5..00000000 --- a/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist_Types.res +++ /dev/null @@ -1,194 +0,0 @@ -type genericDist = DistributionTypes.genericDist -@genType -type error = DistributionTypes.error - -@genType -module Error = { - type t = error - - let fromString = (s: string): t => Other(s) - - @genType - let toString = (x: t) => { - switch x { - | NotYetImplemented => "Not Yet Implemented" - | Unreachable => "Unreachable" - | DistributionVerticalShiftIsInvalid => "Distribution Vertical Shift Is Invalid" - | ArgumentError(x) => `Argument Error: ${x}` - | Other(s) => s - } - } - - let resultStringToResultError: result<'a, string> => result<'a, error> = n => - n->E.R2.errMap(r => r->fromString->Error) -} - -module Operation = { - type direction = - | Algebraic - | Pointwise - - type arithmeticOperation = [ - | #Add - | #Multiply - | #Subtract - | #Divide - | #Power - | #Logarithm - ] - - let arithmeticToFn = (arithmetic: arithmeticOperation) => - switch arithmetic { - | #Add => \"+." - | #Multiply => \"*." - | #Subtract => \"-." - | #Power => \"**" - | #Divide => \"/." - | #Logarithm => (a, b) => log(a) /. log(b) - } - - type toFloat = [ - | #Cdf(float) - | #Inv(float) - | #Mean - | #Pdf(float) - | #Sample - ] - - @genType - type pointsetXSelection = [#Linear | #ByWeight] - - type toDist = - | Normalize - | ToPointSet - | ToSampleSet(int) - | Truncate(option, option) - | Inspect - - type toFloatArray = Sample(int) - - type toString = - | ToString - | ToSparkline(int) - - type toBool = IsNormalized - - type fromDist = - | ToFloat(toFloat) - | ToDist(toDist) - | ToDistCombination(direction, arithmeticOperation, [#Dist(genericDist) | #Float(float)]) - | ToString(toString) - | ToBool(toBool) - - type singleParamaterFunction = - | FromDist(fromDist) - | FromFloat(fromDist) - - @genType - type genericFunctionCallInfo = - | FromDist(fromDist, genericDist) - | FromFloat(fromDist, float) - | Mixture(array<(genericDist, float)>) - - let distCallToString = (distFunction: fromDist): string => - switch distFunction { - | ToFloat(#Cdf(r)) => `cdf(${E.Float.toFixed(r)})` - | ToFloat(#Inv(r)) => `inv(${E.Float.toFixed(r)})` - | ToFloat(#Mean) => `mean` - | ToFloat(#Pdf(r)) => `pdf(${E.Float.toFixed(r)})` - | ToFloat(#Sample) => `sample` - | ToDist(Normalize) => `normalize` - | ToDist(ToPointSet) => `toPointSet` - | ToDist(ToSampleSet(r)) => `toSampleSet(${E.I.toString(r)})` - | ToDist(Truncate(_, _)) => `truncate` - | ToDist(Inspect) => `inspect` - | ToString(ToString) => `toString` - | ToString(ToSparkline(n)) => `toSparkline(${E.I.toString(n)})` - | ToBool(IsNormalized) => `isNormalized` - | ToDistCombination(Algebraic, _, _) => `algebraic` - | ToDistCombination(Pointwise, _, _) => `pointwise` - } - - let toString = (d: genericFunctionCallInfo): string => - switch d { - | FromDist(f, _) | FromFloat(f, _) => distCallToString(f) - | Mixture(_) => `mixture` - } -} - -/* -It can be a pain to write out the genericFunctionCallInfo. The constructors help with this. -This code only covers some of genericFunctionCallInfo: many arguments could be called with either a -float or a distribution. The "UsingDists" module assumes that everything is a distribution. -This is a tradeoff of some generality in order to get a bit more simplicity. -I could see having a longer interface in the future, but it could be messy. -Like, algebraicAddDistFloat vs. algebraicAddDistDist -*/ -module Constructors = { - type t = Operation.genericFunctionCallInfo - - module UsingDists = { - @genType - let mean = (dist): t => FromDist(ToFloat(#Mean), dist) - let sample = (dist): t => FromDist(ToFloat(#Sample), dist) - let cdf = (dist, x): t => FromDist(ToFloat(#Cdf(x)), dist) - let inv = (dist, x): t => FromDist(ToFloat(#Inv(x)), dist) - let pdf = (dist, x): t => FromDist(ToFloat(#Pdf(x)), dist) - let normalize = (dist): t => FromDist(ToDist(Normalize), dist) - let isNormalized = (dist): t => FromDist(ToBool(IsNormalized), dist) - let toPointSet = (dist): t => FromDist(ToDist(ToPointSet), dist) - let toSampleSet = (dist, r): t => FromDist(ToDist(ToSampleSet(r)), dist) - let truncate = (dist, left, right): t => FromDist(ToDist(Truncate(left, right)), dist) - let inspect = (dist): t => FromDist(ToDist(Inspect), dist) - let toString = (dist): t => FromDist(ToString(ToString), dist) - let toSparkline = (dist, n): t => FromDist(ToString(ToSparkline(n)), dist) - let algebraicAdd = (dist1, dist2: genericDist): t => FromDist( - ToDistCombination(Algebraic, #Add, #Dist(dist2)), - dist1, - ) - let algebraicMultiply = (dist1, dist2): t => FromDist( - ToDistCombination(Algebraic, #Multiply, #Dist(dist2)), - dist1, - ) - let algebraicDivide = (dist1, dist2): t => FromDist( - ToDistCombination(Algebraic, #Divide, #Dist(dist2)), - dist1, - ) - let algebraicSubtract = (dist1, dist2): t => FromDist( - ToDistCombination(Algebraic, #Subtract, #Dist(dist2)), - dist1, - ) - let algebraicLogarithm = (dist1, dist2): t => FromDist( - ToDistCombination(Algebraic, #Logarithm, #Dist(dist2)), - dist1, - ) - let algebraicPower = (dist1, dist2): t => FromDist( - ToDistCombination(Algebraic, #Power, #Dist(dist2)), - dist1, - ) - let pointwiseAdd = (dist1, dist2): t => FromDist( - ToDistCombination(Pointwise, #Add, #Dist(dist2)), - dist1, - ) - let pointwiseMultiply = (dist1, dist2): t => FromDist( - ToDistCombination(Pointwise, #Multiply, #Dist(dist2)), - dist1, - ) - let pointwiseDivide = (dist1, dist2): t => FromDist( - ToDistCombination(Pointwise, #Divide, #Dist(dist2)), - dist1, - ) - let pointwiseSubtract = (dist1, dist2): t => FromDist( - ToDistCombination(Pointwise, #Subtract, #Dist(dist2)), - dist1, - ) - let pointwiseLogarithm = (dist1, dist2): t => FromDist( - ToDistCombination(Pointwise, #Logarithm, #Dist(dist2)), - dist1, - ) - let pointwisePower = (dist1, dist2): t => FromDist( - ToDistCombination(Pointwise, #Power, #Dist(dist2)), - dist1, - ) - } -} diff --git a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/AlgebraicShapeCombination.res b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/AlgebraicShapeCombination.res index f8740de9..63600e43 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/AlgebraicShapeCombination.res +++ b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/AlgebraicShapeCombination.res @@ -96,36 +96,25 @@ let toDiscretePointMassesFromTriangulars = ( } let combineShapesContinuousContinuous = ( - op: Operation.algebraicOperation, + op: Operation.convolutionOperation, s1: PointSetTypes.xyShape, s2: PointSetTypes.xyShape, ): PointSetTypes.xyShape => { // if we add the two distributions, we should probably use normal filters. // if we multiply the two distributions, we should probably use lognormal filters. let t1m = toDiscretePointMassesFromTriangulars(s1) - let t2m = switch op { - | #Divide => toDiscretePointMassesFromTriangulars(~inverse=true, s2) - | _ => toDiscretePointMassesFromTriangulars(~inverse=false, s2) - } + let t2m = toDiscretePointMassesFromTriangulars(~inverse=false, s2) let combineMeansFn = switch op { | #Add => (m1, m2) => m1 +. m2 | #Subtract => (m1, m2) => m1 -. m2 | #Multiply => (m1, m2) => m1 *. m2 - | #Divide => (m1, mInv2) => m1 *. mInv2 - | #Power => (m1, mInv2) => m1 ** mInv2 - | #Logarithm => (m1, m2) => log(m1) /. log(m2) } // note: here, mInv2 = mean(1 / t2) ~= 1 / mean(t2) - // TODO: Variances are for exponentatiation or logarithms are almost totally made up and very likely very wrong. - // converts the variances and means of the two inputs into the variance of the output let combineVariancesFn = switch op { | #Add => (v1, v2, _, _) => v1 +. v2 | #Subtract => (v1, v2, _, _) => v1 +. v2 | #Multiply => (v1, v2, m1, m2) => v1 *. v2 +. v1 *. m2 ** 2. +. v2 *. m1 ** 2. - | #Power => (v1, v2, m1, m2) => v1 *. v2 +. v1 *. m2 ** 2. +. v2 *. m1 ** 2. - | #Logarithm => (v1, v2, m1, m2) => v1 *. v2 +. v1 *. m2 ** 2. +. v2 *. m1 ** 2. - | #Divide => (v1, vInv2, m1, mInv2) => v1 *. vInv2 +. v1 *. mInv2 ** 2. +. vInv2 *. m1 ** 2. } // TODO: If operating on two positive-domain distributions, we should take that into account @@ -198,16 +187,20 @@ let toDiscretePointMassesFromDiscrete = (s: PointSetTypes.xyShape): pointMassesW {n: n, masses: masses, means: means, variances: variances} } +type argumentPosition = First | Second + let combineShapesContinuousDiscrete = ( - op: Operation.algebraicOperation, + op: Operation.convolutionOperation, continuousShape: PointSetTypes.xyShape, discreteShape: PointSetTypes.xyShape, + ~discretePosition: argumentPosition, ): PointSetTypes.xyShape => { let t1n = continuousShape |> XYShape.T.length let t2n = discreteShape |> XYShape.T.length // each x pair is added/subtracted - let fn = Operation.Algebraic.toFn(op) + let opFunc = Operation.Convolution.toFn(op) + let fn = discretePosition == First ? (a, b) => opFunc(b, a) : opFunc let outXYShapes: array> = Belt.Array.makeUninitializedUnsafe(t2n) @@ -218,49 +211,56 @@ let combineShapesContinuousDiscrete = ( // creates a new continuous shape for each one of the discrete points, and collects them in outXYShapes. let dxyShape: array<(float, float)> = Belt.Array.makeUninitializedUnsafe(t1n) for i in 0 to t1n - 1 { + // When this operation is flipped (like 1 - normal(5, 2)) then the + // x axis coordinates would all come out the wrong order. So we need + // to fill them out in the opposite direction + let index = discretePosition == First ? t1n - 1 - i : i Belt.Array.set( dxyShape, - i, + index, ( fn(continuousShape.xs[i], discreteShape.xs[j]), continuousShape.ys[i] *. discreteShape.ys[j], ), ) |> ignore - () } Belt.Array.set(outXYShapes, j, dxyShape) |> ignore () } - | #Multiply - | #Power - | #Logarithm - | #Divide => + | #Multiply => for j in 0 to t2n - 1 { // creates a new continuous shape for each one of the discrete points, and collects them in outXYShapes. let dxyShape: array<(float, float)> = Belt.Array.makeUninitializedUnsafe(t1n) for i in 0 to t1n - 1 { + // If this operation would flip the x axis (such as -1 * normal(5, 2)), + // then we want to fill the shape in backwards to ensure all the points + // are still in the right order + let index = discreteShape.xs[j] > 0.0 ? i : t1n - 1 - i Belt.Array.set( dxyShape, - i, + index, ( fn(continuousShape.xs[i], discreteShape.xs[j]), - continuousShape.ys[i] *. discreteShape.ys[j] /. discreteShape.xs[j], + continuousShape.ys[i] *. discreteShape.ys[j] /. Js.Math.abs_float(discreteShape.xs[j]), ), ) |> ignore () } Belt.Array.set(outXYShapes, j, dxyShape) |> ignore - () } } outXYShapes |> E.A.fmap(XYShape.T.fromZippedArray) |> E.A.fold_left( - XYShape.PointwiseCombination.combine( - \"+.", - XYShape.XtoY.continuousInterpolator(#Linear, #UseZero), - ), + (acc, x) => + XYShape.PointwiseCombination.addCombine( + XYShape.XtoY.continuousInterpolator(#Linear, #UseZero), + acc, + x, + ), XYShape.T.empty, ) } + +let isOrdered = (a: XYShape.T.t): bool => E.A.Sorted.Floats.isSorted(a.xs) diff --git a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Continuous.res b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Continuous.res index 4c48df70..d4286387 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Continuous.res +++ b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Continuous.res @@ -87,12 +87,11 @@ let stepwiseToLinear = (t: t): t => // Note: This results in a distribution with as many points as the sum of those in t1 and t2. let combinePointwise = ( ~integralSumCachesFn=(_, _) => None, - ~integralCachesFn: (t, t) => option=(_, _) => None, ~distributionType: PointSetTypes.distributionType=#PDF, - fn: (float, float) => float, + fn: (float, float) => result, t1: PointSetTypes.continuousShape, t2: PointSetTypes.continuousShape, -): PointSetTypes.continuousShape => { +): result => { // If we're adding the distributions, and we know the total of each, then we // can just sum them up. Otherwise, all bets are off. let combinedIntegralSum = Common.combineIntegralSums( @@ -120,9 +119,8 @@ let combinePointwise = ( let interpolator = XYShape.XtoY.continuousInterpolator(t1.interpolation, extrapolation) - make( - ~integralSumCache=combinedIntegralSum, - XYShape.PointwiseCombination.combine(fn, interpolator, t1.xyShape, t2.xyShape), + XYShape.PointwiseCombination.combine(fn, interpolator, t1.xyShape, t2.xyShape)->E.R2.fmap(x => + make(~integralSumCache=combinedIntegralSum, x) ) } @@ -141,18 +139,47 @@ let updateIntegralSumCache = (integralSumCache, t: t): t => { let updateIntegralCache = (integralCache, t: t): t => {...t, integralCache: integralCache} -let reduce = ( +let sum = ( ~integralSumCachesFn: (float, float) => option=(_, _) => None, - ~integralCachesFn: (t, t) => option=(_, _) => None, - fn, continuousShapes, -) => +): t => continuousShapes |> E.A.fold_left( - combinePointwise(~integralSumCachesFn, ~integralCachesFn, fn), + (x, y) => + combinePointwise(~integralSumCachesFn, (a, b) => Ok(a +. b), x, y)->E.R.toExn( + "Addition should never fail", + _, + ), empty, ) -let mapY = (~integralSumCacheFn=_ => None, ~integralCacheFn=_ => None, ~fn, t: t) => +let reduce = ( + ~integralSumCachesFn: (float, float) => option=(_, _) => None, + fn: (float, float) => result, + continuousShapes, +): result => + continuousShapes |> E.A.R.foldM(combinePointwise(~integralSumCachesFn, fn), empty) + +let mapYResult = ( + ~integralSumCacheFn=_ => None, + ~integralCacheFn=_ => None, + ~fn: float => result, + t: t, +): result => + XYShape.T.mapYResult(fn, getShape(t))->E.R2.fmap(x => + make( + ~interpolation=t.interpolation, + ~integralSumCache=t.integralSumCache |> E.O.bind(_, integralSumCacheFn), + ~integralCache=t.integralCache |> E.O.bind(_, integralCacheFn), + x, + ) + ) + +let mapY = ( + ~integralSumCacheFn=_ => None, + ~integralCacheFn=_ => None, + ~fn: float => float, + t: t, +): t => make( ~interpolation=t.interpolation, ~integralSumCache=t.integralSumCache |> E.O.bind(_, integralSumCacheFn), @@ -176,6 +203,7 @@ module T = Dist({ let minX = shapeFn(XYShape.T.minX) let maxX = shapeFn(XYShape.T.maxX) let mapY = mapY + let mapYResult = mapYResult let updateIntegralCache = updateIntegralCache let toDiscreteProbabilityMassFraction = _ => 0.0 let toPointSetDist = (t: t): PointSetTypes.pointSetDist => Continuous(t) @@ -241,15 +269,21 @@ module T = Dist({ XYShape.Analysis.getVarianceDangerously(t, mean, Analysis.getMeanOfSquares) }) +let isNormalized = (t: t): bool => { + let areaUnderIntegral = t |> updateIntegralCache(Some(T.integral(t))) |> T.integralEndY + areaUnderIntegral < 1. +. 1e-7 && areaUnderIntegral > 1. -. 1e-7 +} + let downsampleEquallyOverX = (length, t): t => t |> shapeMap(XYShape.XsConversion.proportionEquallyOverX(length)) /* This simply creates multiple copies of the continuous distribution, scaled and shifted according to each discrete data point, and then adds them all together. */ let combineAlgebraicallyWithDiscrete = ( - op: Operation.algebraicOperation, + op: Operation.convolutionOperation, t1: t, t2: PointSetTypes.discreteShape, + ~discretePosition: AlgebraicShapeCombination.argumentPosition, ) => { let t1s = t1 |> getShape let t2s = t2.xyShape // TODO would like to use Discrete.getShape here, but current file structure doesn't allow for that @@ -266,11 +300,11 @@ let combineAlgebraicallyWithDiscrete = ( op, continuousAsLinear |> getShape, t2s, + ~discretePosition, ) let combinedIntegralSum = switch op { - | #Multiply - | #Divide => + | #Multiply => Common.combineIntegralSums((a, b) => Some(a *. b), t1.integralSumCache, t2.integralSumCache) | _ => None } @@ -280,7 +314,7 @@ let combineAlgebraicallyWithDiscrete = ( } } -let combineAlgebraically = (op: Operation.algebraicOperation, t1: t, t2: t) => { +let combineAlgebraically = (op: Operation.convolutionOperation, t1: t, t2: t) => { let s1 = t1 |> getShape let s2 = t2 |> getShape let t1n = s1 |> XYShape.T.length diff --git a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Discrete.res b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Discrete.res index 3bd22018..fdc921c6 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Discrete.res +++ b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Discrete.res @@ -34,11 +34,6 @@ let lastY = (t: t) => t |> getShape |> XYShape.T.lastY let combinePointwise = ( ~integralSumCachesFn=(_, _) => None, - ~integralCachesFn: ( - PointSetTypes.continuousShape, - PointSetTypes.continuousShape, - ) => option=(_, _) => None, - fn, t1: PointSetTypes.discreteShape, t2: PointSetTypes.discreteShape, ): PointSetTypes.discreteShape => { @@ -54,24 +49,16 @@ let combinePointwise = ( make( ~integralSumCache=combinedIntegralSum, XYShape.PointwiseCombination.combine( - \"+.", + (a, b) => Ok(a +. b), XYShape.XtoY.discreteInterpolator, t1.xyShape, t2.xyShape, - ), + )->E.R.toExn("Addition operation should never fail", _), ) } -let reduce = ( - ~integralSumCachesFn=(_, _) => None, - ~integralCachesFn=(_, _) => None, - fn, - discreteShapes, -): PointSetTypes.discreteShape => - discreteShapes |> E.A.fold_left( - combinePointwise(~integralSumCachesFn, ~integralCachesFn, fn), - empty, - ) +let reduce = (~integralSumCachesFn=(_, _) => None, discreteShapes): PointSetTypes.discreteShape => + discreteShapes |> E.A.fold_left(combinePointwise(~integralSumCachesFn), empty) let updateIntegralSumCache = (integralSumCache, t: t): t => { ...t, @@ -85,7 +72,7 @@ let updateIntegralCache = (integralCache, t: t): t => { /* This multiples all of the data points together and creates a new discrete distribution from the results. Data points at the same xs get added together. It may be a good idea to downsample t1 and t2 before and/or the result after. */ -let combineAlgebraically = (op: Operation.algebraicOperation, t1: t, t2: t): t => { +let combineAlgebraically = (op: Operation.convolutionOperation, t1: t, t2: t): t => { let t1s = t1 |> getShape let t2s = t2 |> getShape let t1n = t1s |> XYShape.T.length @@ -97,7 +84,7 @@ let combineAlgebraically = (op: Operation.algebraicOperation, t1: t, t2: t): t = t2.integralSumCache, ) - let fn = Operation.Algebraic.toFn(op) + let fn = Operation.Convolution.toFn(op) let xToYMap = E.FloatFloatMap.empty() for i in 0 to t1n - 1 { @@ -116,7 +103,26 @@ let combineAlgebraically = (op: Operation.algebraicOperation, t1: t, t2: t): t = make(~integralSumCache=combinedIntegralSum, combinedShape) } -let mapY = (~integralSumCacheFn=_ => None, ~integralCacheFn=_ => None, ~fn, t: t) => +let mapYResult = ( + ~integralSumCacheFn=_ => None, + ~integralCacheFn=_ => None, + ~fn: float => result, + t: t, +): result => + XYShape.T.mapYResult(fn, getShape(t))->E.R2.fmap(x => + make( + ~integralSumCache=t.integralSumCache |> E.O.bind(_, integralSumCacheFn), + ~integralCache=t.integralCache |> E.O.bind(_, integralCacheFn), + x, + ) + ) + +let mapY = ( + ~integralSumCacheFn=_ => None, + ~integralCacheFn=_ => None, + ~fn: float => float, + t: t, +): t => make( ~integralSumCache=t.integralSumCache |> E.O.bind(_, integralSumCacheFn), ~integralCache=t.integralCache |> E.O.bind(_, integralCacheFn), @@ -156,6 +162,7 @@ module T = Dist({ let maxX = shapeFn(XYShape.T.maxX) let toDiscreteProbabilityMassFraction = _ => 1.0 let mapY = mapY + let mapYResult = mapYResult let updateIntegralCache = updateIntegralCache let toPointSetDist = (t: t): PointSetTypes.pointSetDist => Discrete(t) let toContinuous = _ => None diff --git a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Distributions.res b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Distributions.res index 0821611c..407eae85 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Distributions.res +++ b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Distributions.res @@ -9,6 +9,12 @@ module type dist = { ~fn: float => float, t, ) => t + let mapYResult: ( + ~integralSumCacheFn: float => option=?, + ~integralCacheFn: PointSetTypes.continuousShape => option=?, + ~fn: float => result, + t, + ) => result let xToY: (float, t) => PointSetTypes.mixedPoint let toPointSetDist: t => PointSetTypes.pointSetDist let toContinuous: t => option @@ -37,6 +43,7 @@ module Dist = (T: dist) => { let integral = T.integral let xTotalRange = (t: t) => maxX(t) -. minX(t) let mapY = T.mapY + let mapYResult = T.mapYResult let xToY = T.xToY let downsample = T.downsample let toPointSetDist = T.toPointSetDist diff --git a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Mixed.res b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Mixed.res index 9e27057a..4ce2bdd6 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Mixed.res +++ b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Mixed.res @@ -146,8 +146,7 @@ module T = Dist({ let discreteIntegral = Continuous.stepwiseToLinear(Discrete.T.Integral.get(t.discrete)) Continuous.make( - XYShape.PointwiseCombination.combine( - \"+.", + XYShape.PointwiseCombination.addCombine( XYShape.XtoY.continuousInterpolator(#Linear, #UseOutermostPoints), Continuous.getShape(continuousIntegral), Continuous.getShape(discreteIntegral), @@ -161,24 +160,20 @@ module T = Dist({ let integralYtoX = (f, t) => t |> integral |> Continuous.getShape |> XYShape.YtoX.linear(f) - // This pipes all ys (continuous and discrete) through fn. - // If mapY is a linear operation, we might be able to update the integralSumCaches as well; - // if not, they'll be set to None. - let mapY = ( - ~integralSumCacheFn=previousIntegralSum => None, - ~integralCacheFn=previousIntegral => None, - ~fn, + let createMixedFromContinuousDiscrete = ( + ~integralSumCacheFn=_ => None, + ~integralCacheFn=_ => None, t: t, + discrete: PointSetTypes.discreteShape, + continuous: PointSetTypes.continuousShape, ): t => { let yMappedDiscrete: PointSetTypes.discreteShape = - t.discrete - |> Discrete.T.mapY(~fn) + discrete |> Discrete.updateIntegralSumCache(E.O.bind(t.discrete.integralSumCache, integralSumCacheFn)) |> Discrete.updateIntegralCache(E.O.bind(t.discrete.integralCache, integralCacheFn)) let yMappedContinuous: PointSetTypes.continuousShape = - t.continuous - |> Continuous.T.mapY(~fn) + continuous |> Continuous.updateIntegralSumCache( E.O.bind(t.continuous.integralSumCache, integralSumCacheFn), ) @@ -192,6 +187,46 @@ module T = Dist({ } } + // This pipes all ys (continuous and discrete) through fn. + // If mapY is a linear operation, we might be able to update the integralSumCaches as well; + // if not, they'll be set to None. + let mapY = ( + ~integralSumCacheFn=_ => None, + ~integralCacheFn=_ => None, + ~fn: float => float, + t: t, + ): t => { + let discrete = t.discrete |> Discrete.T.mapY(~fn) + let continuous = t.continuous |> Continuous.T.mapY(~fn) + createMixedFromContinuousDiscrete( + ~integralCacheFn, + ~integralSumCacheFn, + t, + discrete, + continuous, + ) + } + + let mapYResult = ( + ~integralSumCacheFn=_ => None, + ~integralCacheFn=_ => None, + ~fn: float => result, + t: t, + ): result => { + E.R.merge( + Discrete.T.mapYResult(~fn, t.discrete), + Continuous.T.mapYResult(~fn, t.continuous), + )->E.R2.fmap(((discreteMapped, continuousMapped)) => { + createMixedFromContinuousDiscrete( + ~integralCacheFn, + ~integralSumCacheFn, + t, + discreteMapped, + continuousMapped, + ) + }) + } + let mean = ({discrete, continuous}: t): float => { let discreteMean = Discrete.T.mean(discrete) let continuousMean = Continuous.T.mean(continuous) @@ -226,7 +261,7 @@ module T = Dist({ } }) -let combineAlgebraically = (op: Operation.algebraicOperation, t1: t, t2: t): t => { +let combineAlgebraically = (op: Operation.convolutionOperation, t1: t, t2: t): t => { // Discrete convolution can cause a huge increase in the number of samples, // so we'll first downsample. @@ -242,9 +277,19 @@ let combineAlgebraically = (op: Operation.algebraicOperation, t1: t, t2: t): t = // continuous (*) continuous => continuous, but also // discrete (*) continuous => continuous (and vice versa). We have to take care of all combos and then combine them: let ccConvResult = Continuous.combineAlgebraically(op, t1.continuous, t2.continuous) - let dcConvResult = Continuous.combineAlgebraicallyWithDiscrete(op, t2.continuous, t1.discrete) - let cdConvResult = Continuous.combineAlgebraicallyWithDiscrete(op, t1.continuous, t2.discrete) - let continuousConvResult = Continuous.reduce(\"+.", [ccConvResult, dcConvResult, cdConvResult]) + let dcConvResult = Continuous.combineAlgebraicallyWithDiscrete( + op, + t2.continuous, + t1.discrete, + ~discretePosition=First, + ) + let cdConvResult = Continuous.combineAlgebraicallyWithDiscrete( + op, + t1.continuous, + t2.discrete, + ~discretePosition=Second, + ) + let continuousConvResult = Continuous.sum([ccConvResult, dcConvResult, cdConvResult]) // ... finally, discrete (*) discrete => discrete, obviously: let discreteConvResult = Discrete.combineAlgebraically(op, t1.discrete, t2.discrete) @@ -266,21 +311,18 @@ let combineAlgebraically = (op: Operation.algebraicOperation, t1: t, t2: t): t = let combinePointwise = ( ~integralSumCachesFn=(_, _) => None, ~integralCachesFn=(_, _) => None, - fn, + fn: (float, float) => result, t1: t, t2: t, -): t => { +): result => { let reducedDiscrete = - [t1, t2] - |> E.A.fmap(toDiscrete) - |> E.A.O.concatSomes - |> Discrete.reduce(~integralSumCachesFn, ~integralCachesFn, fn) + [t1, t2] |> E.A.fmap(toDiscrete) |> E.A.O.concatSomes |> Discrete.reduce(~integralSumCachesFn) let reducedContinuous = [t1, t2] |> E.A.fmap(toContinuous) |> E.A.O.concatSomes - |> Continuous.reduce(~integralSumCachesFn, ~integralCachesFn, fn) + |> Continuous.reduce(~integralSumCachesFn, fn) let combinedIntegralSum = Common.combineIntegralSums( integralSumCachesFn, @@ -293,11 +335,12 @@ let combinePointwise = ( t1.integralCache, t2.integralCache, ) - - make( - ~integralSumCache=combinedIntegralSum, - ~integralCache=combinedIntegral, - ~discrete=reducedDiscrete, - ~continuous=reducedContinuous, + reducedContinuous->E.R2.fmap(continuous => + make( + ~integralSumCache=combinedIntegralSum, + ~integralCache=combinedIntegral, + ~discrete=reducedDiscrete, + ~continuous, + ) ) } diff --git a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetDist.res b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetDist.res index d0668a57..12aa5477 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetDist.res +++ b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetDist.res @@ -16,6 +16,13 @@ let fmap = ((fn1, fn2, fn3), t: t): t => | Continuous(m) => Continuous(fn3(m)) } +let fmapResult = ((fn1, fn2, fn3), t: t): result => + switch t { + | Mixed(m) => fn1(m)->E.R2.fmap(x => PointSetTypes.Mixed(x)) + | Discrete(m) => fn2(m)->E.R2.fmap(x => PointSetTypes.Discrete(x)) + | Continuous(m) => fn3(m)->E.R2.fmap(x => PointSetTypes.Continuous(x)) + } + let toMixed = mapToAll(( m => m, d => @@ -35,13 +42,24 @@ let toMixed = mapToAll(( )) //TODO WARNING: The combineAlgebraicallyWithDiscrete will break for subtraction and division, like, discrete - continous -let combineAlgebraically = (op: Operation.algebraicOperation, t1: t, t2: t): t => +let combineAlgebraically = (op: Operation.convolutionOperation, t1: t, t2: t): t => switch (t1, t2) { | (Continuous(m1), Continuous(m2)) => Continuous.combineAlgebraically(op, m1, m2) |> Continuous.T.toPointSetDist - | (Continuous(m1), Discrete(m2)) - | (Discrete(m2), Continuous(m1)) => - Continuous.combineAlgebraicallyWithDiscrete(op, m1, m2) |> Continuous.T.toPointSetDist + | (Discrete(m1), Continuous(m2)) => + Continuous.combineAlgebraicallyWithDiscrete( + op, + m2, + m1, + ~discretePosition=First, + ) |> Continuous.T.toPointSetDist + | (Continuous(m1), Discrete(m2)) => + Continuous.combineAlgebraicallyWithDiscrete( + op, + m1, + m2, + ~discretePosition=Second, + ) |> Continuous.T.toPointSetDist | (Discrete(m1), Discrete(m2)) => Discrete.combineAlgebraically(op, m1, m2) |> Discrete.T.toPointSetDist | (m1, m2) => Mixed.combineAlgebraically(op, toMixed(m1), toMixed(m2)) |> Mixed.T.toPointSetDist @@ -53,23 +71,28 @@ let combinePointwise = ( PointSetTypes.continuousShape, PointSetTypes.continuousShape, ) => option=(_, _) => None, - fn, + fn: (float, float) => result, t1: t, t2: t, -) => +): result => switch (t1, t2) { | (Continuous(m1), Continuous(m2)) => - PointSetTypes.Continuous( - Continuous.combinePointwise(~integralSumCachesFn, ~integralCachesFn, fn, m1, m2), - ) + Continuous.combinePointwise( + ~integralSumCachesFn, + fn, + m1, + m2, + )->E.R2.fmap(x => PointSetTypes.Continuous(x)) | (Discrete(m1), Discrete(m2)) => - PointSetTypes.Discrete( - Discrete.combinePointwise(~integralSumCachesFn, ~integralCachesFn, fn, m1, m2), - ) + Ok(PointSetTypes.Discrete(Discrete.combinePointwise(~integralSumCachesFn, m1, m2))) | (m1, m2) => - PointSetTypes.Mixed( - Mixed.combinePointwise(~integralSumCachesFn, ~integralCachesFn, fn, toMixed(m1), toMixed(m2)), - ) + Mixed.combinePointwise( + ~integralSumCachesFn, + ~integralCachesFn, + fn, + toMixed(m1), + toMixed(m2), + )->E.R2.fmap(x => PointSetTypes.Mixed(x)) } module T = Dist({ @@ -134,10 +157,8 @@ module T = Dist({ let integralYtoX = f => mapToAll((Mixed.T.Integral.yToX(f), Discrete.T.Integral.yToX(f), Continuous.T.Integral.yToX(f))) let maxX = mapToAll((Mixed.T.maxX, Discrete.T.maxX, Continuous.T.maxX)) - let mapY = ( - ~integralSumCacheFn=previousIntegralSum => None, - ~integralCacheFn=previousIntegral => None, - ~fn, + let mapY = (~integralSumCacheFn=_ => None, ~integralCacheFn=_ => None, ~fn: float => float): ( + t => t ) => fmap(( Mixed.T.mapY(~integralSumCacheFn, ~integralCacheFn, ~fn), @@ -145,6 +166,17 @@ module T = Dist({ Continuous.T.mapY(~integralSumCacheFn, ~integralCacheFn, ~fn), )) + let mapYResult = ( + ~integralSumCacheFn=_ => None, + ~integralCacheFn=_ => None, + ~fn: float => result, + ): (t => result) => + fmapResult(( + Mixed.T.mapYResult(~integralSumCacheFn, ~integralCacheFn, ~fn), + Discrete.T.mapYResult(~integralSumCacheFn, ~integralCacheFn, ~fn), + Continuous.T.mapYResult(~integralSumCacheFn, ~integralCacheFn, ~fn), + )) + let mean = (t: t): float => switch t { | Mixed(m) => Mixed.T.mean(m) @@ -203,8 +235,8 @@ let operate = (distToFloatOp: Operation.distToFloatOperation, s): float => | #Mean => T.mean(s) } -let toSparkline = (t: t, bucketCount) => +let toSparkline = (t: t, bucketCount): result => T.toContinuous(t) ->E.O2.fmap(Continuous.downsampleEquallyOverX(bucketCount)) - ->E.O2.toResult("toContinous Error: Could not convert into continuous distribution") + ->E.O2.toResult(PointSetTypes.CannotSparklineDiscrete) ->E.R2.fmap(r => Continuous.getShape(r).ys->Sparklines.create()) diff --git a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetTypes.res b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetTypes.res index a1a02a67..fcc796ec 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetTypes.res +++ b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetTypes.res @@ -94,3 +94,11 @@ module MixedPoint = { let add = combine2((a, b) => a +. b) } + +@genType +type sparklineError = CannotSparklineDiscrete + +let sparklineErrorToString = (err: sparklineError): string => + switch err { + | CannotSparklineDiscrete => "Cannot find the sparkline of a discrete distribution" + } diff --git a/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/KdeLibrary.js b/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/KdeLibrary.js index 460846e8..9cc75b39 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/KdeLibrary.js +++ b/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/KdeLibrary.js @@ -15,8 +15,18 @@ const samplesToContinuousPdf = ( if (_.isFinite(max)) { _samples = _.filter(_samples, (r) => r < max); } + + // The pdf that's created from this function is not a pdf but a pmf. y values + // being probability mass and not density. + // This is awkward, because our code assumes later that y is a density let pdf = pdfast.create(_samples, { size, width }); - return { xs: pdf.map((r) => r.x), ys: pdf.map((r) => r.y) }; + + // To convert this to a density, we need to find the step size. This is kept + // constant for all y values + let stepSize = pdf[1].x - pdf[0].x; + + // We then adjust the y values to density + return { xs: pdf.map((r) => r.x), ys: pdf.map((r) => r.y / stepSize) }; }; module.exports = { diff --git a/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSetDist.res b/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSetDist.res index fcd4055d..14c66812 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSetDist.res +++ b/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSetDist.res @@ -1,3 +1,24 @@ +@genType +module Error = { + @genType + type sampleSetError = TooFewSamples + + let sampleSetErrorToString = (err: sampleSetError): string => + switch err { + | TooFewSamples => "Too few samples when constructing sample set" + } + + @genType + type pointsetConversionError = TooFewSamplesForConversionToPointSet + + let pointsetConversionErrorToString = (err: pointsetConversionError) => + switch err { + | TooFewSamplesForConversionToPointSet => "Too Few Samples to convert to point set" + } +} + +include Error + /* This is used as a smart constructor. The only way to create a SampleSetDist.t is to call this constructor. @@ -8,7 +29,7 @@ module T: { //When we get a good functional library in TS, we could refactor that out. @genType type t = array - let make: array => result + let make: array => result let get: t => array } = { type t = array @@ -16,7 +37,7 @@ module T: { if E.A.length(a) > 5 { Ok(a) } else { - Error("too small") + Error(TooFewSamples) } let get = (a: t) => a } @@ -31,13 +52,13 @@ some refactoring. */ let toPointSetDist = (~samples: t, ~samplingInputs: SamplingInputs.samplingInputs): result< PointSetTypes.pointSetDist, - string, + pointsetConversionError, > => SampleSetDist_ToPointSet.toPointSetDist( ~samples=get(samples), ~samplingInputs, (), - ).pointSetDist->E.O2.toResult("Failed to convert to PointSetDist") + ).pointSetDist->E.O2.toResult(TooFewSamplesForConversionToPointSet) //Randomly get one sample from the distribution let sample = (t: t): float => { @@ -62,7 +83,28 @@ let sampleN = (t: t, n) => { } //TODO: Figure out what to do if distributions are different lengths. ``zip`` is kind of inelegant for this. -let map2 = (~fn: (float, float) => float, ~t1: t, ~t2: t) => { +let map2 = (~fn: (float, float) => result, ~t1: t, ~t2: t): result< + t, + Operation.Error.t, +> => { let samples = Belt.Array.zip(get(t1), get(t2))->E.A2.fmap(((a, b)) => fn(a, b)) - make(samples) + + // This assertion should never be reached. In order for it to be reached, one + // of the input parameters would need to be a sample set distribution with less + // than 6 samples. Which should be impossible due to the smart constructor. + // I could prove this to the type system (say, creating a {first: float, second: float, ..., fifth: float, rest: array} + // But doing so would take too much time, so I'll leave it as an assertion + E.A.R.firstErrorOrOpen(samples)->E.R2.fmap(x => + E.R.toExn("Input of samples should be larger than 5", make(x)) + ) } + +let mean = t => T.get(t)->E.A.Floats.mean +let geomean = t => T.get(t)->E.A.Floats.geomean +let mode = t => T.get(t)->E.A.Floats.mode +let sum = t => T.get(t)->E.A.Floats.sum +let min = t => T.get(t)->E.A.Floats.min +let max = t => T.get(t)->E.A.Floats.max +let stdev = t => T.get(t)->E.A.Floats.stdev +let variance = t => T.get(t)->E.A.Floats.variance +let percentile = (t, f) => T.get(t)->E.A.Floats.percentile(f) diff --git a/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSetDist_ToPointSet.res b/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSetDist_ToPointSet.res index 59b4fa46..142f10b2 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSetDist_ToPointSet.res +++ b/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSetDist_ToPointSet.res @@ -39,28 +39,6 @@ module Internals = { module T = { type t = array - let splitContinuousAndDiscrete = (sortedArray: t) => { - let continuous = [] - let discrete = E.FloatFloatMap.empty() - Belt.Array.forEachWithIndex(sortedArray, (index, element) => { - let maxIndex = (sortedArray |> Array.length) - 1 - let possiblySimilarElements = switch index { - | 0 => [index + 1] - | n if n == maxIndex => [index - 1] - | _ => [index - 1, index + 1] - } |> Belt.Array.map(_, r => sortedArray[r]) - let hasSimilarElement = Belt.Array.some(possiblySimilarElements, r => r == element) - hasSimilarElement - ? E.FloatFloatMap.increment(element, discrete) - : { - let _ = Js.Array.push(element, continuous) - } - - () - }) - (continuous, discrete) - } - let xWidthToUnitWidth = (samples, outputXYPoints, xWidth) => { let xyPointRange = E.A.Sorted.range(samples) |> E.O.default(0.0) let xyPointWidth = xyPointRange /. float_of_int(outputXYPoints) @@ -83,9 +61,13 @@ let toPointSetDist = ( ~samples: Internals.T.t, ~samplingInputs: SamplingInputs.samplingInputs, (), -) => { +): Internals.Types.outputs => { Array.fast_sort(compare, samples) - let (continuousPart, discretePart) = E.A.Sorted.Floats.split(samples) + let minDiscreteToKeep = MagicNumbers.ToPointSet.minDiscreteToKeep(samples) + let (continuousPart, discretePart) = E.A.Sorted.Floats.splitContinuousAndDiscreteForMinWeight( + samples, + ~minDiscreteWeight=minDiscreteToKeep, + ) let length = samples |> E.A.length |> float_of_int let discrete: PointSetTypes.discreteShape = discretePart @@ -133,9 +115,17 @@ let toPointSetDist = ( ~discrete=Some(discrete), ) + /* + I'm surprised that this doesn't come out normalized. My guess is that the KDE library + we're using is standardizing on something else. If we ever change that library, we should + check to see if we still need to do this. + */ + + let normalizedPointSet = pointSetDist->E.O2.fmap(PointSetDist.T.normalize) + let samplesParse: Internals.Types.outputs = { continuousParseParams: pdf |> E.O.fmap(snd), - pointSetDist: pointSetDist, + pointSetDist: normalizedPointSet, } samplesParse diff --git a/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res b/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res index da722036..997506d9 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res +++ b/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res @@ -52,7 +52,7 @@ module Normal = { switch operation { | #Add => Some(#Normal({mean: n1 +. n2.mean, stdev: n2.stdev})) | #Subtract => Some(#Normal({mean: n1 -. n2.mean, stdev: n2.stdev})) - | #Multiply => Some(#Normal({mean: n1 *. n2.mean, stdev: n1 *. n2.stdev})) + | #Multiply => Some(#Normal({mean: n1 *. n2.mean, stdev: Js.Math.abs_float(n1) *. n2.stdev})) | _ => None } @@ -60,8 +60,8 @@ module Normal = { switch operation { | #Add => Some(#Normal({mean: n1.mean +. n2, stdev: n1.stdev})) | #Subtract => Some(#Normal({mean: n1.mean -. n2, stdev: n1.stdev})) - | #Multiply => Some(#Normal({mean: n1.mean *. n2, stdev: n1.stdev *. n2})) - | #Divide => Some(#Normal({mean: n1.mean /. n2, stdev: n1.stdev /. n2})) + | #Multiply => Some(#Normal({mean: n1.mean *. n2, stdev: n1.stdev *. Js.Math.abs_float(n2)})) + | #Divide => Some(#Normal({mean: n1.mean /. n2, stdev: n1.stdev /. Js.Math.abs_float(n2)})) | _ => None } } @@ -86,7 +86,10 @@ module Exponential = { module Cauchy = { type t = cauchy - let make = (local, scale): symbolicDist => #Cauchy({local: local, scale: scale}) + let make = (local, scale): result => + scale > 0.0 + ? Ok(#Cauchy({local: local, scale: scale})) + : Error("Cauchy distribution scale parameter must larger than 0.") let pdf = (x, t: t) => Jstat.Cauchy.pdf(x, t.local, t.scale) let cdf = (x, t: t) => Jstat.Cauchy.cdf(x, t.local, t.scale) let inv = (p, t: t) => Jstat.Cauchy.inv(p, t.local, t.scale) @@ -377,7 +380,7 @@ module T = { ): analyticalSimplificationResult => switch (d1, d2) { | (#Float(v1), #Float(v2)) => - switch Operation.Algebraic.applyFn(op, v1, v2) { + switch Operation.Algebraic.toFn(op, v1, v2) { | Ok(r) => #AnalyticalSolution(#Float(r)) | Error(n) => #Error(n) } diff --git a/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDistTypes.res b/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDistTypes.res index 4625867d..333e2e63 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDistTypes.res +++ b/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDistTypes.res @@ -45,6 +45,6 @@ type symbolicDist = [ type analyticalSimplificationResult = [ | #AnalyticalSolution(symbolicDist) - | #Error(string) + | #Error(Operation.Error.t) | #NoSolution ] diff --git a/packages/squiggle-lang/src/rescript/MagicNumbers.res b/packages/squiggle-lang/src/rescript/MagicNumbers.res new file mode 100644 index 00000000..124a44f4 --- /dev/null +++ b/packages/squiggle-lang/src/rescript/MagicNumbers.res @@ -0,0 +1,37 @@ +module Math = { + let e = Js.Math._E + let pi = Js.Math._PI +} + +module Epsilon = { + let ten = 1e-10 + let seven = 1e-7 +} + +module Environment = { + let defaultXYPointLength = 1000 + let defaultSampleCount = 10000 +} + +module OpCost = { + let floatCost = 1 + let symbolicCost = 1000 + // Discrete cost is the length of the xyShape + let mixedCost = 1000 + let continuousCost = 1000 + let wildcardCost = 1000 + let monteCarloCost = Environment.defaultSampleCount +} + +module ToPointSet = { + /* + This function chooses the minimum amount of duplicate samples that need + to exist in order for this to be considered discrete. The tricky thing + is that there are some operations that create duplicate continuous samples, + so we can't guarantee that these only will occur because the fundamental + structure is meant to be discrete. I chose this heuristic because I think + it would strike a reasonable trade-off, but I’m really unsure what’s + best right now. + */ + let minDiscreteToKeep = samples => max(20, E.A.length(samples) / 50) +} diff --git a/packages/squiggle-lang/src/rescript/OldInterpreter/AST.res b/packages/squiggle-lang/src/rescript/OldInterpreter/AST.res deleted file mode 100644 index 569912c3..00000000 --- a/packages/squiggle-lang/src/rescript/OldInterpreter/AST.res +++ /dev/null @@ -1,24 +0,0 @@ -open ASTTypes - -let toString = ASTTypes.Node.toString - -let envs = (samplingInputs, environment) => { - samplingInputs: samplingInputs, - environment: environment, - evaluateNode: ASTEvaluator.toLeaf, -} - -let toLeaf = (samplingInputs, environment, node: node) => - ASTEvaluator.toLeaf(envs(samplingInputs, environment), node) - -let toPointSetDist = (samplingInputs, environment, node: node) => - switch toLeaf(samplingInputs, environment, node) { - | Ok(#RenderedDist(pointSetDist)) => Ok(pointSetDist) - | Ok(_) => Error("Rendering failed.") - | Error(e) => Error(e) - } - -let runFunction = (samplingInputs, environment, inputs, fn: ASTTypes.Function.t) => { - let params = envs(samplingInputs, environment) - ASTTypes.Function.run(params, inputs, fn) -} diff --git a/packages/squiggle-lang/src/rescript/OldInterpreter/ASTEvaluator.res b/packages/squiggle-lang/src/rescript/OldInterpreter/ASTEvaluator.res deleted file mode 100644 index cd4c241d..00000000 --- a/packages/squiggle-lang/src/rescript/OldInterpreter/ASTEvaluator.res +++ /dev/null @@ -1,257 +0,0 @@ -open ASTTypes - -type tResult = node => result - -/* Given two random variables A and B, this returns the distribution - of a new variable that is the result of the operation on A and B. - For instance, normal(0, 1) + normal(1, 1) -> normal(1, 2). - In general, this is implemented via convolution. */ -module AlgebraicCombination = { - let tryAnalyticalSimplification = (operation, t1: node, t2: node) => - switch (operation, t1, t2) { - | (operation, #SymbolicDist(d1), #SymbolicDist(d2)) => - switch SymbolicDist.T.tryAnalyticalSimplification(d1, d2, operation) { - | #AnalyticalSolution(symbolicDist) => Ok(#SymbolicDist(symbolicDist)) - | #Error(er) => Error(er) - | #NoSolution => Ok(#AlgebraicCombination(operation, t1, t2)) - } - | _ => Ok(#AlgebraicCombination(operation, t1, t2)) - } - - let combinationByRendering = (evaluationParams, algebraicOp, t1: node, t2: node): result< - node, - string, - > => - E.R.merge( - Node.ensureIsRenderedAndGetShape(evaluationParams, t1), - Node.ensureIsRenderedAndGetShape(evaluationParams, t2), - ) |> E.R.fmap(((a, b)) => #RenderedDist(PointSetDist.combineAlgebraically(algebraicOp, a, b))) - - let nodeScore: node => int = x => - switch x { - | #SymbolicDist(#Float(_)) => 1 - | #SymbolicDist(_) => 1000 - | #RenderedDist(Discrete(m)) => m.xyShape |> XYShape.T.length - | #RenderedDist(Mixed(_)) => 1000 - | #RenderedDist(Continuous(_)) => 1000 - | _ => 1000 - } - - let choose = (t1: node, t2: node) => - nodeScore(t1) * nodeScore(t2) > 10000 ? #Sampling : #Analytical - - let combine = (evaluationParams, algebraicOp, t1: node, t2: node): result => - E.R.merge( - ASTTypes.SamplingDistribution.renderIfIsNotSamplingDistribution(evaluationParams, t1), - ASTTypes.SamplingDistribution.renderIfIsNotSamplingDistribution(evaluationParams, t2), - ) |> E.R.bind(_, ((a, b)) => - switch choose(a, b) { - | #Sampling => - ASTTypes.SamplingDistribution.combineShapesUsingSampling( - evaluationParams, - algebraicOp, - a, - b, - ) - | #Analytical => combinationByRendering(evaluationParams, algebraicOp, a, b) - } - ) - - let operationToLeaf = ( - evaluationParams: evaluationParams, - algebraicOp: Operation.algebraicOperation, - t1: node, - t2: node, - ): result => - algebraicOp - |> tryAnalyticalSimplification(_, t1, t2) - |> E.R.bind(_, x => - switch x { - | #SymbolicDist(_) as t => Ok(t) - | _ => combine(evaluationParams, algebraicOp, t1, t2) - } - ) -} - -module PointwiseCombination = { - //TODO: This is crude and slow. It forces everything to be pointSetDist, even though much - //of the process could happen on symbolic distributions without a conversion to be a pointSetDist. - let pointwiseAdd = (evaluationParams: evaluationParams, t1: node, t2: node) => - switch (Node.render(evaluationParams, t1), Node.render(evaluationParams, t2)) { - | (Ok(#RenderedDist(rs1)), Ok(#RenderedDist(rs2))) => - Ok( - #RenderedDist( - PointSetDist.combinePointwise( - ~integralSumCachesFn=(a, b) => Some(a +. b), - ~integralCachesFn=(a, b) => Some( - Continuous.combinePointwise(~distributionType=#CDF, \"+.", a, b), - ), - \"+.", - rs1, - rs2, - ), - ), - ) - | (Error(e1), _) => Error(e1) - | (_, Error(e2)) => Error(e2) - | _ => Error("Pointwise combination: rendering failed.") - } - - let pointwiseCombine = (fn, evaluationParams: evaluationParams, t1: node, t2: node) => - switch // TODO: construct a function that we can easily sample from, to construct - // a RenderedDist. Use the xMin and xMax of the rendered pointSetDists to tell the sampling function where to look. - // TODO: This should work for symbolic distributions too! - (Node.render(evaluationParams, t1), Node.render(evaluationParams, t2)) { - | (Ok(#RenderedDist(rs1)), Ok(#RenderedDist(rs2))) => - Ok(#RenderedDist(PointSetDist.combinePointwise(fn, rs1, rs2))) - | (Error(e1), _) => Error(e1) - | (_, Error(e2)) => Error(e2) - | _ => Error("Pointwise combination: rendering failed.") - } - - let operationToLeaf = ( - evaluationParams: evaluationParams, - pointwiseOp: Operation.pointwiseOperation, - t1: node, - t2: node, - ) => - switch pointwiseOp { - | #Add => pointwiseAdd(evaluationParams, t1, t2) - | #Multiply => pointwiseCombine(\"*.", evaluationParams, t1, t2) - | #Power => pointwiseCombine(\"**", evaluationParams, t1, t2) - } -} - -module Truncate = { - type simplificationResult = [ - | #Solution(ASTTypes.node) - | #Error(string) - | #NoSolution - ] - - let trySimplification = (leftCutoff, rightCutoff, t): simplificationResult => - switch (leftCutoff, rightCutoff, t) { - | (None, None, t) => #Solution(t) - | (Some(lc), Some(rc), _) if lc > rc => - #Error("Left truncation bound must be smaller than right truncation bound.") - | (lc, rc, #SymbolicDist(#Uniform(u))) => - #Solution(#SymbolicDist(#Uniform(SymbolicDist.Uniform.truncate(lc, rc, u)))) - | _ => #NoSolution - } - - let truncateAsShape = (evaluationParams: evaluationParams, leftCutoff, rightCutoff, t) => - switch // TODO: use named args for xMin/xMax in renderToShape; if we're lucky we can at least get the tail - // of a distribution we otherwise wouldn't get at all - Node.ensureIsRendered(evaluationParams, t) { - | Ok(#RenderedDist(rs)) => - Ok(#RenderedDist(PointSetDist.T.truncate(leftCutoff, rightCutoff, rs))) - | Error(e) => Error(e) - | _ => Error("Could not truncate distribution.") - } - - let operationToLeaf = ( - evaluationParams, - leftCutoff: option, - rightCutoff: option, - t: node, - ): result => - t - |> trySimplification(leftCutoff, rightCutoff) - |> ( - x => - switch x { - | #Solution(t) => Ok(t) - | #Error(e) => Error(e) - | #NoSolution => truncateAsShape(evaluationParams, leftCutoff, rightCutoff, t) - } - ) -} - -module Normalize = { - let rec operationToLeaf = (evaluationParams, t: node): result => - switch t { - | #RenderedDist(s) => Ok(#RenderedDist(PointSetDist.T.normalize(s))) - | #SymbolicDist(_) => Ok(t) - | _ => ASTTypes.Node.evaluateAndRetry(evaluationParams, operationToLeaf, t) - } -} - -module FunctionCall = { - let _runHardcodedFunction = (name, evaluationParams, args) => - TypeSystem.Function.Ts.findByNameAndRun(HardcodedFunctions.all, name, evaluationParams, args) - - let _runLocalFunction = (name, evaluationParams: evaluationParams, args) => - Environment.getFunction(evaluationParams.environment, name) |> E.R.bind(_, ((argNames, fn)) => - ASTTypes.Function.run(evaluationParams, args, (argNames, fn)) - ) - - let _runWithEvaluatedInputs = ( - evaluationParams: ASTTypes.evaluationParams, - name, - args: array, - ) => - _runHardcodedFunction(name, evaluationParams, args) |> E.O.default( - _runLocalFunction(name, evaluationParams, args), - ) - - // TODO: This forces things to be floats - let run = (evaluationParams, name, args) => - args - |> E.A.fmap(a => evaluationParams.evaluateNode(evaluationParams, a)) - |> E.A.R.firstErrorOrOpen - |> E.R.bind(_, _runWithEvaluatedInputs(evaluationParams, name)) -} - -module Render = { - let rec operationToLeaf = (evaluationParams: evaluationParams, t: node): result => - switch t { - | #Function(_) => Error("Cannot render a function") - | #SymbolicDist(d) => - Ok( - #RenderedDist( - SymbolicDist.T.toPointSetDist(evaluationParams.samplingInputs.pointSetDistLength, d), - ), - ) - | #RenderedDist(_) as t => Ok(t) // already a rendered pointSetDist, we're done here - | _ => ASTTypes.Node.evaluateAndRetry(evaluationParams, operationToLeaf, t) - } -} - -/* This function recursively goes through the nodes of the parse tree, - replacing each Operation node and its subtree with a Data node. - Whenever possible, the replacement produces a new Symbolic Data node, - but most often it will produce a RenderedDist. - This function is used mainly to turn a parse tree into a single RenderedDist - that can then be displayed to the user. */ -let rec toLeaf = (evaluationParams: ASTTypes.evaluationParams, node: node): result => - switch node { - // Leaf nodes just stay leaf nodes - | #SymbolicDist(_) - | #Function(_) - | #RenderedDist(_) => - Ok(node) - | #Array(args) => - args |> E.A.fmap(toLeaf(evaluationParams)) |> E.A.R.firstErrorOrOpen |> E.R.fmap(r => #Array(r)) - // Operations nevaluationParamsd to be turned into leaves - | #AlgebraicCombination(algebraicOp, t1, t2) => - AlgebraicCombination.operationToLeaf(evaluationParams, algebraicOp, t1, t2) - | #PointwiseCombination(pointwiseOp, t1, t2) => - PointwiseCombination.operationToLeaf(evaluationParams, pointwiseOp, t1, t2) - | #Truncate(leftCutoff, rightCutoff, t) => - Truncate.operationToLeaf(evaluationParams, leftCutoff, rightCutoff, t) - | #Normalize(t) => Normalize.operationToLeaf(evaluationParams, t) - | #Render(t) => Render.operationToLeaf(evaluationParams, t) - | #Hash(t) => - t - |> E.A.fmap(((name: string, node: node)) => - toLeaf(evaluationParams, node) |> E.R.fmap(r => (name, r)) - ) - |> E.A.R.firstErrorOrOpen - |> E.R.fmap(r => #Hash(r)) - | #Symbol(r) => - ASTTypes.Environment.get(evaluationParams.environment, r) - |> E.O.toResult("Undeclared variable " ++ r) - |> E.R.bind(_, toLeaf(evaluationParams)) - | #FunctionCall(name, args) => - FunctionCall.run(evaluationParams, name, args) |> E.R.bind(_, toLeaf(evaluationParams)) - } diff --git a/packages/squiggle-lang/src/rescript/OldInterpreter/ASTTypes.res b/packages/squiggle-lang/src/rescript/OldInterpreter/ASTTypes.res deleted file mode 100644 index 873eeb86..00000000 --- a/packages/squiggle-lang/src/rescript/OldInterpreter/ASTTypes.res +++ /dev/null @@ -1,233 +0,0 @@ -@genType -type rec hash = array<(string, node)> -and node = [ - | #SymbolicDist(SymbolicDistTypes.symbolicDist) - | #RenderedDist(PointSetTypes.pointSetDist) - | #Symbol(string) - | #Hash(hash) - | #Array(array) - | #Function(array, node) - | #AlgebraicCombination(Operation.algebraicOperation, node, node) - | #PointwiseCombination(Operation.pointwiseOperation, node, node) - | #Normalize(node) - | #Render(node) - | #Truncate(option, option, node) - | #FunctionCall(string, array) -] - -type statement = [ - | #Assignment(string, node) - | #Expression(node) -] -type program = array - -type environment = Belt.Map.String.t - -type rec evaluationParams = { - samplingInputs: SamplingInputs.samplingInputs, - environment: environment, - evaluateNode: (evaluationParams, node) => Belt.Result.t, -} - -module Environment = { - type t = environment - module MS = Belt.Map.String - let fromArray = MS.fromArray - let empty: t = []->fromArray - let mergeKeepSecond = (a: t, b: t) => - MS.merge(a, b, (_, a, b) => - switch (a, b) { - | (_, Some(b)) => Some(b) - | (Some(a), _) => Some(a) - | _ => None - } - ) - let update = (t, str, fn) => MS.update(t, str, fn) - let get = (t: t, str) => MS.get(t, str) - let getFunction = (t: t, str) => - switch get(t, str) { - | Some(#Function(argNames, fn)) => Ok((argNames, fn)) - | _ => Error("Function " ++ (str ++ " not found")) - } -} - -module Node = { - let getFloat = (node: node) => - node |> ( - x => - switch x { - | #RenderedDist(Discrete({xyShape: {xs: [x], ys: [1.0]}})) => Some(x) - | #SymbolicDist(#Float(x)) => Some(x) - | _ => None - } - ) - - let evaluate = (evaluationParams: evaluationParams) => - evaluationParams.evaluateNode(evaluationParams) - - let evaluateAndRetry = (evaluationParams, fn, node) => - node |> evaluationParams.evaluateNode(evaluationParams) |> E.R.bind(_, fn(evaluationParams)) - - let rec toString: node => string = x => - switch x { - | #SymbolicDist(d) => SymbolicDist.T.toString(d) - | #RenderedDist(_) => "[renderedShape]" - | #AlgebraicCombination(op, t1, t2) => - Operation.Algebraic.format(op, toString(t1), toString(t2)) - | #PointwiseCombination(op, t1, t2) => - Operation.Pointwise.format(op, toString(t1), toString(t2)) - | #Normalize(t) => "normalize(k" ++ (toString(t) ++ ")") - | #Truncate(lc, rc, t) => Operation.Truncate.toString(lc, rc, toString(t)) - | #Render(t) => toString(t) - | #Symbol(t) => "Symbol: " ++ t - | #FunctionCall(name, args) => - "[Function call: (" ++ - (name ++ - ((args |> E.A.fmap(toString) |> Js.String.concatMany(_, ",")) ++ ")]")) - | #Function(args, internal) => - "[Function: (" ++ ((args |> Js.String.concatMany(_, ",")) ++ (toString(internal) ++ ")]")) - | #Array(a) => "[" ++ ((a |> E.A.fmap(toString) |> Js.String.concatMany(_, ",")) ++ "]") - | #Hash(h) => - "{" ++ - ((h - |> E.A.fmap(((name, value)) => name ++ (":" ++ toString(value))) - |> Js.String.concatMany(_, ",")) ++ - "}") - } - - let render = (evaluationParams: evaluationParams, r) => #Render(r) |> evaluate(evaluationParams) - - let ensureIsRendered = (params, t) => - switch t { - | #RenderedDist(_) => Ok(t) - | _ => - switch render(params, t) { - | Ok(#RenderedDist(r)) => Ok(#RenderedDist(r)) - | Ok(_) => Error("Did not render as requested") - | Error(e) => Error(e) - } - } - - let ensureIsRenderedAndGetShape = (params, t) => - switch ensureIsRendered(params, t) { - | Ok(#RenderedDist(r)) => Ok(r) - | Ok(_) => Error("Did not render as requested") - | Error(e) => Error(e) - } - - let toPointSetDist = (item: node) => - switch item { - | #RenderedDist(r) => Some(r) - | _ => None - } - - let _toFloat = (t: PointSetTypes.pointSetDist) => - switch t { - | Discrete({xyShape: {xs: [x], ys: [1.0]}}) => Some(#SymbolicDist(#Float(x))) - | _ => None - } - - let toFloat = (item: node): result => - item |> toPointSetDist |> E.O.bind(_, _toFloat) |> E.O.toResult("Not valid shape") -} - -module Function = { - type t = (array, node) - let fromNode: node => option = node => - switch node { - | #Function(r) => Some(r) - | _ => None - } - let argumentNames = ((a, _): t) => a - let internals = ((_, b): t) => b - let run = (evaluationParams: evaluationParams, args: array, t: t) => - if E.A.length(args) == E.A.length(argumentNames(t)) { - let newEnvironment = Belt.Array.zip(argumentNames(t), args) |> Environment.fromArray - let newEvaluationParams: evaluationParams = { - samplingInputs: evaluationParams.samplingInputs, - environment: Environment.mergeKeepSecond(evaluationParams.environment, newEnvironment), - evaluateNode: evaluationParams.evaluateNode, - } - evaluationParams.evaluateNode(newEvaluationParams, internals(t)) - } else { - Error("Wrong number of variables") - } -} - -module SamplingDistribution = { - type t = [ - | #SymbolicDist(SymbolicDistTypes.symbolicDist) - | #RenderedDist(PointSetTypes.pointSetDist) - ] - - let isSamplingDistribution: node => bool = x => - switch x { - | #SymbolicDist(_) => true - | #RenderedDist(_) => true - | _ => false - } - - let fromNode: node => result = x => - switch x { - | #SymbolicDist(n) => Ok(#SymbolicDist(n)) - | #RenderedDist(n) => Ok(#RenderedDist(n)) - | _ => Error("Not valid type") - } - - let renderIfIsNotSamplingDistribution = (params, t): result => - !isSamplingDistribution(t) - ? switch Node.render(params, t) { - | Ok(r) => Ok(r) - | Error(e) => Error(e) - } - : Ok(t) - - let map = (~renderedDistFn, ~symbolicDistFn, node: node) => - node |> ( - x => - switch x { - | #RenderedDist(r) => Some(renderedDistFn(r)) - | #SymbolicDist(s) => Some(symbolicDistFn(s)) - | _ => None - } - ) - - let sampleN = n => - map(~renderedDistFn=PointSetDist.sampleNRendered(n), ~symbolicDistFn=SymbolicDist.T.sampleN(n)) - - let getCombinationSamples = (n, algebraicOp, t1: node, t2: node) => - switch (sampleN(n, t1), sampleN(n, t2)) { - | (Some(a), Some(b)) => - Some( - Belt.Array.zip(a, b) |> E.A.fmap(((a, b)) => Operation.Algebraic.toFn(algebraicOp, a, b)), - ) - | _ => None - } - - let combineShapesUsingSampling = ( - evaluationParams: evaluationParams, - algebraicOp, - t1: node, - t2: node, - ) => { - let i1 = renderIfIsNotSamplingDistribution(evaluationParams, t1) - let i2 = renderIfIsNotSamplingDistribution(evaluationParams, t2) - E.R.merge(i1, i2) |> E.R.bind(_, ((a, b)) => { - let samples = - getCombinationSamples( - evaluationParams.samplingInputs.sampleCount, - algebraicOp, - a, - b, - ) |> E.O.toResult("Could not get samples") - - let sampleSetDist = samples->E.R.bind(SampleSetDist.make) - - let pointSetDist = - sampleSetDist->E.R.bind(r => - SampleSetDist.toPointSetDist(~samplingInputs=evaluationParams.samplingInputs, ~samples=r) - ) - pointSetDist |> E.R.fmap(r => #Normalize(#RenderedDist(r))) - }) - } -} diff --git a/packages/squiggle-lang/src/rescript/OldInterpreter/DistPlus.res b/packages/squiggle-lang/src/rescript/OldInterpreter/DistPlus.res deleted file mode 100644 index f3297703..00000000 --- a/packages/squiggle-lang/src/rescript/OldInterpreter/DistPlus.res +++ /dev/null @@ -1,87 +0,0 @@ -open PointSetTypes - -@genType -type t = PointSetTypes.distPlus - -let pointSetDistIntegral = pointSetDist => PointSetDist.T.Integral.get(pointSetDist) -let make = (~pointSetDist, ~squiggleString, ()): t => { - let integral = pointSetDistIntegral(pointSetDist) - {pointSetDist: pointSetDist, integralCache: integral, squiggleString: squiggleString} -} - -let update = (~pointSetDist=?, ~integralCache=?, ~squiggleString=?, t: t) => { - pointSetDist: E.O.default(t.pointSetDist, pointSetDist), - integralCache: E.O.default(t.integralCache, integralCache), - squiggleString: E.O.default(t.squiggleString, squiggleString), -} - -let updateShape = (pointSetDist, t) => { - let integralCache = pointSetDistIntegral(pointSetDist) - update(~pointSetDist, ~integralCache, t) -} - -let toPointSetDist = ({pointSetDist, _}: t) => pointSetDist - -let pointSetDistFn = (fn, {pointSetDist}: t) => fn(pointSetDist) - -module T = Distributions.Dist({ - type t = PointSetTypes.distPlus - type integral = PointSetTypes.distPlus - let toPointSetDist = toPointSetDist - let toContinuous = pointSetDistFn(PointSetDist.T.toContinuous) - let toDiscrete = pointSetDistFn(PointSetDist.T.toDiscrete) - - let normalize = (t: t): t => { - let normalizedShape = t |> toPointSetDist |> PointSetDist.T.normalize - t |> updateShape(normalizedShape) - } - - let truncate = (leftCutoff, rightCutoff, t: t): t => { - let truncatedShape = t |> toPointSetDist |> PointSetDist.T.truncate(leftCutoff, rightCutoff) - - t |> updateShape(truncatedShape) - } - - let xToY = (f, t: t) => t |> toPointSetDist |> PointSetDist.T.xToY(f) - - let minX = pointSetDistFn(PointSetDist.T.minX) - let maxX = pointSetDistFn(PointSetDist.T.maxX) - let toDiscreteProbabilityMassFraction = pointSetDistFn( - PointSetDist.T.toDiscreteProbabilityMassFraction, - ) - - // This bit is kind of awkward, could probably use rethinking. - let integral = (t: t) => updateShape(Continuous(t.integralCache), t) - - let updateIntegralCache = (integralCache: option, t) => - update(~integralCache=E.O.default(t.integralCache, integralCache), t) - - let downsample = (i, t): t => updateShape(t |> toPointSetDist |> PointSetDist.T.downsample(i), t) - // todo: adjust for limit, maybe? - let mapY = ( - ~integralSumCacheFn=previousIntegralSum => None, - ~integralCacheFn=previousIntegralCache => None, - ~fn, - {pointSetDist, _} as t: t, - ): t => PointSetDist.T.mapY(~integralSumCacheFn, ~fn, pointSetDist) |> updateShape(_, t) - - // get the total of everything - let integralEndY = (t: t) => { - PointSetDist.T.Integral.sum(toPointSetDist(t)) - } - - // TODO: Fix this below, obviously. Adjust for limits - let integralXtoY = (f, t: t) => { - PointSetDist.T.Integral.xToY(f, toPointSetDist(t)) - } - - // TODO: This part is broken when there is a limit, if this is supposed to be taken into account. - let integralYtoX = (f, t: t) => { - PointSetDist.T.Integral.yToX(f, toPointSetDist(t)) - } - - let mean = (t: t) => { - PointSetDist.T.mean(t.pointSetDist) - } - let variance = (t: t) => PointSetDist.T.variance(t.pointSetDist) -}) diff --git a/packages/squiggle-lang/src/rescript/OldInterpreter/typeSystem/HardcodedFunctions.res b/packages/squiggle-lang/src/rescript/OldInterpreter/typeSystem/HardcodedFunctions.res deleted file mode 100644 index 0c2dde67..00000000 --- a/packages/squiggle-lang/src/rescript/OldInterpreter/typeSystem/HardcodedFunctions.res +++ /dev/null @@ -1,240 +0,0 @@ -open TypeSystem - -let wrongInputsError = (r: array) => { - let inputs = r |> E.A.fmap(TypedValue.toString) |> Js.String.concatMany(_, ",") - Js.log3("Inputs were", inputs, r) - Error("Wrong inputs. The inputs were:" ++ inputs) -} - -let to_: (float, float) => result = (low, high) => - switch (low, high) { - | (low, high) if low <= 0.0 && low < high => - Ok(#SymbolicDist(SymbolicDist.Normal.from90PercentCI(low, high))) - | (low, high) if low < high => - Ok(#SymbolicDist(SymbolicDist.Lognormal.from90PercentCI(low, high))) - | (_, _) => Error("Low value must be less than high value.") - } - -let makeSymbolicFromTwoFloats = (name, fn) => - Function.T.make( - ~name, - ~outputType=#SamplingDistribution, - ~inputTypes=[#Float, #Float], - ~run=x => - switch x { - | [#Float(a), #Float(b)] => fn(a, b) |> E.R.fmap(r => #SymbolicDist(r)) - | e => wrongInputsError(e) - }, - (), - ) - -let makeSymbolicFromOneFloat = (name, fn) => - Function.T.make( - ~name, - ~outputType=#SamplingDistribution, - ~inputTypes=[#Float], - ~run=x => - switch x { - | [#Float(a)] => fn(a) |> E.R.fmap(r => #SymbolicDist(r)) - | e => wrongInputsError(e) - }, - (), - ) - -let makeDistFloat = (name, fn) => - Function.T.make( - ~name, - ~outputType=#SamplingDistribution, - ~inputTypes=[#SamplingDistribution, #Float], - ~run=x => - switch x { - | [#SamplingDist(a), #Float(b)] => fn(a, b) - | [#RenderedDist(a), #Float(b)] => fn(#RenderedDist(a), b) - | e => wrongInputsError(e) - }, - (), - ) - -let makeRenderedDistFloat = (name, fn) => - Function.T.make( - ~name, - ~outputType=#RenderedDistribution, - ~inputTypes=[#RenderedDistribution, #Float], - ~shouldCoerceTypes=true, - ~run=x => - switch x { - | [#RenderedDist(a), #Float(b)] => fn(a, b) - | e => wrongInputsError(e) - }, - (), - ) - -let makeDist = (name, fn) => - Function.T.make( - ~name, - ~outputType=#SamplingDistribution, - ~inputTypes=[#SamplingDistribution], - ~run=x => - switch x { - | [#SamplingDist(a)] => fn(a) - | [#RenderedDist(a)] => fn(#RenderedDist(a)) - | e => wrongInputsError(e) - }, - (), - ) - -let floatFromDist = ( - distToFloatOp: Operation.distToFloatOperation, - t: TypeSystem.samplingDist, -): result => - switch t { - | #SymbolicDist(s) => - SymbolicDist.T.operate(distToFloatOp, s) |> E.R.bind(_, v => Ok(#SymbolicDist(#Float(v)))) - | #RenderedDist(rs) => - PointSetDist.operate(distToFloatOp, rs) |> (v => Ok(#SymbolicDist(#Float(v)))) - } - -let verticalScaling = (scaleOp, rs, scaleBy) => { - // scaleBy has to be a single float, otherwise we'll return an error. - let fn = (secondary, main) => Operation.Scale.toFn(scaleOp, main, secondary) - let integralSumCacheFn = Operation.Scale.toIntegralSumCacheFn(scaleOp) - let integralCacheFn = Operation.Scale.toIntegralCacheFn(scaleOp) - Ok( - #RenderedDist( - PointSetDist.T.mapY( - ~integralSumCacheFn=integralSumCacheFn(scaleBy), - ~integralCacheFn=integralCacheFn(scaleBy), - ~fn=fn(scaleBy), - rs, - ), - ), - ) -} - -module Multimodal = { - let getByNameResult = Hash.getByNameResult - - let _paramsToDistsAndWeights = (r: array) => - switch r { - | [#Hash(r)] => - let dists = - getByNameResult(r, "dists") - ->E.R.bind(TypeSystem.TypedValue.toArray) - ->E.R.bind(r => r |> E.A.fmap(TypeSystem.TypedValue.toDist) |> E.A.R.firstErrorOrOpen) - let weights = - getByNameResult(r, "weights") - ->E.R.bind(TypeSystem.TypedValue.toArray) - ->E.R.bind(r => r |> E.A.fmap(TypeSystem.TypedValue.toFloat) |> E.A.R.firstErrorOrOpen) - - E.R.merge(dists, weights)->E.R.bind(((a, b)) => - E.A.length(b) > E.A.length(a) - ? Error("Too many weights provided") - : Ok( - E.A.zipMaxLength(a, b) |> E.A.fmap(((a, b)) => ( - a |> E.O.toExn(""), - b |> E.O.default(1.0), - )), - ) - ) - | _ => Error("Needs items") - } - let _runner: array => result = r => { - let paramsToDistsAndWeights = - _paramsToDistsAndWeights(r) |> E.R.fmap( - E.A.fmap(((dist, weight)) => - #FunctionCall("scaleMultiply", [dist, #SymbolicDist(#Float(weight))]) - ), - ) - let pointwiseSum: result = - paramsToDistsAndWeights->E.R.bind(E.R.errorIfCondition(E.A.isEmpty, "Needs one input")) - |> E.R.fmap(r => - r - |> Js.Array.sliceFrom(1) - |> E.A.fold_left((acc, x) => #PointwiseCombination(#Add, acc, x), E.A.unsafe_get(r, 0)) - ) - pointwiseSum - } - - let _function = Function.T.make( - ~name="multimodal", - ~outputType=#SamplingDistribution, - ~inputTypes=[#Hash([("dists", #Array(#SamplingDistribution)), ("weights", #Array(#Float))])], - ~run=_runner, - (), - ) -} - -let all = [ - makeSymbolicFromTwoFloats("normal", SymbolicDist.Normal.make), - makeSymbolicFromTwoFloats("uniform", SymbolicDist.Uniform.make), - makeSymbolicFromTwoFloats("beta", SymbolicDist.Beta.make), - makeSymbolicFromTwoFloats("lognormal", SymbolicDist.Lognormal.make), - makeSymbolicFromTwoFloats("lognormalFromMeanAndStdDev", SymbolicDist.Lognormal.fromMeanAndStdev), - makeSymbolicFromOneFloat("exponential", SymbolicDist.Exponential.make), - Function.T.make( - ~name="to", - ~outputType=#SamplingDistribution, - ~inputTypes=[#Float, #Float], - ~run=x => - switch x { - | [#Float(a), #Float(b)] => to_(a, b) - | e => wrongInputsError(e) - }, - (), - ), - Function.T.make( - ~name="triangular", - ~outputType=#SamplingDistribution, - ~inputTypes=[#Float, #Float, #Float], - ~run=x => - switch x { - | [#Float(a), #Float(b), #Float(c)] => - SymbolicDist.Triangular.make(a, b, c) |> E.R.fmap(r => #SymbolicDist(r)) - | e => wrongInputsError(e) - }, - (), - ), - Function.T.make( - ~name="log", - ~outputType=#Float, - ~inputTypes=[#Float], - ~run=x => - switch x { - | [#Float(a)] => Ok(#SymbolicDist(#Float(Js.Math.log(a)))) - | e => wrongInputsError(e) - }, - (), - ), - makeDistFloat("pdf", (dist, float) => floatFromDist(#Pdf(float), dist)), - makeDistFloat("inv", (dist, float) => floatFromDist(#Inv(float), dist)), - makeDistFloat("cdf", (dist, float) => floatFromDist(#Cdf(float), dist)), - makeDist("mean", dist => floatFromDist(#Mean, dist)), - makeDist("sample", dist => floatFromDist(#Sample, dist)), - Function.T.make( - ~name="render", - ~outputType=#RenderedDistribution, - ~inputTypes=[#RenderedDistribution], - ~run=x => - switch x { - | [#RenderedDist(c)] => Ok(#RenderedDist(c)) - | e => wrongInputsError(e) - }, - (), - ), - Function.T.make( - ~name="normalize", - ~outputType=#SamplingDistribution, - ~inputTypes=[#SamplingDistribution], - ~run=x => - switch x { - | [#SamplingDist(#SymbolicDist(c))] => Ok(#SymbolicDist(c)) - | [#SamplingDist(#RenderedDist(c))] => Ok(#RenderedDist(PointSetDist.T.normalize(c))) - | e => wrongInputsError(e) - }, - (), - ), - makeRenderedDistFloat("scaleExp", (dist, float) => verticalScaling(#Power, dist, float)), - makeRenderedDistFloat("scaleMultiply", (dist, float) => verticalScaling(#Multiply, dist, float)), - makeRenderedDistFloat("scaleLog", (dist, float) => verticalScaling(#Logarithm, dist, float)), - Multimodal._function, -] diff --git a/packages/squiggle-lang/src/rescript/OldInterpreter/typeSystem/TypeSystem.res b/packages/squiggle-lang/src/rescript/OldInterpreter/typeSystem/TypeSystem.res deleted file mode 100644 index 32cc9e1b..00000000 --- a/packages/squiggle-lang/src/rescript/OldInterpreter/typeSystem/TypeSystem.res +++ /dev/null @@ -1,196 +0,0 @@ -type node = ASTTypes.node -let getFloat = ASTTypes.Node.getFloat - -type samplingDist = [ - | #SymbolicDist(SymbolicDistTypes.symbolicDist) - | #RenderedDist(PointSetTypes.pointSetDist) -] - -type rec hashType = array<(string, _type)> -and _type = [ - | #Float - | #SamplingDistribution - | #RenderedDistribution - | #Array(_type) - | #Hash(hashType) -] - -type rec hashTypedValue = array<(string, typedValue)> -and typedValue = [ - | #Float(float) - | #RenderedDist(PointSetTypes.pointSetDist) - | #SamplingDist(samplingDist) - | #Array(array) - | #Hash(hashTypedValue) -] - -type _function = { - name: string, - inputTypes: array<_type>, - outputType: _type, - run: array => result, - shouldCoerceTypes: bool, -} - -type functions = array<_function> -type inputNodes = array - -module TypedValue = { - let rec toString: typedValue => string = x => - switch x { - | #SamplingDist(_) => "[sampling dist]" - | #RenderedDist(_) => "[rendered PointSetDist]" - | #Float(f) => "Float: " ++ Js.Float.toString(f) - | #Array(a) => "[" ++ ((a |> E.A.fmap(toString) |> Js.String.concatMany(_, ",")) ++ "]") - | #Hash(v) => - "{" ++ - ((v - |> E.A.fmap(((name, value)) => name ++ (":" ++ toString(value))) - |> Js.String.concatMany(_, ",")) ++ - "}") - } - - let rec fromNode = (node: node): result => - switch node { - | #SymbolicDist(#Float(r)) => Ok(#Float(r)) - | #SymbolicDist(s) => Ok(#SamplingDist(#SymbolicDist(s))) - | #RenderedDist(s) => Ok(#RenderedDist(s)) - | #Array(r) => r |> E.A.fmap(fromNode) |> E.A.R.firstErrorOrOpen |> E.R.fmap(r => #Array(r)) - | #Hash(hash) => - hash - |> E.A.fmap(((name, t)) => fromNode(t) |> E.R.fmap(r => (name, r))) - |> E.A.R.firstErrorOrOpen - |> E.R.fmap(r => #Hash(r)) - | e => Error("Wrong type: " ++ ASTTypes.Node.toString(e)) - } - - // todo: Arrays and hashes - let rec fromNodeWithTypeCoercion = (evaluationParams, _type: _type, node) => - switch (_type, node) { - | (#Float, _) => - switch getFloat(node) { - | Some(a) => Ok(#Float(a)) - | _ => Error("Type Error: Expected float.") - } - | (#SamplingDistribution, _) => - ASTTypes.SamplingDistribution.renderIfIsNotSamplingDistribution( - evaluationParams, - node, - ) |> E.R.bind(_, fromNode) - | (#RenderedDistribution, _) => - ASTTypes.Node.render(evaluationParams, node) |> E.R.bind(_, fromNode) - | (#Array(_type), #Array(b)) => - b - |> E.A.fmap(fromNodeWithTypeCoercion(evaluationParams, _type)) - |> E.A.R.firstErrorOrOpen - |> E.R.fmap(r => #Array(r)) - | (#Hash(named), #Hash(r)) => - let keyValues = - named |> E.A.fmap(((name, intendedType)) => (name, intendedType, Hash.getByName(r, name))) - let typedHash = - keyValues - |> E.A.fmap(((name, intendedType, optionNode)) => - switch optionNode { - | Some(node) => - fromNodeWithTypeCoercion(evaluationParams, intendedType, node) |> E.R.fmap(node => ( - name, - node, - )) - | None => Error("Hash parameter not present in hash.") - } - ) - |> E.A.R.firstErrorOrOpen - |> E.R.fmap(r => #Hash(r)) - typedHash - | _ => Error("fromNodeWithTypeCoercion error, sorry.") - } - - let toFloat: typedValue => result = x => - switch x { - | #Float(x) => Ok(x) - | _ => Error("Not a float") - } - - let toArray: typedValue => result, string> = x => - switch x { - | #Array(x) => Ok(x) - | _ => Error("Not an array") - } - - let toNamed: typedValue => result = x => - switch x { - | #Hash(x) => Ok(x) - | _ => Error("Not a named item") - } - - let toDist: typedValue => result = x => - switch x { - | #SamplingDist(#SymbolicDist(c)) => Ok(#SymbolicDist(c)) - | #SamplingDist(#RenderedDist(c)) => Ok(#RenderedDist(c)) - | #RenderedDist(c) => Ok(#RenderedDist(c)) - | #Float(x) => Ok(#SymbolicDist(#Float(x))) - | x => Error("Cannot be converted into a distribution: " ++ toString(x)) - } -} - -module Function = { - type t = _function - type ts = functions - - module T = { - let make = (~name, ~inputTypes, ~outputType, ~run, ~shouldCoerceTypes=true, _): t => { - name: name, - inputTypes: inputTypes, - outputType: outputType, - run: run, - shouldCoerceTypes: shouldCoerceTypes, - } - - let _inputLengthCheck = (inputNodes: inputNodes, t: t) => { - let expectedLength = E.A.length(t.inputTypes) - let actualLength = E.A.length(inputNodes) - expectedLength == actualLength - ? Ok(inputNodes) - : Error( - "Wrong number of inputs. Expected" ++ - ((expectedLength |> E.I.toString) ++ - (". Got:" ++ (actualLength |> E.I.toString))), - ) - } - - let _coerceInputNodes = (evaluationParams, inputTypes, shouldCoerce, inputNodes) => - Belt.Array.zip(inputTypes, inputNodes) - |> E.A.fmap(((def, input)) => - shouldCoerce - ? TypedValue.fromNodeWithTypeCoercion(evaluationParams, def, input) - : TypedValue.fromNode(input) - ) - |> E.A.R.firstErrorOrOpen - - let inputsToTypedValues = ( - evaluationParams: ASTTypes.evaluationParams, - inputNodes: inputNodes, - t: t, - ) => - _inputLengthCheck(inputNodes, t)->E.R.bind( - _coerceInputNodes(evaluationParams, t.inputTypes, t.shouldCoerceTypes), - ) - - let run = (evaluationParams: ASTTypes.evaluationParams, inputNodes: inputNodes, t: t) => - inputsToTypedValues(evaluationParams, inputNodes, t)->E.R.bind(t.run) - |> ( - x => - switch x { - | Ok(i) => Ok(i) - | Error(r) => Error("Function " ++ (t.name ++ (" error: " ++ r))) - } - ) - } - - module Ts = { - let findByName = (ts: ts, n: string) => ts |> Belt.Array.getBy(_, ({name}) => name == n) - - let findByNameAndRun = (ts: ts, n: string, evaluationParams, inputTypes) => - findByName(ts, n) |> E.O.fmap(T.run(evaluationParams, inputTypes)) - } -} diff --git a/packages/squiggle-lang/src/rescript/OldParser/Parser.res b/packages/squiggle-lang/src/rescript/OldParser/Parser.res deleted file mode 100644 index 8a5e4390..00000000 --- a/packages/squiggle-lang/src/rescript/OldParser/Parser.res +++ /dev/null @@ -1,290 +0,0 @@ -module MathJsonToMathJsAdt = { - type rec arg = - | Symbol(string) - | Value(float) - | Fn(fn) - | Array(array) - | Blocks(array) - | Object(Js.Dict.t) - | Assignment(arg, arg) - | FunctionAssignment(fnAssignment) - and fn = { - name: string, - args: array, - } - and fnAssignment = { - name: string, - args: array, - expression: arg, - } - - let rec run = (j: Js.Json.t) => { - open Json.Decode - switch field("mathjs", string, j) { - | "FunctionNode" => - let args = j |> field("args", array(run)) - let name = j |> optional(field("fn", field("name", string))) - name |> E.O.fmap(name => Fn({name: name, args: args |> E.A.O.concatSomes})) - | "OperatorNode" => - let args = j |> field("args", array(run)) - Some( - Fn({ - name: j |> field("fn", string), - args: args |> E.A.O.concatSomes, - }), - ) - | "ConstantNode" => optional(field("value", Json.Decode.float), j) |> E.O.fmap(r => Value(r)) - | "ParenthesisNode" => j |> field("content", run) - | "ObjectNode" => - let properties = j |> field("properties", dict(run)) - Js.Dict.entries(properties) - |> E.A.fmap(((key, value)) => value |> E.O.fmap(v => (key, v))) - |> E.A.O.concatSomes - |> Js.Dict.fromArray - |> (r => Some(Object(r))) - | "ArrayNode" => - let items = field("items", array(run), j) - Some(Array(items |> E.A.O.concatSomes)) - | "SymbolNode" => Some(Symbol(field("name", string, j))) - | "AssignmentNode" => - let object_ = j |> field("object", run) - let value_ = j |> field("value", run) - switch (object_, value_) { - | (Some(o), Some(v)) => Some(Assignment(o, v)) - | _ => None - } - | "BlockNode" => - let block = r => r |> field("node", run) - let args = j |> field("blocks", array(block)) |> E.A.O.concatSomes - Some(Blocks(args)) - | "FunctionAssignmentNode" => - let name = j |> field("name", string) - let args = j |> field("params", array(field("name", string))) - let expression = j |> field("expr", run) - expression |> E.O.fmap(expression => FunctionAssignment({ - name: name, - args: args, - expression: expression, - })) - | n => - Js.log3("Couldn't parse mathjs node", j, n) - None - } - } -} - -module MathAdtToDistDst = { - open MathJsonToMathJsAdt - - let handleSymbol = sym => Ok(#Symbol(sym)) - - // TODO: This only works on the top level, which needs to be refactored. Also, I think functions don't need to be done like this anymore. - module MathAdtCleaner = { - let transformWithSymbol = (f: float, s: string) => - switch s { - | "K" => Some(f *. 1000.) - | "M" => Some(f *. 1000000.) - | "B" => Some(f *. 1000000000.) - | "T" => Some(f *. 1000000000000.) - | _ => None - } - let rec run = x => - switch x { - | Fn({name: "multiply", args: [Value(f), Symbol(s)]}) as doNothing => - transformWithSymbol(f, s) |> E.O.fmap(r => Value(r)) |> E.O.default(doNothing) - | Fn({name: "unaryMinus", args: [Value(f)]}) => Value(-1.0 *. f) - | Fn({name, args}) => Fn({name: name, args: args |> E.A.fmap(run)}) - | Array(args) => Array(args |> E.A.fmap(run)) - | Symbol(s) => Symbol(s) - | Value(v) => Value(v) - | Blocks(args) => Blocks(args |> E.A.fmap(run)) - | Assignment(a, b) => Assignment(a, run(b)) - | FunctionAssignment(a) => FunctionAssignment(a) - | Object(v) => - Object( - v - |> Js.Dict.entries - |> E.A.fmap(((key, value)) => (key, run(value))) - |> Js.Dict.fromArray, - ) - } - } - - let lognormal = (args, parseArgs, nodeParser) => - switch args { - | [Object(o)] => - let g = s => - Js.Dict.get(o, s) |> E.O.toResult("Variable was empty") |> E.R.bind(_, nodeParser) - switch (g("mean"), g("stdev"), g("mu"), g("sigma")) { - | (Ok(mean), Ok(stdev), _, _) => - Ok(#FunctionCall("lognormalFromMeanAndStdDev", [mean, stdev])) - | (_, _, Ok(mu), Ok(sigma)) => Ok(#FunctionCall("lognormal", [mu, sigma])) - | _ => Error("Lognormal distribution needs either mean and stdev or mu and sigma") - } - | _ => parseArgs() |> E.R.fmap((args: array) => #FunctionCall("lognormal", args)) - } - - // Error("Dotwise exponentiation needs two operands") - let operationParser = (name: string, args: result, string>): result< - ASTTypes.node, - string, - > => { - let toOkAlgebraic = r => Ok(#AlgebraicCombination(r)) - let toOkPointwise = r => Ok(#PointwiseCombination(r)) - let toOkTruncate = r => Ok(#Truncate(r)) - args |> E.R.bind(_, args => - switch (name, args) { - | ("add", [l, r]) => toOkAlgebraic((#Add, l, r)) - | ("add", _) => Error("Addition needs two operands") - | ("unaryMinus", [l]) => toOkAlgebraic((#Multiply, #SymbolicDist(#Float(-1.0)), l)) - | ("subtract", [l, r]) => toOkAlgebraic((#Subtract, l, r)) - | ("subtract", _) => Error("Subtraction needs two operands") - | ("multiply", [l, r]) => toOkAlgebraic((#Multiply, l, r)) - | ("multiply", _) => Error("Multiplication needs two operands") - | ("pow", [l, r]) => toOkAlgebraic((#Power, l, r)) - | ("pow", _) => Error("Exponentiation needs two operands") - | ("dotMultiply", [l, r]) => toOkPointwise((#Multiply, l, r)) - | ("dotMultiply", _) => Error("Dotwise multiplication needs two operands") - | ("dotPow", [l, r]) => toOkPointwise((#Power, l, r)) - | ("dotPow", _) => Error("Dotwise exponentiation needs two operands") - | ("rightLogShift", [l, r]) => toOkPointwise((#Add, l, r)) - | ("rightLogShift", _) => Error("Dotwise addition needs two operands") - | ("divide", [l, r]) => toOkAlgebraic((#Divide, l, r)) - | ("divide", _) => Error("Division needs two operands") - | ("leftTruncate", [d, #SymbolicDist(#Float(lc))]) => toOkTruncate((Some(lc), None, d)) - | ("leftTruncate", _) => - Error("leftTruncate needs two arguments: the expression and the cutoff") - | ("rightTruncate", [d, #SymbolicDist(#Float(rc))]) => toOkTruncate((None, Some(rc), d)) - | ("rightTruncate", _) => - Error("rightTruncate needs two arguments: the expression and the cutoff") - | ("truncate", [d, #SymbolicDist(#Float(lc)), #SymbolicDist(#Float(rc))]) => - toOkTruncate((Some(lc), Some(rc), d)) - | ("truncate", _) => Error("truncate needs three arguments: the expression and both cutoffs") - | _ => Error("This type not currently supported") - } - ) - } - - let functionParser = ( - nodeParser: MathJsonToMathJsAdt.arg => Belt.Result.t, - name: string, - args: array, - ): result => { - let parseArray = ags => ags |> E.A.fmap(nodeParser) |> E.A.R.firstErrorOrOpen - let parseArgs = () => parseArray(args) - switch name { - | "lognormal" => lognormal(args, parseArgs, nodeParser) - | "multimodal" - | "add" - | "subtract" - | "multiply" - | "unaryMinus" - | "dotMultiply" - | "dotPow" - | "rightLogShift" - | "divide" - | "pow" - | "leftTruncate" - | "rightTruncate" - | "truncate" => - operationParser(name, parseArgs()) - | "mm" => - let weights = - args - |> E.A.last - |> E.O.bind(_, x => - switch x { - | Array(values) => Some(parseArray(values)) - | _ => None - } - ) - let possibleDists = E.O.isSome(weights) - ? Belt.Array.slice(args, ~offset=0, ~len=E.A.length(args) - 1) - : args - let dists = parseArray(possibleDists) - switch (weights, dists) { - | (Some(Error(r)), _) => Error(r) - | (_, Error(r)) => Error(r) - | (None, Ok(dists)) => - let hash: ASTTypes.node = #FunctionCall( - "multimodal", - [#Hash([("dists", #Array(dists)), ("weights", #Array([]))])], - ) - Ok(hash) - | (Some(Ok(weights)), Ok(dists)) => - let hash: ASTTypes.node = #FunctionCall( - "multimodal", - [#Hash([("dists", #Array(dists)), ("weights", #Array(weights))])], - ) - Ok(hash) - } - | name => parseArgs() |> E.R.fmap((args: array) => #FunctionCall(name, args)) - } - } - - let rec nodeParser: MathJsonToMathJsAdt.arg => result = x => - switch x { - | Value(f) => Ok(#SymbolicDist(#Float(f))) - | Symbol(sym) => Ok(#Symbol(sym)) - | Fn({name, args}) => functionParser(nodeParser, name, args) - | _ => Error("This type not currently supported") - } - - // | FunctionAssignment({name, args, expression}) => { - // let evaluatedExpression = run(expression); - // `Function(_ => Ok(evaluatedExpression)); - // } - let rec topLevel = (r): result => - switch r { - | FunctionAssignment({name, args, expression}) => - switch nodeParser(expression) { - | Ok(r) => Ok([#Assignment(name, #Function(args, r))]) - | Error(r) => Error(r) - } - | Value(_) as r => nodeParser(r) |> E.R.fmap(r => [#Expression(r)]) - | Fn(_) as r => nodeParser(r) |> E.R.fmap(r => [#Expression(r)]) - | Array(_) => Error("Array not valid as top level") - | Symbol(s) => handleSymbol(s) |> E.R.fmap(r => [#Expression(r)]) - | Object(_) => Error("Object not valid as top level") - | Assignment(name, value) => - switch name { - | Symbol(symbol) => nodeParser(value) |> E.R.fmap(r => [#Assignment(symbol, r)]) - | _ => Error("Symbol not a string") - } - | Blocks(blocks) => - blocks |> E.A.fmap(b => topLevel(b)) |> E.A.R.firstErrorOrOpen |> E.R.fmap(E.A.concatMany) - } - - let run = (r): result => r |> MathAdtCleaner.run |> topLevel -} - -/* The MathJs parser doesn't support '.+' syntax, but we want it because it - would make sense with '.*'. Our workaround is to change this to >>>, which is - logShift in mathJS. We don't expect to use logShift anytime soon, so this tradeoff - seems fine. - */ -let pointwiseToRightLogShift = Js.String.replaceByRe(%re("/\.\+/g"), ">>>") - -let fromString2 = str => { - /* We feed the user-typed string into Mathjs.parseMath, - which returns a JSON with (hopefully) a single-element array. - This array element is the top-level node of a nested-object tree - representing the functions/arguments/values/etc. in the string. - - The function MathJsonToMathJsAdt then recursively unpacks this JSON into a typed data structure we can use. - Inside of this function, MathAdtToDistDst is called whenever a distribution function is encountered. - */ - let mathJsToJson = str |> pointwiseToRightLogShift |> Mathjs.parseMath - - let mathJsParse = E.R.bind(mathJsToJson, r => - switch MathJsonToMathJsAdt.run(r) { - | Some(r) => Ok(r) - | None => Error("MathJsParse Error") - } - ) - - let value = E.R.bind(mathJsParse, MathAdtToDistDst.run) - value -} - -let fromString = str => fromString2(str) diff --git a/packages/squiggle-lang/src/rescript/ProgramEvaluator.res b/packages/squiggle-lang/src/rescript/ProgramEvaluator.res deleted file mode 100644 index 96a765d8..00000000 --- a/packages/squiggle-lang/src/rescript/ProgramEvaluator.res +++ /dev/null @@ -1,185 +0,0 @@ -// TODO: This setup is more confusing than it should be, there's more work to do in cleanup here. -module Inputs = { - module SamplingInputs = { - type t = { - sampleCount: option, - outputXYPoints: option, - kernelWidth: option, - pointDistLength: option, - } - } - let defaultRecommendedLength = 100 - let defaultShouldDownsample = true - - type inputs = { - squiggleString: string, - samplingInputs: SamplingInputs.t, - environment: ASTTypes.environment, - } - - let empty: SamplingInputs.t = { - sampleCount: None, - outputXYPoints: None, - kernelWidth: None, - pointDistLength: None, - } - - let make = ( - ~samplingInputs=empty, - ~squiggleString, - ~environment=ASTTypes.Environment.empty, - (), - ): inputs => { - samplingInputs: samplingInputs, - squiggleString: squiggleString, - environment: environment, - } -} - -type exportDistribution = [ - | #DistPlus(DistPlus.t) - | #Float(float) - | #Function(float => Belt.Result.t) -] - -type exportEnv = array<(string, ASTTypes.node)> - -type exportType = { - environment: exportEnv, - exports: array, -} - -module Internals = { - let addVariable = ( - {samplingInputs, squiggleString, environment}: Inputs.inputs, - str, - node, - ): Inputs.inputs => { - samplingInputs: samplingInputs, - squiggleString: squiggleString, - environment: ASTTypes.Environment.update(environment, str, _ => Some(node)), - } - - type outputs = { - graph: ASTTypes.node, - pointSetDist: PointSetTypes.pointSetDist, - } - let makeOutputs = (graph, shape): outputs => {graph: graph, pointSetDist: shape} - - let makeInputs = (inputs: Inputs.inputs): SamplingInputs.samplingInputs => { - sampleCount: inputs.samplingInputs.sampleCount |> E.O.default(10000), - outputXYPoints: inputs.samplingInputs.outputXYPoints |> E.O.default(10000), - kernelWidth: inputs.samplingInputs.kernelWidth, - pointSetDistLength: inputs.samplingInputs.pointDistLength |> E.O.default(10000), - } - - let runNode = (inputs, node) => AST.toLeaf(makeInputs(inputs), inputs.environment, node) - - let renderIfNeeded = (inputs: Inputs.inputs, node: ASTTypes.node): result< - ASTTypes.node, - string, - > => - node |> ( - x => - switch x { - | #Normalize(_) as n - | #SymbolicDist(_) as n => - #Render(n) - |> runNode(inputs) - |> ( - x => - switch x { - | Ok(#RenderedDist(_)) as r => r - | Error(r) => Error(r) - | _ => Error("Didn't render, but intended to") - } - ) - - | n => Ok(n) - } - ) - - let outputToDistPlus = (inputs: Inputs.inputs, pointSetDist: PointSetTypes.pointSetDist) => - DistPlus.make(~pointSetDist, ~squiggleString=Some(inputs.squiggleString), ()) - - let rec returnDist = ( - functionInfo: (array, ASTTypes.node), - inputs: Inputs.inputs, - env: ASTTypes.environment, - ) => { - (input: float) => { - let foo: Inputs.inputs = {...inputs, environment: env} - evaluateFunction(foo, functionInfo, [#SymbolicDist(#Float(input))]) |> E.R.bind(_, a => - switch a { - | #DistPlus(d) => Ok(DistPlus.T.normalize(d)) - | n => - Js.log2("Error here", n) - Error("wrong type") - } - ) - } - } - // TODO: Consider using ExpressionTypes.ExpressionTree.getFloat or similar in this function - and coersionToExportedTypes = (inputs, env: ASTTypes.environment, ex: ASTTypes.node): result< - exportDistribution, - string, - > => - ex - |> renderIfNeeded(inputs) - |> E.R.bind(_, x => - switch x { - | #RenderedDist(Discrete({xyShape: {xs: [x], ys: [1.0]}})) => Ok(#Float(x)) - | #SymbolicDist(#Float(x)) => Ok(#Float(x)) - | #RenderedDist(n) => Ok(#DistPlus(outputToDistPlus(inputs, n))) - | #Function(n) => Ok(#Function(returnDist(n, inputs, env))) - | n => Error("Didn't output a rendered distribution. Format:" ++ AST.toString(n)) - } - ) - - and evaluateFunction = (inputs: Inputs.inputs, fn: (array, ASTTypes.node), fnInputs) => { - let output = AST.runFunction(makeInputs(inputs), inputs.environment, fnInputs, fn) - output |> E.R.bind(_, coersionToExportedTypes(inputs, inputs.environment)) - } - - let runProgram = (inputs: Inputs.inputs, p: ASTTypes.program) => { - let ins = ref(inputs) - p - |> E.A.fmap(x => - switch x { - | #Assignment(name, node) => - ins := addVariable(ins.contents, name, node) - None - | #Expression(node) => Some(runNode(ins.contents, node)) - } - ) - |> E.A.O.concatSomes - |> E.A.R.firstErrorOrOpen - |> E.R.bind(_, d => - d - |> E.A.fmap(x => coersionToExportedTypes(inputs, ins.contents.environment, x)) - |> E.A.R.firstErrorOrOpen - ) - |> E.R.fmap(ex => { - environment: Belt.Map.String.toArray(ins.contents.environment), - exports: ex, - }) - } - - let inputsToLeaf = (inputs: Inputs.inputs) => - Parser.fromString(inputs.squiggleString) |> E.R.bind(_, g => runProgram(inputs, g)) -} - -@genType -let runAll: (string, Inputs.SamplingInputs.t, exportEnv) => result = ( - squiggleString, - samplingInputs, - environment, -) => { - let inputs = Inputs.make( - ~samplingInputs, - ~squiggleString, - ~environment=Belt.Map.String.fromArray(environment), - (), - ) - Internals.inputsToLeaf(inputs) -} diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer.res index 7e93f367..d2e4858f 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer.res @@ -6,5 +6,10 @@ module Js = Reducer_Js module MathJs = Reducer_MathJs type expressionValue = Reducer_Expression.expressionValue +type externalBindings = Expression.externalBindings let evaluate = Expression.eval +let evaluateUsingExternalBindings = Expression.evalUsingExternalBindings +let evaluatePartialUsingExternalBindings = Expression.evalPartialUsingExternalBindings let parse = Expression.parse +let parseOuter = Expression.parseOuter +let parsePartial = Expression.parsePartial diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer.resi b/packages/squiggle-lang/src/rescript/Reducer/Reducer.resi index 6f6a55a8..8bbfc0b5 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer.resi +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer.resi @@ -7,7 +7,20 @@ module MathJs = Reducer_MathJs @genType type expressionValue = ReducerInterface_ExpressionValue.expressionValue - +@genType +type externalBindings = ReducerInterface_ExpressionValue.externalBindings @genType let evaluate: string => result +@genType +let evaluateUsingExternalBindings: ( + string, + externalBindings, +) => result +@genType +let evaluatePartialUsingExternalBindings: ( + string, + externalBindings, +) => result let parse: string => result +let parseOuter: string => result +let parsePartial: string => result diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch.res index 23ab4451..83d3eca2 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch.res @@ -1 +1,2 @@ module Builtin = Reducer_Dispatch_BuiltIn +module BuiltinMacros = Reducer_Dispatch_BuiltInMacros diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltInMacros.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltInMacros.res new file mode 100644 index 00000000..b7df0ff3 --- /dev/null +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltInMacros.res @@ -0,0 +1,122 @@ +/* + Macros are like functions but instead of taking values as parameters, + they take expressions as parameters and return a new expression. + Macros are used to define language building blocks. They are like Lisp macros. +*/ +module ExpressionT = Reducer_Expression_T +module ExpressionValue = ReducerInterface.ExpressionValue +module Result = Belt.Result + +open Reducer_ErrorValue + +type expression = ExpressionT.expression + +type reducerFn = ( + expression, + ExpressionT.bindings, +) => result + +let dispatchMacroCall = ( + list: list, + bindings: ExpressionT.bindings, + reduceExpression: reducerFn, +): result => { + let rec replaceSymbols = (expression: expression, bindings: ExpressionT.bindings): result< + expression, + errorValue, + > => + switch expression { + | ExpressionT.EValue(EvSymbol(aSymbol)) => + switch bindings->Belt.Map.String.get(aSymbol) { + | Some(boundExpression) => boundExpression->Ok + | None => RESymbolNotFound(aSymbol)->Error + } + | ExpressionT.EValue(_) => expression->Ok + | ExpressionT.EBindings(_) => expression->Ok + | ExpressionT.EList(list) => { + let racc = list->Belt.List.reduceReverse(Ok(list{}), (racc, each: expression) => + racc->Result.flatMap(acc => { + each + ->replaceSymbols(bindings) + ->Result.flatMap(newNode => { + acc->Belt.List.add(newNode)->Ok + }) + }) + ) + racc->Result.map(acc => acc->ExpressionT.EList) + } + } + + let doBindStatement = (statement: expression, bindings: ExpressionT.bindings) => { + switch statement { + | ExpressionT.EList(list{ + ExpressionT.EValue(EvCall("$let")), + ExpressionT.EValue(EvSymbol(aSymbol)), + expressionToReduce, + }) => { + let rNewExpressionToReduce = replaceSymbols(expressionToReduce, bindings) + + let rNewValue = + rNewExpressionToReduce->Result.flatMap(newExpressionToReduce => + reduceExpression(newExpressionToReduce, bindings) + ) + + let rNewExpression = rNewValue->Result.map(newValue => ExpressionT.EValue(newValue)) + rNewExpression->Result.map(newExpression => + Belt.Map.String.set(bindings, aSymbol, newExpression)->ExpressionT.EBindings + ) + } + | _ => REAssignmentExpected->Error + } + } + + let doExportVariableExpression = (bindings: ExpressionT.bindings) => { + let emptyDictionary: Js.Dict.t = Js.Dict.empty() + let reducedBindings = bindings->Belt.Map.String.keep((_key, value) => + switch value { + | ExpressionT.EValue(_) => true + | _ => false + } + ) + let externalBindings = reducedBindings->Belt.Map.String.reduce(emptyDictionary, ( + acc, + key, + expressionValue, + ) => { + let value = switch expressionValue { + | EValue(aValue) => aValue + | _ => EvSymbol("internal") + } + Js.Dict.set(acc, key, value) + acc + }) + externalBindings->ExpressionValue.EvRecord->ExpressionT.EValue->Ok + } + + let doBindExpression = (expression: expression, bindings: ExpressionT.bindings) => + switch expression { + | ExpressionT.EList(list{ExpressionT.EValue(EvCall("$let")), ..._}) => + REExpressionExpected->Error + | ExpressionT.EList(list{ExpressionT.EValue(EvCall("$exportVariablesExpression"))}) => + doExportVariableExpression(bindings) + | _ => replaceSymbols(expression, bindings) + } + + switch list { + | list{ExpressionT.EValue(EvCall("$$bindings"))} => bindings->ExpressionT.EBindings->Ok + + | list{ + ExpressionT.EValue(EvCall("$$bindStatement")), + ExpressionT.EBindings(bindings), + statement, + } => + doBindStatement(statement, bindings) + | list{ + ExpressionT.EValue(EvCall("$$bindExpression")), + ExpressionT.EBindings(bindings), + expression, + } => + doBindExpression(expression, bindings) + | _ => list->ExpressionT.EList->Ok + } +} diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_ErrorValue.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_ErrorValue.res index 4ad60732..96b73fd2 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_ErrorValue.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_ErrorValue.res @@ -9,6 +9,7 @@ type errorValue = | RERecordPropertyNotFound(string, string) | RESymbolNotFound(string) | RESyntaxError(string) + | REDistributionError(DistributionTypes.error) | RETodo(string) // To do type t = errorValue @@ -20,6 +21,7 @@ let errorToString = err => | REAssignmentExpected => "Assignment expected" | REExpressionExpected => "Expression expected" | REFunctionExpected(msg) => `Function expected: ${msg}` + | REDistributionError(err) => `Distribution Math Error: ${DistributionTypes.Error.toString(err)}` | REJavaScriptExn(omsg, oname) => { let answer = "JS Exception:" let answer = switch oname { diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res index 90e79e8e..b45b8e14 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res @@ -15,7 +15,7 @@ type t = expression */ let rec toString = expression => switch expression { - | T.EBindings(bindings) => "$$bound" + | T.EBindings(_) => "$$bound" | T.EList(aList) => `(${Belt.List.map(aList, aValue => toString(aValue)) ->Extra.List.interperse(" ") @@ -39,12 +39,26 @@ let parse_ = (expr: string, parser, converter): result => let parse = (mathJsCode: string): result => mathJsCode->parse_(MathJs.Parse.parse, MathJs.ToExpression.fromNode) +let parsePartial = (mathJsCode: string): result => + mathJsCode->parse_(MathJs.Parse.parse, MathJs.ToExpression.fromPartialNode) + +let parseOuter = (mathJsCode: string): result => + mathJsCode->parse_(MathJs.Parse.parse, MathJs.ToExpression.fromOuterNode) + let defaultBindings: T.bindings = Belt.Map.String.empty /* Recursively evaluate/reduce the expression (Lisp AST) */ let rec reduceExpression = (expression: t, bindings: T.bindings): result => { + /* + Macros are like functions but instead of taking values as parameters, + they take expressions as parameters and return a new expression. + Macros are used to define language building blocks. They are like Lisp macros. + */ + let doMacroCall = (list: list, bindings: T.bindings): result => + Reducer_Dispatch_BuiltInMacros.dispatchMacroCall(list, bindings, reduceExpression) + /* After reducing each level of expression(Lisp AST), we have a value list to evaluate */ @@ -54,72 +68,10 @@ let rec reduceExpression = (expression: t, bindings: T.bindings): result valueList->Belt.List.toArray->ExpressionValue.EvArray->Ok } - /* - Macros are like functions but instead of taking values as parameters, - they take expressions as parameters and return a new expression. - Macros are used to define language building blocks. They are like Lisp macros. - */ - let doMacroCall = (list: list, bindings: T.bindings): result => { - let dispatchMacroCall = (list: list, bindings: T.bindings): result => { - let rec replaceSymbols = (expression: t, bindings: T.bindings): result => - switch expression { - | T.EValue(EvSymbol(aSymbol)) => - switch bindings->Belt.Map.String.get(aSymbol) { - | Some(boundExpression) => boundExpression->Ok - | None => RESymbolNotFound(aSymbol)->Error - } - | T.EValue(_) => expression->Ok - | T.EBindings(_) => expression->Ok - | T.EList(list) => { - let racc = list->Belt.List.reduceReverse(Ok(list{}), (racc, each: expression) => - racc->Result.flatMap(acc => { - each - ->replaceSymbols(bindings) - ->Result.flatMap(newNode => { - acc->Belt.List.add(newNode)->Ok - }) - }) - ) - racc->Result.map(acc => acc->T.EList) - } - } - - let doBindStatement = (statement: t, bindings: T.bindings) => { - switch statement { - | T.EList(list{T.EValue(EvCall("$let")), T.EValue(EvSymbol(aSymbol)), expression}) => { - let rNewExpression = replaceSymbols(expression, bindings) - rNewExpression->Result.map(newExpression => - Belt.Map.String.set(bindings, aSymbol, newExpression)->T.EBindings - ) - } - | _ => REAssignmentExpected->Error - } - } - - let doBindExpression = (expression: t, bindings: T.bindings) => { - switch expression { - | T.EList(list{T.EValue(EvCall("$let")), ..._}) => REExpressionExpected->Error - | _ => replaceSymbols(expression, bindings) - } - } - - switch list { - | list{T.EValue(EvCall("$$bindings"))} => bindings->T.EBindings->Ok - - | list{T.EValue(EvCall("$$bindStatement")), T.EBindings(bindings), statement} => - doBindStatement(statement, bindings) - | list{T.EValue(EvCall("$$bindExpression")), T.EBindings(bindings), expression} => - doBindExpression(expression, bindings) - | _ => list->T.EList->Ok - } - } - - list->dispatchMacroCall(bindings) - } - let rec seekMacros = (expression: t, bindings: T.bindings): result => switch expression { - | T.EValue(value) => expression->Ok + | T.EValue(_value) => expression->Ok + | T.EBindings(_value) => expression->Ok | T.EList(list) => { let racc: result, 'e> = list->Belt.List.reduceReverse(Ok(list{}), ( racc, @@ -155,6 +107,7 @@ let rec reduceExpression = (expression: t, bindings: T.bindings): resultResult.flatMap(acc => acc->reduceValueList) } + | EBindings(_bindings) => RETodo("Error: Bindings cannot be reduced to values")->Error } let rExpandedExpression: result = expression->seekMacros(bindings) @@ -163,17 +116,71 @@ let rec reduceExpression = (expression: t, bindings: T.bindings): result => +let evalUsingExternalBindingsExpression_ = (aExpression, bindings): result => reduceExpression(aExpression, bindings) /* - Evaluates MathJs code via Reducer using bindings and answers the result + Evaluates MathJs code via Reducer using bindings and answers the result. + When bindings are used, the code is a partial code as if it is cut from a larger code. + Therefore all statements are assignments. */ -let evalWBindings = (codeText: string, bindings: T.bindings) => { - parse(codeText)->Result.flatMap(code => code->evalWBindingsExpression(bindings)) +let evalPartialUsingExternalBindings_ = (codeText: string, bindings: T.bindings) => { + parsePartial(codeText)->Result.flatMap(expression => + expression->evalUsingExternalBindingsExpression_(bindings) + ) } /* - Evaluates MathJs code via Reducer and answers the result + Evaluates MathJs code via Reducer using bindings and answers the result. + When bindings are used, the code is a partial code as if it is cut from a larger code. + Therefore all statments are assignments. */ -let eval = (code: string) => evalWBindings(code, defaultBindings) +let evalOuterWBindings_ = (codeText: string, bindings: T.bindings) => { + parseOuter(codeText)->Result.flatMap(expression => + expression->evalUsingExternalBindingsExpression_(bindings) + ) +} + +/* + Evaluates MathJs code and bindings via Reducer and answers the result +*/ +let eval = (codeText: string) => { + parse(codeText)->Result.flatMap(expression => + expression->evalUsingExternalBindingsExpression_(defaultBindings) + ) +} + +type externalBindings = ReducerInterface.ExpressionValue.externalBindings //Js.Dict.t + +let externalBindingsToBindings = (externalBindings: externalBindings): T.bindings => { + let keys = Js.Dict.keys(externalBindings) + keys->Belt.Array.reduce(defaultBindings, (acc, key) => { + let value = Js.Dict.unsafeGet(externalBindings, key) + acc->Belt.Map.String.set(key, T.EValue(value)) + }) +} +/* + Evaluates code with external bindings. External bindings are a record of expression values. +*/ +let evalUsingExternalBindings = (code: string, externalBindings: externalBindings) => { + let bindings = externalBindings->externalBindingsToBindings + evalOuterWBindings_(code, bindings) +} + +/* + Evaluates code with external bindings. External bindings are a record of expression values. + The code is a partial code as if it is cut from a larger code. Therefore all statments are assignments. +*/ +let evalPartialUsingExternalBindings = (code: string, externalBindings: externalBindings): result< + externalBindings, + 'e, +> => { + let bindings = externalBindings->externalBindingsToBindings + let answer = evalPartialUsingExternalBindings_(code, bindings) + answer->Result.flatMap(answer => + switch answer { + | EvRecord(aRecord) => Ok(aRecord) + | _ => RETodo("TODO: External bindings must be returned")->Error + } + ) +} diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_MathJs/Reducer_MathJs_Eval.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_MathJs/Reducer_MathJs_Eval.res index 94a1834c..ab9fb711 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_MathJs/Reducer_MathJs_Eval.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_MathJs/Reducer_MathJs_Eval.res @@ -12,7 +12,7 @@ type answer = {"value": unit} Rescript cannot type cast on basic values passed on their own. This is why we call evalua inside Javascript and wrap the result in an Object */ -let eval__ = %raw(`function (expr) { return {value: Mathjs.evaluate(expr)}; }`) +let eval__: string => 'a = %raw(`function (expr) { return {value: Mathjs.evaluate(expr)}; }`) /* Call MathJs evaluate and return as a variant diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_MathJs/Reducer_MathJs_ToExpression.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_MathJs/Reducer_MathJs_ToExpression.res index 30c8651d..a8b7b39a 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_MathJs/Reducer_MathJs_ToExpression.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_MathJs/Reducer_MathJs_ToExpression.res @@ -9,6 +9,22 @@ type expression = ExpressionT.expression type expressionValue = ExpressionValue.expressionValue type errorValue = ErrorValue.errorValue +let passToFunction = (fName: string, rLispArgs): result => { + let toEvCallValue = (name: string): expression => name->ExpressionValue.EvCall->ExpressionT.EValue + + let fn = fName->toEvCallValue + rLispArgs->Result.flatMap(lispArgs => list{fn, ...lispArgs}->ExpressionT.EList->Ok) +} + +type blockTag = + | ImportVariablesStatement + | ExportVariablesExpression +type tagOrNode = + | BlockTag(blockTag) + | BlockNode(Parse.node) + +let toTagOrNode = block => BlockNode(block["node"]) + let rec fromNode = (mathJsNode: Parse.node): result => Parse.castNodeType(mathJsNode)->Result.flatMap(typedMathJsNode => { let fromNodeList = (nodeList: list): result, 'e> => @@ -18,16 +34,9 @@ let rec fromNode = (mathJsNode: Parse.node): result => ) ) - let toEvCallValue = (name: string): expression => - name->ExpressionValue.EvCall->ExpressionT.EValue let toEvSymbolValue = (name: string): expression => name->ExpressionValue.EvSymbol->ExpressionT.EValue - let passToFunction = (fName: string, rLispArgs): result => { - let fn = fName->toEvCallValue - rLispArgs->Result.flatMap(lispArgs => list{fn, ...lispArgs}->ExpressionT.EList->Ok) - } - let caseFunctionNode = fNode => { let lispArgs = fNode["args"]->Belt.List.fromArray->fromNodeList passToFunction(fNode->Parse.nameOfFunctionNode, lispArgs) @@ -94,27 +103,6 @@ let rec fromNode = (mathJsNode: Parse.node): result => aNode["items"]->Belt.List.fromArray->fromNodeList->Result.map(list => ExpressionT.EList(list)) } - let caseBlockNode = (bNode): result => { - let blocks = bNode["blocks"] - let initialBindings = passToFunction("$$bindings", list{}->Ok) - let lastIndex = Belt.Array.length(blocks) - 1 - blocks->Belt.Array.reduceWithIndex(initialBindings, (rPreviousBindings, block, i) => { - rPreviousBindings->Result.flatMap(previousBindings => { - let node = block["node"] - let rStatement: result = node->fromNode - let bindName = if i == lastIndex { - "$$bindExpression" - } else { - "$$bindStatement" - } - rStatement->Result.flatMap((statement: expression) => { - let lispArgs = list{previousBindings, statement}->Ok - passToFunction(bindName, lispArgs) - }) - }) - }) - } - let rFinalExpression: result = switch typedMathJsNode { | MjAccessorNode(aNode) => caseAccessorNode(aNode["object"], aNode["index"]) | MjArrayNode(aNode) => caseArrayNode(aNode) @@ -124,8 +112,7 @@ let rec fromNode = (mathJsNode: Parse.node): result => let rExpr: result = expr->Ok rExpr } - | MjBlockNode(bNode) => caseBlockNode(bNode) - // | MjBlockNode(bNode) => "statement"->toEvSymbolValue->Ok + | MjBlockNode(bNode) => bNode["blocks"]->Belt.Array.map(toTagOrNode)->caseTagOrNodes | MjConstantNode(cNode) => cNode["value"]->JavaScript.Gate.jsToEv->Result.flatMap(v => v->ExpressionT.EValue->Ok) | MjFunctionNode(fNode) => fNode->caseFunctionNode @@ -136,3 +123,73 @@ let rec fromNode = (mathJsNode: Parse.node): result => } rFinalExpression }) +and caseTagOrNodes = (tagOrNodes): result => { + let initialBindings = passToFunction("$$bindings", list{}->Ok) + let lastIndex = Belt.Array.length(tagOrNodes) - 1 + tagOrNodes->Belt.Array.reduceWithIndex(initialBindings, (rPreviousBindings, tagOrNode, i) => { + rPreviousBindings->Result.flatMap(previousBindings => { + let rStatement: result = switch tagOrNode { + | BlockNode(node) => fromNode(node) + | BlockTag(tag) => + switch tag { + | ImportVariablesStatement => passToFunction("$importVariablesStatement", list{}->Ok) + | ExportVariablesExpression => passToFunction("$exportVariablesExpression", list{}->Ok) + } + } + + let bindName = if i == lastIndex { + "$$bindExpression" + } else { + "$$bindStatement" + } + + rStatement->Result.flatMap((statement: expression) => { + let lispArgs = list{previousBindings, statement}->Ok + passToFunction(bindName, lispArgs) + }) + }) + }) +} + +let fromPartialNode = (mathJsNode: Parse.node): result => { + Parse.castNodeType(mathJsNode)->Result.flatMap(typedMathJsNode => { + let casePartialBlockNode = (bNode: Parse.blockNode) => { + let blocksOrTags = bNode["blocks"]->Belt.Array.map(toTagOrNode) + let completed = Js.Array2.concat(blocksOrTags, [BlockTag(ExportVariablesExpression)]) + completed->caseTagOrNodes + } + + let casePartialExpression = (node: Parse.node) => { + let completed = [BlockNode(node), BlockTag(ExportVariablesExpression)] + + completed->caseTagOrNodes + } + + let rFinalExpression: result = switch typedMathJsNode { + | MjBlockNode(bNode) => casePartialBlockNode(bNode) + | _ => casePartialExpression(mathJsNode) + } + rFinalExpression + }) +} + +let fromOuterNode = (mathJsNode: Parse.node): result => { + Parse.castNodeType(mathJsNode)->Result.flatMap(typedMathJsNode => { + let casePartialBlockNode = (bNode: Parse.blockNode) => { + let blocksOrTags = bNode["blocks"]->Belt.Array.map(toTagOrNode) + let completed = blocksOrTags + completed->caseTagOrNodes + } + + let casePartialExpression = (node: Parse.node) => { + let completed = [BlockNode(node)] + completed->caseTagOrNodes + } + + let rFinalExpression: result = switch typedMathJsNode { + | MjBlockNode(bNode) => casePartialBlockNode(bNode) + | _ => casePartialExpression(mathJsNode) + } + rFinalExpression + }) +} diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExpressionValue.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExpressionValue.res index 2a8dc2e1..381cc654 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExpressionValue.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExpressionValue.res @@ -10,12 +10,15 @@ type rec expressionValue = | EvArray(array) | EvBool(bool) | EvCall(string) // External function call - | EvDistribution(GenericDist_Types.genericDist) + | EvDistribution(DistributionTypes.genericDist) | EvNumber(float) | EvRecord(Js.Dict.t) | EvString(string) | EvSymbol(string) +@genType +type externalBindings = Js.Dict.t + type functionCall = (string, array) let rec toString = aValue => @@ -33,17 +36,18 @@ let rec toString = aValue => ->Js.String.concatMany("") `[${args}]` } - | EvRecord(aRecord) => { - let pairs = - aRecord - ->Js.Dict.entries - ->Belt.Array.map(((eachKey, eachValue)) => `${eachKey}: ${toString(eachValue)}`) - ->Extra_Array.interperse(", ") - ->Js.String.concatMany("") - `{${pairs}}` - } + | EvRecord(aRecord) => aRecord->toStringRecord | EvDistribution(dist) => GenericDist.toString(dist) } +and toStringRecord = aRecord => { + let pairs = + aRecord + ->Js.Dict.entries + ->Belt.Array.map(((eachKey, eachValue)) => `${eachKey}: ${toString(eachValue)}`) + ->Extra_Array.interperse(", ") + ->Js.String.concatMany("") + `{${pairs}}` +} let toStringWithType = aValue => switch aValue { @@ -68,3 +72,9 @@ let toStringResult = x => | Ok(a) => `Ok(${toString(a)})` | Error(m) => `Error(${ErrorValue.errorToString(m)})` } + +let toStringResultRecord = x => + switch x { + | Ok(a) => `Ok(${toStringRecord(a)})` + | Error(m) => `Error(${ErrorValue.errorToString(m)})` + } diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalLibrary.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalLibrary.res index 063fd472..84d37d95 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalLibrary.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalLibrary.res @@ -2,13 +2,13 @@ module ExpressionValue = ReducerInterface_ExpressionValue type expressionValue = ExpressionValue.expressionValue -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} -} +// 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 diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res index f7381f07..ab76f469 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res @@ -1,12 +1,10 @@ module ExpressionValue = ReducerInterface_ExpressionValue type expressionValue = ReducerInterface_ExpressionValue.expressionValue -let defaultSampleCount = 10000 - let runGenericOperation = DistributionOperation.run( ~env={ - sampleCount: defaultSampleCount, - xyPointLength: 1000, + sampleCount: MagicNumbers.Environment.defaultSampleCount, + xyPointLength: MagicNumbers.Environment.defaultXYPointLength, }, ) @@ -24,13 +22,12 @@ module Helpers = { | "dotPow" => #Power | "multiply" => #Multiply | "dotMultiply" => #Multiply - | "dotLog" => #Logarithm | _ => #Multiply } let catchAndConvertTwoArgsToDists = (args: array): option<( - GenericDist_Types.genericDist, - GenericDist_Types.genericDist, + DistributionTypes.genericDist, + DistributionTypes.genericDist, )> => { switch args { | [EvDistribution(a), EvDistribution(b)] => Some((a, b)) @@ -41,33 +38,41 @@ module Helpers = { } let toFloatFn = ( - fnCall: GenericDist_Types.Operation.toFloat, - dist: GenericDist_Types.genericDist, + fnCall: DistributionTypes.DistributionOperation.toFloat, + dist: DistributionTypes.genericDist, ) => { - FromDist(GenericDist_Types.Operation.ToFloat(fnCall), dist)->runGenericOperation->Some + FromDist(DistributionTypes.DistributionOperation.ToFloat(fnCall), dist) + ->runGenericOperation + ->Some } let toStringFn = ( - fnCall: GenericDist_Types.Operation.toString, - dist: GenericDist_Types.genericDist, + fnCall: DistributionTypes.DistributionOperation.toString, + dist: DistributionTypes.genericDist, ) => { - FromDist(GenericDist_Types.Operation.ToString(fnCall), dist)->runGenericOperation->Some + FromDist(DistributionTypes.DistributionOperation.ToString(fnCall), dist) + ->runGenericOperation + ->Some } let toBoolFn = ( - fnCall: GenericDist_Types.Operation.toBool, - dist: GenericDist_Types.genericDist, + fnCall: DistributionTypes.DistributionOperation.toBool, + dist: DistributionTypes.genericDist, ) => { - FromDist(GenericDist_Types.Operation.ToBool(fnCall), dist)->runGenericOperation->Some + FromDist(DistributionTypes.DistributionOperation.ToBool(fnCall), dist) + ->runGenericOperation + ->Some } - let toDistFn = (fnCall: GenericDist_Types.Operation.toDist, dist) => { - FromDist(GenericDist_Types.Operation.ToDist(fnCall), dist)->runGenericOperation->Some + let toDistFn = (fnCall: DistributionTypes.DistributionOperation.toDist, dist) => { + FromDist(DistributionTypes.DistributionOperation.ToDist(fnCall), dist) + ->runGenericOperation + ->Some } let twoDiststoDistFn = (direction, arithmetic, dist1, dist2) => { FromDist( - GenericDist_Types.Operation.ToDistCombination( + DistributionTypes.DistributionOperation.ToDistCombination( direction, arithmeticMap(arithmetic), #Dist(dist2), @@ -84,7 +89,7 @@ module Helpers = { let parseNumberArray = (ags: array): Belt.Result.t, string> => E.A.fmap(parseNumber, ags) |> E.A.R.firstErrorOrOpen - let parseDist = (args: expressionValue): Belt.Result.t => + let parseDist = (args: expressionValue): Belt.Result.t => switch args { | EvDistribution(x) => Ok(x) | EvNumber(x) => Ok(GenericDist.fromFloat(x)) @@ -92,12 +97,12 @@ module Helpers = { } let parseDistributionArray = (ags: array): Belt.Result.t< - array, + array, string, > => E.A.fmap(parseDist, ags) |> E.A.R.firstErrorOrOpen let mixtureWithGivenWeights = ( - distributions: array, + distributions: array, weights: array, ): DistributionOperation.outputType => E.A.length(distributions) == E.A.length(weights) @@ -107,7 +112,7 @@ module Helpers = { ) let mixtureWithDefaultWeights = ( - distributions: array, + distributions: array, ): DistributionOperation.outputType => { let length = E.A.length(distributions) let weights = Belt.Array.make(length, 1.0 /. Belt.Int.toFloat(length)) @@ -126,7 +131,7 @@ module Helpers = { | Error(err) => GenDistError(ArgumentError(err)) } } - | Some(EvDistribution(b)) => + | Some(EvDistribution(_)) => switch parseDistributionArray(args) { | Ok(distributions) => mixtureWithDefaultWeights(distributions) | Error(err) => GenDistError(ArgumentError(err)) @@ -149,6 +154,7 @@ module SymbolicConstructors = { | "uniform" => Ok(SymbolicDist.Uniform.make) | "beta" => Ok(SymbolicDist.Beta.make) | "lognormal" => Ok(SymbolicDist.Lognormal.make) + | "cauchy" => Ok(SymbolicDist.Cauchy.make) | "to" => Ok(SymbolicDist.From90thPercentile.make) | _ => Error("Unreachable state") } @@ -164,14 +170,10 @@ module SymbolicConstructors = { ): option => switch symbolicResult { | Ok(r) => Some(Dist(Symbolic(r))) - | Error(r) => Some(GenDistError(Other(r))) + | Error(r) => Some(GenDistError(OtherError(r))) } } -module Math = { - let e = 2.718281828459 -} - let dispatchToGenericOutput = (call: ExpressionValue.functionCall): option< DistributionOperation.outputType, > => { @@ -182,7 +184,7 @@ let dispatchToGenericOutput = (call: ExpressionValue.functionCall): option< ->E.R.bind(r => r(f1)) ->SymbolicConstructors.symbolicResultToOutput | ( - ("normal" | "uniform" | "beta" | "lognormal" | "to") as fnName, + ("normal" | "uniform" | "beta" | "lognormal" | "cauchy" | "to") as fnName, [EvNumber(f1), EvNumber(f2)], ) => SymbolicConstructors.twoFloat(fnName) @@ -200,7 +202,12 @@ let dispatchToGenericOutput = (call: ExpressionValue.functionCall): option< Helpers.toStringFn(ToSparkline(Belt.Float.toInt(n)), dist) | ("exp", [EvDistribution(a)]) => // https://mathjs.org/docs/reference/functions/exp.html - Helpers.twoDiststoDistFn(Algebraic, "pow", GenericDist.fromFloat(Math.e), a)->Some + Helpers.twoDiststoDistFn( + Algebraic(AsDefault), + "pow", + GenericDist.fromFloat(MagicNumbers.Math.e), + a, + )->Some | ("normalize", [EvDistribution(dist)]) => Helpers.toDistFn(Normalize, dist) | ("isNormalized", [EvDistribution(dist)]) => Helpers.toBoolFn(IsNormalized, dist) | ("toPointSet", [EvDistribution(dist)]) => Helpers.toDistFn(ToPointSet, dist) @@ -210,7 +217,7 @@ let dispatchToGenericOutput = (call: ExpressionValue.functionCall): option< | ("toSampleSet", [EvDistribution(dist), EvNumber(float)]) => Helpers.toDistFn(ToSampleSet(Belt.Int.fromFloat(float)), dist) | ("toSampleSet", [EvDistribution(dist)]) => - Helpers.toDistFn(ToSampleSet(defaultSampleCount), dist) + Helpers.toDistFn(ToSampleSet(MagicNumbers.Environment.defaultSampleCount), dist) | ("inspect", [EvDistribution(dist)]) => Helpers.toDistFn(Inspect, dist) | ("truncateLeft", [EvDistribution(dist), EvNumber(float)]) => Helpers.toDistFn(Truncate(Some(float), None), dist) @@ -220,31 +227,38 @@ let dispatchToGenericOutput = (call: ExpressionValue.functionCall): option< Helpers.toDistFn(Truncate(Some(float1), Some(float2)), dist) | ("mx" | "mixture", args) => Helpers.mixture(args)->Some | ("log", [EvDistribution(a)]) => - Helpers.twoDiststoDistFn(Algebraic, "log", a, GenericDist.fromFloat(Math.e))->Some + Helpers.twoDiststoDistFn( + Algebraic(AsDefault), + "log", + a, + GenericDist.fromFloat(MagicNumbers.Math.e), + )->Some | ("log10", [EvDistribution(a)]) => - Helpers.twoDiststoDistFn(Algebraic, "log", a, GenericDist.fromFloat(10.0))->Some + Helpers.twoDiststoDistFn(Algebraic(AsDefault), "log", a, GenericDist.fromFloat(10.0))->Some | ("unaryMinus", [EvDistribution(a)]) => - Helpers.twoDiststoDistFn(Algebraic, "multiply", a, GenericDist.fromFloat(-1.0))->Some - | (("add" | "multiply" | "subtract" | "divide" | "pow" | "log") as arithmetic, [a, b] as args) => + Helpers.twoDiststoDistFn(Algebraic(AsDefault), "multiply", a, GenericDist.fromFloat(-1.0))->Some + | (("add" | "multiply" | "subtract" | "divide" | "pow" | "log") as arithmetic, [_, _] as args) => Helpers.catchAndConvertTwoArgsToDists(args)->E.O2.fmap(((fst, snd)) => - Helpers.twoDiststoDistFn(Algebraic, arithmetic, fst, snd) + Helpers.twoDiststoDistFn(Algebraic(AsDefault), arithmetic, fst, snd) ) | ( ("dotAdd" | "dotMultiply" | "dotSubtract" | "dotDivide" - | "dotPow" - | "dotLog") as arithmetic, - [a, b] as args, + | "dotPow") as arithmetic, + [_, _] as args, ) => Helpers.catchAndConvertTwoArgsToDists(args)->E.O2.fmap(((fst, snd)) => Helpers.twoDiststoDistFn(Pointwise, arithmetic, fst, snd) ) - | ("dotLog", [EvDistribution(a)]) => - Helpers.twoDiststoDistFn(Pointwise, "dotLog", a, GenericDist.fromFloat(Math.e))->Some | ("dotExp", [EvDistribution(a)]) => - Helpers.twoDiststoDistFn(Pointwise, "dotPow", GenericDist.fromFloat(Math.e), a)->Some + Helpers.twoDiststoDistFn( + Pointwise, + "dotPow", + GenericDist.fromFloat(MagicNumbers.Math.e), + a, + )->Some | _ => None } } @@ -258,12 +272,7 @@ let genericOutputToReducerValue = (o: DistributionOperation.outputType): result< | Float(d) => Ok(EvNumber(d)) | String(d) => Ok(EvString(d)) | Bool(d) => Ok(EvBool(d)) - | GenDistError(NotYetImplemented) => Error(RETodo("Function not yet implemented")) - | GenDistError(Unreachable) => Error(RETodo("Unreachable")) - | GenDistError(DistributionVerticalShiftIsInvalid) => - Error(RETodo("Distribution Vertical Shift Is Invalid")) - | GenDistError(ArgumentError(err)) => Error(RETodo("Argument Error: " ++ err)) - | GenDistError(Other(s)) => Error(RETodo(s)) + | GenDistError(err) => Error(REDistributionError(err)) } let dispatch = call => { diff --git a/packages/squiggle-lang/src/rescript/TypescriptInterface.res b/packages/squiggle-lang/src/rescript/TypescriptInterface.res index 6a0b7bcf..fc381729 100644 --- a/packages/squiggle-lang/src/rescript/TypescriptInterface.res +++ b/packages/squiggle-lang/src/rescript/TypescriptInterface.res @@ -31,6 +31,9 @@ let makeSampleSetDist = SampleSetDist.make @genType let evaluate = Reducer.evaluate +@genType +let evaluateUsingExternalBindings = Reducer.evaluateUsingExternalBindings + @genType type expressionValue = ReducerInterface_ExpressionValue.expressionValue @@ -53,4 +56,4 @@ type continuousShape = PointSetTypes.continuousShape let errorValueToString = Reducer_ErrorValue.errorToString @genType -let distributionErrorToString = GenericDist_Types.Error.toString +let distributionErrorToString = DistributionTypes.Error.toString diff --git a/packages/squiggle-lang/src/rescript/Utility/E.res b/packages/squiggle-lang/src/rescript/Utility/E.res index 24e7947c..a110f1b7 100644 --- a/packages/squiggle-lang/src/rescript/Utility/E.res +++ b/packages/squiggle-lang/src/rescript/Utility/E.res @@ -1,4 +1,7 @@ -open Rationale.Function.Infix +/* +Some functions from modules `L`, `O`, and `R` below were copied directly from +running `rescript convert -all` on Rationale https://github.com/jonlaing/rationale +*/ module FloatFloatMap = { module Id = Belt.Id.MakeComparable({ type t = float @@ -8,7 +11,7 @@ module FloatFloatMap = { type t = Belt.MutableMap.t let fromArray = (ar: array<(float, float)>) => Belt.MutableMap.fromArray(ar, ~id=module(Id)) - let toArray = (t: t) => Belt.MutableMap.toArray(t) + let toArray = (t: t): array<(float, float)> => Belt.MutableMap.toArray(t) let empty = () => Belt.MutableMap.make(~id=module(Id)) let increment = (el, t: t) => Belt.MutableMap.update(t, el, x => @@ -20,6 +23,10 @@ module FloatFloatMap = { let get = (el, t: t) => Belt.MutableMap.get(t, el) let fmap = (fn, t: t) => Belt.MutableMap.map(t, fn) + let partition = (fn, t: t) => { + let (match, noMatch) = Belt.Array.partition(toArray(t), fn) + (fromArray(match), fromArray(noMatch)) + } } module Int = { @@ -28,7 +35,7 @@ module Int = { } /* Utils */ module U = { - let isEqual = (a, b) => a == b + let isEqual = \"==" let toA = a => [a] let id = e => e } @@ -51,17 +58,59 @@ module O = { | None => rFn() } () - let fmap = Rationale.Option.fmap - let bind = Rationale.Option.bind - let default = Rationale.Option.default - let isSome = Rationale.Option.isSome - let isNone = Rationale.Option.isNone - let toExn = Rationale.Option.toExn - let some = Rationale.Option.some - let firstSome = Rationale.Option.firstSome - let toExt = Rationale.Option.toExn // wanna flag this-- looks like a typo but `Rationale.OptiontoExt` doesn't exist. - let flatApply = (fn, b) => Rationale.Option.apply(fn, Some(b)) |> Rationale.Option.flatten - let flatten = Rationale.Option.flatten + let fmap = (f: 'a => 'b, x: option<'a>): option<'b> => { + switch x { + | None => None + | Some(x') => Some(f(x')) + } + } + let bind = (o, f) => + switch o { + | None => None + | Some(a) => f(a) + } + let default = (d, o) => + switch o { + | None => d + | Some(a) => a + } + let isSome = o => + switch o { + | Some(_) => true + | _ => false + } + let isNone = o => + switch o { + | None => true + | _ => false + } + let toExn = (err, o) => + switch o { + | None => raise(Failure(err)) + | Some(a) => a + } + + let some = a => Some(a) + let firstSome = (a, b) => + switch a { + | None => b + | _ => a + } + + let toExt = toExn + + let flatten = o => + switch o { + | None => None + | Some(x) => x + } + + let apply = (o, a) => + switch o { + | Some(f) => bind(a, b => some(f(b))) + | _ => None + } + let flatApply = (fn, b) => apply(fn, Some(b)) |> flatten let toBool = opt => switch opt { @@ -109,6 +158,11 @@ module O2 = { /* Functions */ module F = { + let pipe = (f, g, x) => g(f(x)) + let compose = (f, g, x) => f(g(x)) + let flip = (f, a, b) => f(b, a) + let always = (x, _y) => x + let apply = (a, e) => a |> e let flatten2Callbacks = (fn1, fn2, fnlast) => @@ -152,13 +206,35 @@ module I = { let toString = Js.Int.toString } +exception Assertion(string) + /* R for Result */ module R = { - let result = Rationale.Result.result + open Belt.Result + let result = (okF, errF, r) => + switch r { + | Ok(a) => okF(a) + | Error(err) => errF(err) + } let id = e => e |> result(U.id, U.id) - let fmap = Rationale.Result.fmap - let bind = Rationale.Result.bind - let toExn = Belt.Result.getExn + let fmap = (f: 'a => 'b, r: result<'a, 'c>): result<'b, 'c> => { + switch r { + | Ok(r') => Ok(f(r')) + | Error(err) => Error(err) + } + } + let bind = (r, f) => + switch r { + | Ok(a) => f(a) + | Error(err) => Error(err) + } + + let toExn = (msg: string, x: result<'a, 'b>): 'a => + switch x { + | Ok(r) => r + | Error(_) => raise(Assertion(msg)) + } + let default = (default, res: Belt.Result.t<'a, 'b>) => switch res { | Ok(r) => r @@ -179,13 +255,17 @@ module R = { let errorIfCondition = (errorCondition, errorMessage, r) => errorCondition(r) ? Error(errorMessage) : Ok(r) - let ap = Rationale.Result.ap + let ap = (r, a) => + switch r { + | Ok(f) => Ok(f(a)) + | Error(err) => Error(err) + } let ap' = (r, a) => switch r { | Ok(f) => fmap(f, a) | Error(err) => Error(err) } - // (a1 -> a2 -> r) -> m a1 -> m a2 -> m r // not in Rationale + let liftM2: (('a, 'b) => 'c, result<'a, 'd>, result<'b, 'd>) => result<'c, 'd> = (op, xR, yR) => { ap'(fmap(op, xR), yR) } @@ -210,10 +290,10 @@ module R2 = { let bind = (a, b) => R.bind(b, a) //Converts result type to change error type only - let errMap = (a, map) => + let errMap = (a: result<'a, 'b>, map: 'b => 'c): result<'a, 'c> => switch a { | Ok(r) => Ok(r) - | Error(e) => map(e) + | Error(e) => Error(map(e)) } let fmap2 = (xR, f) => @@ -235,7 +315,7 @@ module S = { } module J = { - let toString = \"||>"(Js.Json.decodeString, O.default("")) + let toString = F.pipe(Js.Json.decodeString, O.default("")) let fromString = Js.Json.string let fromNumber = Js.Json.number @@ -248,7 +328,7 @@ module J = { let toString = (str: option<'a>) => switch str { - | Some(str) => Some(str |> \"||>"(Js.Json.decodeString, O.default(""))) + | Some(str) => Some(str |> F.pipe(Js.Json.decodeString, O.default(""))) | _ => None } } @@ -263,34 +343,132 @@ module JsDate = { /* List */ module L = { + module Util = { + let eq = (a, b) => a == b + } let fmap = List.map let get = Belt.List.get let toArray = Array.of_list let fmapi = List.mapi let concat = List.concat - let drop = Rationale.RList.drop - let remove = Rationale.RList.remove + let concat' = (xs, ys) => List.append(ys, xs) + + let rec drop = (i, xs) => + switch (i, xs) { + | (_, list{}) => list{} + | (i, _) if i <= 0 => xs + | (i, list{_, ...b}) => drop(i - 1, b) + } + + let append = (a, xs) => List.append(xs, list{a}) + let take = { + let rec loop = (i, xs, acc) => + switch (i, xs) { + | (i, _) if i <= 0 => acc + | (_, list{}) => acc + | (i, list{a, ...b}) => loop(i - 1, b, append(a, acc)) + } + (i, xs) => loop(i, xs, list{}) + } + let takeLast = (i, xs) => List.rev(xs) |> take(i) |> List.rev + + let splitAt = (i, xs) => (take(i, xs), takeLast(List.length(xs) - i, xs)) + let remove = (i, n, xs) => { + let (a, b) = splitAt(i, xs) + \"@"(a, drop(n, b)) + } + let find = List.find let filter = List.filter let for_all = List.for_all let exists = List.exists let sort = List.sort let length = List.length - let filter_opt = Rationale.RList.filter_opt - let uniqBy = Rationale.RList.uniqBy - let join = Rationale.RList.join - let head = Rationale.RList.head - let uniq = Rationale.RList.uniq + + let filter_opt = xs => { + let rec loop = (l, acc) => + switch l { + | list{} => acc + | list{hd, ...tl} => + switch hd { + | None => loop(tl, acc) + | Some(x) => loop(tl, list{x, ...acc}) + } + } + List.rev(loop(xs, list{})) + } + + let containsWith = f => List.exists(f) + + let uniqWithBy = (eq, f, xs) => + List.fold_left( + ((acc, tacc), v) => + containsWith(eq(f(v)), tacc) ? (acc, tacc) : (append(v, acc), append(f(v), tacc)), + (list{}, list{}), + xs, + ) |> fst + + let uniqBy = (f, xs) => uniqWithBy(Util.eq, f, xs) + let join = j => List.fold_left((acc, v) => String.length(acc) == 0 ? v : acc ++ (j ++ v), "") + + let head = xs => + switch List.hd(xs) { + | exception _ => None + | a => Some(a) + } + + let uniq = xs => uniqBy(x => x, xs) let flatten = List.flatten - let last = Rationale.RList.last + let last = xs => xs |> List.rev |> head let append = List.append let getBy = Belt.List.getBy - let dropLast = Rationale.RList.dropLast - let contains = Rationale.RList.contains - let without = Rationale.RList.without - let update = Rationale.RList.update + let dropLast = (i, xs) => take(List.length(xs) - i, xs) + let containsWith = f => List.exists(f) + let contains = x => containsWith(Util.eq(x)) + + let reject = pred => List.filter(x => !pred(x)) + let tail = xs => + switch List.tl(xs) { + | exception _ => None + | a => Some(a) + } + + let init = xs => { + O.fmap(List.rev, xs |> List.rev |> tail) + } + + let singleton = (x: 'a): list<'a> => list{x} + + let adjust = (f, i, xs) => { + let (a, b) = splitAt(i + 1, xs) + switch a { + | _ if i < 0 => xs + | _ if i >= List.length(xs) => xs + | list{} => b + | list{a} => list{f(a), ...b} + | a => + O.fmap( + concat'(b), + O.bind(init(a), x => + O.fmap(F.flip(append, x), O.fmap(fmap(f), O.fmap(singleton, last(a)))) + ), + ) |> O.default(xs) + } + } + + let without = (exclude, xs) => reject(x => contains(x, exclude), xs) + let update = (x, i, xs) => adjust(F.always(x), i, xs) let iter = List.iter - let findIndex = Rationale.RList.findIndex + + let findIndex = { + let rec loop = (pred, xs, i) => + switch xs { + | list{} => None + | list{a, ...b} => pred(a) ? Some(i) : loop(pred, b, i + 1) + } + (pred, xs) => loop(pred, xs, 0) + } + let headSafe = Belt.List.head let tailSafe = Belt.List.tail let headExn = Belt.List.headExn @@ -340,8 +518,6 @@ module A = { let reduce = Belt.Array.reduce let reducei = Belt.Array.reduceWithIndex let isEmpty = r => length(r) < 1 - let min = a => get(a, 0) |> O.fmap(first => Belt.Array.reduce(a, first, (i, j) => i < j ? i : j)) - let max = a => get(a, 0) |> O.fmap(first => Belt.Array.reduce(a, first, (i, j) => i > j ? i : j)) let stableSortBy = Belt.SortArray.stableSortBy let toRanges = (a: array<'a>) => switch a |> Belt.Array.length { @@ -354,9 +530,12 @@ module A = { Belt.Array.getUnsafe(a, index), Belt.Array.getUnsafe(a, index + 1), )) - |> Rationale.Result.return + |> (x => Ok(x)) } + let tail = Belt.Array.sliceToEnd(_, 1) + + let zip = Belt.Array.zip // This zips while taking the longest elements of each array. let zipMaxLength = (array1, array2) => { let maxLength = Int.max(length(array1), length(array2)) @@ -415,8 +594,8 @@ module A = { module O = { let concatSomes = (optionals: array>): array<'a> => optionals - |> Js.Array.filter(Rationale.Option.isSome) - |> Js.Array.map(Rationale.Option.toExn("Warning: This should not have happened")) + |> Js.Array.filter(O.isSome) + |> Js.Array.map(O.toExn("Warning: This should not have happened")) let defaultEmpty = (o: option>): array<'a> => switch o { | Some(o) => o @@ -438,6 +617,32 @@ module A = { r |> Belt.Array.map(_, r => Belt.Result.getExn(r)) bringErrorUp |> Belt.Result.map(_, forceOpen) } + let filterOk = (x: array>): array<'a> => fmap(R.toOption, x)->O.concatSomes + + let forM = (x: array<'a>, fn: 'a => result<'b, 'c>): result, 'c> => + firstErrorOrOpen(fmap(fn, x)) + + let foldM = (fn: ('c, 'a) => result<'b, 'e>, init: 'c, x: array<'a>): result<'c, 'e> => { + let acc = ref(init) + let final = ref(Ok()) + let break = ref(false) + let i = ref(0) + + while break.contents != true && i.contents < length(x) { + switch fn(acc.contents, x[i.contents]) { + | Ok(r) => acc := r + | Error(err) => { + final := Error(err) + break := true + } + } + i := i.contents + 1 + } + switch final.contents { + | Ok(_) => Ok(acc.contents) + | Error(err) => Error(err) + } + } } module Sorted = { @@ -448,8 +653,11 @@ module A = { | (Some(min), Some(max)) => Some(max -. min) | _ => None } + + let floatCompare: (float, float) => int = compare + let binarySearchFirstElementGreaterIndex = (ar: array<'a>, el: 'a) => { - let el = Belt.SortArray.binarySearchBy(ar, el, compare) + let el = Belt.SortArray.binarySearchBy(ar, el, floatCompare) let el = el < 0 ? el * -1 - 1 : el switch el { | e if e >= length(ar) => #overMax @@ -460,25 +668,33 @@ module A = { let concat = (t1: array<'a>, t2: array<'a>) => { let ts = Belt.Array.concat(t1, t2) - ts |> Array.fast_sort(compare) + ts |> Array.fast_sort(floatCompare) ts } let concatMany = (t1: array>) => { let ts = Belt.Array.concatMany(t1) - ts |> Array.fast_sort(compare) + ts |> Array.fast_sort(floatCompare) ts } module Floats = { + let isSorted = (ar: array): bool => + reduce(zip(ar, tail(ar)), true, (acc, (first, second)) => acc && first < second) + let makeIncrementalUp = (a, b) => Array.make(b - a + 1, a) |> Array.mapi((i, c) => c + i) |> Belt.Array.map(_, float_of_int) let makeIncrementalDown = (a, b) => Array.make(a - b + 1, a) |> Array.mapi((i, c) => c - i) |> Belt.Array.map(_, float_of_int) - let split = (sortedArray: array) => { - let continuous = [] + /* + This function goes through a sorted array and divides it into two different clusters: + continuous samples and discrete samples. The discrete samples are stored in a mutable map. + Samples are thought to be discrete if they have any duplicates. + */ + let _splitContinuousAndDiscreteForDuplicates = (sortedArray: array) => { + let continuous: array = [] let discrete = FloatFloatMap.empty() Belt.Array.forEachWithIndex(sortedArray, (index, element) => { let maxIndex = (sortedArray |> Array.length) - 1 @@ -499,14 +715,48 @@ module A = { (continuous, discrete) } + + /* + This function works very similarly to splitContinuousAndDiscreteForDuplicates. The one major difference + is that you can specify a minDiscreteWeight. If the min discreet weight is 4, that would mean that + at least four elements needed from a specific value for that to be kept as discrete. This is important + because in some cases, we can expect that some common elements will be generated by regular operations. + The final continous array will be sorted. + */ + let splitContinuousAndDiscreteForMinWeight = ( + sortedArray: array, + ~minDiscreteWeight: int, + ) => { + let (continuous, discrete) = _splitContinuousAndDiscreteForDuplicates(sortedArray) + let keepFn = v => Belt.Float.toInt(v) >= minDiscreteWeight + let (discreteToKeep, discreteToIntegrate) = FloatFloatMap.partition( + ((_, v)) => keepFn(v), + discrete, + ) + let newContinousSamples = + discreteToIntegrate->FloatFloatMap.toArray + |> fmap(((k, v)) => Belt.Array.makeBy(Belt.Float.toInt(v), _ => k)) + |> Belt.Array.concatMany + let newContinuous = concat(continuous, newContinousSamples) + newContinuous |> Array.fast_sort(floatCompare) + (newContinuous, discreteToKeep) + } } } module Floats = { - let sum = Belt.Array.reduce(_, 0., (i, j) => i +. j) - let mean = a => sum(a) /. (Array.length(a) |> float_of_int) + let mean = Jstat.mean + let geomean = Jstat.geomean + let mode = Jstat.mode + let variance = Jstat.variance + let stdev = Jstat.stdev + let sum = Jstat.sum let random = Js.Math.random_int + //Passing true for the exclusive parameter excludes both endpoints of the range. + //https://jstat.github.io/all.html + let percentile = (a, b) => Jstat.percentile(a, b, false) + // Gives an array with all the differences between values // diff([1,5,3,7]) = [4,-2,4] let diff = (arr: array): array => @@ -525,6 +775,9 @@ module A = { let diff = (max -. min) /. Belt.Float.fromInt(n - 1) Belt.Array.makeBy(n, i => min +. Belt.Float.fromInt(i) *. diff) } + + let min = Js.Math.minMany_float + let max = Js.Math.maxMany_float } } @@ -536,7 +789,7 @@ module A2 = { module JsArray = { let concatSomes = (optionals: Js.Array.t>): Js.Array.t<'a> => optionals - |> Js.Array.filter(Rationale.Option.isSome) - |> Js.Array.map(Rationale.Option.toExn("Warning: This should not have happened")) + |> Js.Array.filter(O.isSome) + |> Js.Array.map(O.toExn("Warning: This should not have happened")) let filter = Js.Array.filter } diff --git a/packages/squiggle-lang/src/rescript/Utility/Operation.res b/packages/squiggle-lang/src/rescript/Utility/Operation.res index 6fb3b24b..4a1ef91a 100644 --- a/packages/squiggle-lang/src/rescript/Utility/Operation.res +++ b/packages/squiggle-lang/src/rescript/Utility/Operation.res @@ -9,6 +9,13 @@ type algebraicOperation = [ | #Power | #Logarithm ] + +type convolutionOperation = [ + | #Add + | #Multiply + | #Subtract +] + @genType type pointwiseOperation = [#Add | #Multiply | #Power] type scaleOperation = [#Multiply | #Power | #Logarithm | #Divide] @@ -20,22 +27,81 @@ type distToFloatOperation = [ | #Sample ] -module Algebraic = { - type t = algebraicOperation +module Convolution = { + type t = convolutionOperation + //Only a selection of operations are supported by convolution. + let fromAlgebraicOperation = (op: algebraicOperation): option => + switch op { + | #Add => Some(#Add) + | #Subtract => Some(#Subtract) + | #Multiply => Some(#Multiply) + | #Divide | #Power | #Logarithm => None + } + + let canDoAlgebraicOperation = (op: algebraicOperation): bool => + fromAlgebraicOperation(op)->E.O.isSome + let toFn: (t, float, float) => float = x => switch x { | #Add => \"+." | #Subtract => \"-." | #Multiply => \"*." - | #Power => \"**" - | #Divide => \"/." - | #Logarithm => (a, b) => log(a) /. log(b) } +} - let applyFn = (t, f1, f2) => - switch (t, f1, f2) { - | (#Divide, _, 0.) => Error("Cannot divide $v1 by zero.") - | _ => Ok(toFn(t, f1, f2)) +type operationError = + | DivisionByZeroError + | ComplexNumberError + +@genType +module Error = { + @genType + type t = operationError + + let toString = (err: t): string => + switch err { + | DivisionByZeroError => "Cannot divide by zero" + | ComplexNumberError => "Operation returned complex result" + } +} + +let power = (a: float, b: float): result => + if a >= 0.0 { + Ok(a ** b) + } else { + Error(ComplexNumberError) + } + +let divide = (a: float, b: float): result => + if b != 0.0 { + Ok(a /. b) + } else { + Error(DivisionByZeroError) + } + +let logarithm = (a: float, b: float): result => + if b == 1. { + Error(DivisionByZeroError) + } else if b == 0. { + Ok(0.) + } else if a > 0.0 && b > 0.0 { + Ok(log(a) /. log(b)) + } else { + Error(ComplexNumberError) + } + +@genType +module Algebraic = { + @genType + type t = algebraicOperation + let toFn: (t, float, float) => result = (x, a, b) => + switch x { + | #Add => Ok(a +. b) + | #Subtract => Ok(a -. b) + | #Multiply => Ok(a *. b) + | #Power => power(a, b) + | #Divide => divide(a, b) + | #Logarithm => logarithm(a, b) } let toString = x => @@ -79,12 +145,12 @@ module DistToFloat = { // Note that different logarithms don't really do anything. module Scale = { type t = scaleOperation - let toFn = x => + let toFn = (x: t, a: float, b: float): result => switch x { - | #Multiply => \"*." - | #Divide => \"/." - | #Power => \"**" - | #Logarithm => (a, b) => log(a) /. log(b) + | #Multiply => Ok(a *. b) + | #Divide => divide(a, b) + | #Power => power(a, b) + | #Logarithm => logarithm(a, b) } let format = (operation: t, value, scaleBy) => diff --git a/packages/squiggle-lang/src/rescript/Utility/Sparklines.res b/packages/squiggle-lang/src/rescript/Utility/Sparklines.res index 12d509fa..72fcfc11 100644 --- a/packages/squiggle-lang/src/rescript/Utility/Sparklines.res +++ b/packages/squiggle-lang/src/rescript/Utility/Sparklines.res @@ -16,7 +16,7 @@ let create = (relativeHeights: array, ~maximum=?, ()) => { if E.A.length(relativeHeights) === 0 { "" } else { - let maximum = maximum->E.O2.default(E.A.max(relativeHeights)->E.O2.toExn("")) + let maximum = maximum->E.O2.default(E.A.Floats.max(relativeHeights)) relativeHeights ->E.A2.fmap(_heightToTickIndex(maximum)) diff --git a/packages/squiggle-lang/src/rescript/Utility/XYShape.res b/packages/squiggle-lang/src/rescript/Utility/XYShape.res index aba9ec80..97974884 100644 --- a/packages/squiggle-lang/src/rescript/Utility/XYShape.res +++ b/packages/squiggle-lang/src/rescript/Utility/XYShape.res @@ -43,6 +43,10 @@ module T = { let xTotalRange = (t: t) => maxX(t) -. minX(t) let mapX = (fn, t: t): t => {xs: E.A.fmap(fn, t.xs), ys: t.ys} let mapY = (fn, t: t): t => {xs: t.xs, ys: E.A.fmap(fn, t.ys)} + let mapYResult = (fn: float => result, t: t): result => { + let mappedYs = E.A.fmap(fn, t.ys) + E.A.R.firstErrorOrOpen(mappedYs)->E.R2.fmap(y => {xs: t.xs, ys: y}) + } let square = mapX(x => x ** 2.0) let zip = ({xs, ys}: t) => Belt.Array.zip(xs, ys) let fromArray = ((xs, ys)): t => {xs: xs, ys: ys} @@ -60,8 +64,8 @@ module T = { module Ts = { type t = T.ts - let minX = (t: t) => t |> E.A.fmap(T.minX) |> E.A.min |> extImp - let maxX = (t: t) => t |> E.A.fmap(T.maxX) |> E.A.max |> extImp + let minX = (t: t) => t |> E.A.fmap(T.minX) |> E.A.Floats.min + let maxX = (t: t) => t |> E.A.fmap(T.maxX) |> E.A.Floats.max let equallyDividedXs = (t: t, newLength) => E.A.Floats.range(minX(t), maxX(t), newLength) let allXs = (t: t) => t |> E.A.fmap(T.xs) |> E.A.Sorted.concatMany } @@ -199,7 +203,7 @@ module XtoY = { /* Returns a between-points-interpolating function that can be used with PointwiseCombination.combine. For discrete distributions, the probability density between points is zero, so we just return zero here. */ - let discreteInterpolator: interpolator = (t: T.t, leftIndex: int, x: float) => 0.0 + let discreteInterpolator: interpolator = (_: T.t, _: int, _: float) => 0.0 } module XsConversion = { @@ -220,8 +224,8 @@ module XsConversion = { module Zipped = { type zipped = array<(float, float)> - let compareYs = ((_, y1), (_, y2)) => y1 > y2 ? 1 : 0 - let compareXs = ((x1, _), (x2, _)) => x1 > x2 ? 1 : 0 + let compareYs = ((_, y1): (float, float), (_, y2): (float, float)) => y1 > y2 ? 1 : 0 + let compareXs = ((x1, _): (float, float), (x2, _): (float, float)) => x1 > x2 ? 1 : 0 let sortByY = (t: zipped) => t |> E.A.stableSortBy(_, compareYs) let sortByX = (t: zipped) => t |> E.A.stableSortBy(_, compareXs) let filterByX = (testFn: float => bool, t: zipped) => t |> E.A.filter(((x, _)) => testFn(x)) @@ -229,7 +233,12 @@ module Zipped = { module PointwiseCombination = { // t1Interpolator and t2Interpolator are functions from XYShape.XtoY, e.g. linearBetweenPointsExtrapolateFlat. - let combine = %raw(` // : (float => float => float, T.t, T.t, bool) => T.t + let combine: ( + (float, float) => result, + interpolator, + T.t, + T.t, + ) => result = %raw(` // This function combines two xyShapes by looping through both of them simultaneously. // It always moves on to the next smallest x, whether that's in the first or second input's xs, // and interpolates the value on the other side, thus accumulating xs and ys. @@ -277,13 +286,28 @@ module PointwiseCombination = { } outX.push(x); - outY.push(fn(ya, yb)); + + // Here I check whether the operation was a success. If it was + // keep going. Otherwise, stop and throw the error back to user + let newY = fn(ya, yb); + if(newY.TAG === 0){ + outY.push(newY._0); + } + else { + return newY; + } } - return {xs: outX, ys: outY}; + return {TAG: 0, _0: {xs: outX, ys: outY}, [Symbol.for("name")]: "Ok"}; } `) + let addCombine = (interpolator: interpolator, t1: T.t, t2: T.t): T.t => + combine((a, b) => Ok(a +. b), interpolator, t1, t2)->E.R.toExn( + "Add operation should never fail", + _, + ) + let combineEvenXs = (~fn, ~xToYSelection, sampleCount, t1: T.t, t2: T.t) => switch (E.A.length(t1.xs), E.A.length(t2.xs)) { | (0, 0) => T.empty diff --git a/packages/website/.prettierignore b/packages/website/.prettierignore new file mode 100644 index 00000000..d858cd65 --- /dev/null +++ b/packages/website/.prettierignore @@ -0,0 +1,2 @@ +.docusaurus +build diff --git a/packages/website/docs/Discussions/Bugs.mdx b/packages/website/docs/Discussions/Bugs.mdx new file mode 100644 index 00000000..1aff87fe --- /dev/null +++ b/packages/website/docs/Discussions/Bugs.mdx @@ -0,0 +1,36 @@ +--- +title: "Known Bugs" +sidebar_position: 6 +--- + +import { SquiggleEditor } from "../../src/components/SquiggleEditor"; + +Much of the Squiggle math is imprecise. This can cause significant errors, so watch out. + +Below are some specific examples to watch for. We'll work on improving these over time and adding much better warnings and error management. + +## Mixtures of distributions with very different means + +If you take the pointwise mixture of two distributions with very different means, then the value of that gets fairly warped. + +In the following case, the mean of the mixture should be equal to the sum of the means of the parts. These are shown as the first two displayed variables. These variables diverge as the underlying distributions change. + + + +## Means of Sample Set Distributions + +The means of sample set distributions can vary dramatically, especially as the numbers get high. + + diff --git a/packages/website/docs/Discussions/Gallery.md b/packages/website/docs/Discussions/Gallery.md new file mode 100644 index 00000000..fee8f344 --- /dev/null +++ b/packages/website/docs/Discussions/Gallery.md @@ -0,0 +1,7 @@ +--- +sidebar_position: 6 +title: Gallery +--- + +- [Adjusting probabilities for the passage of time](https://www.lesswrong.com/s/rDe8QE5NvXcZYzgZ3/p/j8o6sgRerE3tqNWdj) by Nuño Sempere +- [GiveWell's GiveDirectly cost effectiveness analysis](https://observablehq.com/@hazelfire/givewells-givedirectly-cost-effectiveness-analysis) by Sam Nolan diff --git a/packages/website/docs/Discussions/Three-Types-Of-Distributions.md b/packages/website/docs/Discussions/Three-Formats-Of-Distributions.md similarity index 99% rename from packages/website/docs/Discussions/Three-Types-Of-Distributions.md rename to packages/website/docs/Discussions/Three-Formats-Of-Distributions.md index ffd99fc1..8ec5b88d 100644 --- a/packages/website/docs/Discussions/Three-Types-Of-Distributions.md +++ b/packages/website/docs/Discussions/Three-Formats-Of-Distributions.md @@ -1,12 +1,10 @@ --- sidebar_position: 5 +title: Three Formats of Distributions +author: Ozzie Gooen +date: 02-19-2022 --- -# Three Formats of Distributions - -_Author: Ozzie Gooen_ -_Written on: Feb 19, 2022_ - Probability distributions have several subtle possible formats. Three important ones that we deal with in Squiggle are symbolic, sample set, and graph formats. _Symbolic_ formats are just the math equations. `normal(5,3)` is the symbolic representation of a normal distribution. diff --git a/packages/website/docs/Features/Functions.mdx b/packages/website/docs/Features/Functions.mdx index 7e0d1fc6..e37ea315 100644 --- a/packages/website/docs/Features/Functions.mdx +++ b/packages/website/docs/Features/Functions.mdx @@ -1,12 +1,15 @@ --- +title: "Functions Reference" sidebar_position: 7 --- import { SquiggleEditor } from "../../src/components/SquiggleEditor"; -# Squiggle Functions Reference +_The source of truth for this document is [this file of code](https://github.com/quantified-uncertainty/squiggle/blob/develop/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res)_ -## Distributions +## Inventory distributions + +We provide starter distributions, computed symbolically. ### Normal distribution @@ -15,6 +18,10 @@ and standard deviation. +#### Validity + +- `sd > 0` + ### Uniform distribution The `uniform(low, high)` function creates a uniform distribution between the @@ -22,86 +29,271 @@ two given numbers. +#### Validity + +- `low < high` + ### Lognormal distribution The `lognormal(mu, sigma)` returns the log of a normal distribution with parameters -mu and sigma. The log of lognormal(mu, sigma) is a normal distribution with parameters -mean mu and standard deviation sigma. +`mu` and `sigma`. The log of `lognormal(mu, sigma)` is a normal distribution with mean `mu` and standard deviation `sigma`. -An alternative format is also available. The "to" notation creates a lognormal +An alternative format is also available. The `to` notation creates a lognormal distribution with a 90% confidence interval between the two numbers. We add -this convinience as lognormal distributions are commonly used in practice. +this convenience as lognormal distributions are commonly used in practice. +#### Future feature: + Furthermore, it's also possible to create a lognormal from it's actual mean and standard deviation, using `lognormalFromMeanAndStdDev`. +TODO: interpreter/parser doesn't provide this in current `develop` branch + +#### Validity + +- `sigma > 0` +- In `x to y` notation, `x < y` + ### Beta distribution -The `beta(a, b)` function creates a beta distribution with parameters a and b: +The `beta(a, b)` function creates a beta distribution with parameters `a` and `b`: - + + +#### Validity + +- `a > 0` +- `b > 0` +- Empirically, we have noticed that numerical instability arises when `a < 1` or `b < 1` ### Exponential distribution -The `exponential(mean)` function creates an exponential distribution with the given -mean. +The `exponential(rate)` function creates an exponential distribution with the given +rate. - + -### The Triangular distribution +#### Validity + +- `rate > 0` + +### Triangular distribution The `triangular(a,b,c)` function creates a triangular distribution with lower -bound a, mode b and upper bound c. +bound `a`, mode `b` and upper bound `c`. + +#### Validity + +- `a < b < c` -### Multimodal distriutions +### Scalar (constant dist) -The multimodal function combines 2 or more other distributions to create a weighted +Squiggle, when the context is right, automatically casts a float to a constant distribution. + +## Operating on distributions + +Here are the ways we combine distributions. + +### Mixture of distributions + +The `mixture` function combines 2 or more other distributions to create a weighted combination of the two. The first positional arguments represent the distributions to be combined, and the last argument is how much to weigh every distribution in the combination. - + It's possible to create discrete distributions using this method. - + As well as mixed distributions: - + -## Other Functions +An alias of `mixture` is `mx` -### PDF of a distribution +#### Validity -The `pdf(distribution, x)` function returns the density of a distribution at the +Using javascript's variable arguments notation, consider `mx(...dists, weights)`: + +- `dists.length == weights.length` + +### Addition + +A horizontal right shift + + + +### Subtraction + +A horizontal left shift + + + +### Multiplication + +TODO: provide intuition pump for the semantics + + + +We also provide concatenation of two distributions as a syntax sugar for `*` + + + +### Division + +TODO: provide intuition pump for the semantics + + + +### Exponentiation + +TODO: provide intuition pump for the semantics + + + +### Taking the base `e` exponential + + + +### Taking logarithms + + + + + +Base `x` + + + +#### Validity + +- `x` must be a scalar +- See [the current discourse](https://github.com/quantified-uncertainty/squiggle/issues/304) + +### Pointwise addition + +**Pointwise operations are done with `PointSetDist` internals rather than `SampleSetDist` internals**. + +TODO: this isn't in the new interpreter/parser yet. + + + +### Pointwise subtraction + +TODO: this isn't in the new interpreter/parser yet. + + + +### Pointwise multiplication + + + +### Pointwise division + + + +### Pointwise exponentiation + + + +## Standard functions on distributions + +### Probability density function + +The `pdf(dist, x)` function returns the density of a distribution at the given point x. -### Inverse of a distribution +#### Validity -The `inv(distribution, prob)` gives the value x or which the probability for all values -lower than x is equal to prob. It is the inverse of `cdf`. +- `x` must be a scalar +- `dist` must be a distribution - +### Cumulative density function -### CDF of a distribution - -The `cdf(distribution,x)` gives the cumulative probability of the distribution +The `cdf(dist, x)` gives the cumulative probability of the distribution or all values lower than x. It is the inverse of `inv`. -### Mean of a distribution +#### Validity + +- `x` must be a scalar +- `dist` must be a distribution + +### Inverse CDF + +The `inv(dist, prob)` gives the value x or which the probability for all values +lower than x is equal to prob. It is the inverse of `cdf`. + + + +#### Validity + +- `prob` must be a scalar (please only put it in `(0,1)`) +- `dist` must be a distribution + +### Mean The `mean(distribution)` function gives the mean (expected value) of a distribution. @@ -112,3 +304,65 @@ The `mean(distribution)` function gives the mean (expected value) of a distribut The `sample(distribution)` samples a given distribution. + +## Converting between distribution formats + +Recall the [three formats of distributions](https://develop--squiggle-documentation.netlify.app/docs/Discussions/Three-Types-Of-Distributions). We can force any distribution into `SampleSet` format + + + +Or `PointSet` format + + + +## Normalization + +Some distribution operations (like horizontal shift) return an unnormalized distriibution. + +We provide a `normalize` function + + + +#### Validity - Input to `normalize` must be a dist + +We provide a predicate `isNormalized`, for when we have simple control flow + + + +#### Validity + +- Input to `isNormalized` must be a dist + +## Convert any distribution to a sample set distribution + +`toSampleSet` has two signatures + +It is unary when you use an internal hardcoded number of samples + + + +And binary when you provide a number of samples (floored) + + + +## `inspect` + +You may like to debug by right clicking your browser and using the _inspect_ functionality on the webpage, and viewing the _console_ tab. Then, wrap your squiggle output with `inspect` to log an internal representation. + + + +Save for a logging side effect, `inspect` does nothing to input and returns it. + +## Truncate + +You can cut off from the left + + + +You can cut off from the right + + + +You can cut off from both sides + + diff --git a/packages/website/docs/Features/Language.mdx b/packages/website/docs/Features/Language.mdx index e559fb60..5b66d2e2 100644 --- a/packages/website/docs/Features/Language.mdx +++ b/packages/website/docs/Features/Language.mdx @@ -1,39 +1,53 @@ --- sidebar_position: 2 +title: Language Basics --- import { SquiggleEditor } from "../../src/components/SquiggleEditor"; -# Squiggle Language +## Expressions -The squiggle language has a very simple syntax. The best way to get to understand -it is by simply looking at examples. +A distribution -## Basic Language + -As an example: +A number + + + +Arrays + + + +Records + + + +## Statements + +A statement assigns expressions to names. It looks like ` = ` -Squiggle can declare variables (`value_of_work = 10 to 70`) and declare exports -(the lone `value_of_work` line). Variables can be used later in a squiggle program -and even in other notebooks! +### Functions -An export is rendered to the output view so you can see your result. - -the exports can be expressions, such as: - - - -## Functions - -Squiggle supports functions, including the rendering of functions: +We can define functions + +## See more + +- [Functions reference](https://squiggle-language.com/docs/Features/Functions) +- [Gallery](https://squiggle-language.com/docs/Discussions/Gallery) diff --git a/packages/website/docs/Features/Javascript-Libraries.md b/packages/website/docs/Features/Node-Packages.md similarity index 82% rename from packages/website/docs/Features/Javascript-Libraries.md rename to packages/website/docs/Features/Node-Packages.md index c782be6f..ab590c32 100644 --- a/packages/website/docs/Features/Javascript-Libraries.md +++ b/packages/website/docs/Features/Node-Packages.md @@ -1,13 +1,12 @@ --- sidebar_position: 3 +title: Node Packages --- -# Javascript Libraries - There are two JavaScript packages currently available for Squiggle: -- [`@quri/squiggle-lang`](https://www.npmjs.com/package/@quri/squiggle-lang) -- [`@quri/squiggle-components`](https://www.npmjs.com/package/@quri/squiggle-components) +- [`@quri/squiggle-lang`](https://www.npmjs.com/package/@quri/squiggle-lang) ![npm version](https://badge.fury.io/js/@quri%2Fsquiggle-lang.svg) +- [`@quri/squiggle-components`](https://www.npmjs.com/package/@quri/squiggle-components) ![npm version](https://badge.fury.io/js/@quri%2Fsquiggle-components.svg) Types are available for both packages. @@ -23,8 +22,8 @@ argument allows you to pass an environment previously created by another `run` call. Passing this environment will mean that all previously declared variables in the previous environment will be made available. -The return type of `run` is a bit complicated, and comes from auto generated js -code that comes from rescript. I highly recommend using typescript when using +The return type of `run` is a bit complicated, and comes from auto generated `js` +code that comes from rescript. We highly recommend using typescript when using this library to help navigate the return type. ## Squiggle Components diff --git a/packages/website/docs/Internal/Grammar.md b/packages/website/docs/Internal/Grammar.md new file mode 100644 index 00000000..97fd546b --- /dev/null +++ b/packages/website/docs/Internal/Grammar.md @@ -0,0 +1,56 @@ +--- +title: Grammar +author: + - Quinn Dougherty +--- + +Formal grammar specification, reference material for parser implementation. + +_In all likelihood the reference will have to be debugged as we see what tests pass and don't pass during implementation_. + +## Lexical descriptions of constants and identifiers + +``` + ::= [-]? [0-9]+ (.[0-9]+)? | [-]? [0-9]+ (.[0-9]+)? [e] [-]? [0-9]+ + ::= [a-zA-Z]+ [a-zA-Z0-9]? + ::= true | false +``` + +## Expressions + +The following gives no typing information. You can obey the grammar and still write nonsensical code. + +Think of javascript's list unpacking notation to read our variable-argument function `mixture`. + +``` + ::= + | - | .+ | .- | + ::= * | / | .* | | + ::= ^ | .^ | + ::= | | | ( ) | | | . | => | (, ) => | ... | () | (, ) | ... +``` + +## Data structures + +``` + ::= [] | [] | [, ] | ... + ::= {} | {: } | {: , : } | ... +``` + +## Statements + +``` + ::= | + ::= = + ::= () = | (, ) = | ... +``` + +## A squiggle file + +To be valid and raise no errors as of current (apr22) interpreter, + +``` + ::= ; | \n + ::= | | | ... +``` + +This isn't strictly speaking true; the interpreter allows expressions outside of the final line. diff --git a/packages/website/docs/Internal/Invariants.md b/packages/website/docs/Internal/Invariants.md index c1c0fd79..91adf1a1 100644 --- a/packages/website/docs/Internal/Invariants.md +++ b/packages/website/docs/Internal/Invariants.md @@ -1,5 +1,5 @@ --- -title: Statistical properties of algebraic combinations of distributions for property testing. +title: Invariants of Probability Distributions urlcolor: blue author: - Nuño Sempere @@ -7,13 +7,17 @@ author: abstract: This document outlines some properties about algebraic combinations of distributions. It is meant to facilitate property tests for [Squiggle](https://squiggle-language.com/), an estimation language for forecasters. So far, we are focusing on the means, the standard deviation and the shape of the pdfs. --- +Invariants to check with property tests. + _This document right now is normative and aspirational, not a description of the testing that's currently done_. +## Algebraic combinations + The academic keyword to search for in relation to this document is "[algebra of random variables](https://wikiless.org/wiki/Algebra_of_random_variables?lang=en)". Squiggle doesn't yet support getting the standard deviation, denoted by $\sigma$, but such support could yet be added. -## Means and standard deviations +### Means and standard deviations -### Sums +#### Sums $$ mean(f+g) = mean(f) + mean(g) @@ -29,7 +33,7 @@ $$ mean(normal(a,b) + normal(c,d)) = mean(normal(a+c, \sqrt{b^2 + d^2})) $$ -### Subtractions +#### Subtractions $$ mean(f-g) = mean(f) - mean(g) @@ -39,7 +43,7 @@ $$ \sigma(f-g) = \sqrt{\sigma(f)^2 + \sigma(g)^2} $$ -### Multiplications +#### Multiplications $$ mean(f \cdot g) = mean(f) \cdot mean(g) @@ -49,15 +53,15 @@ $$ \sigma(f \cdot g) = \sqrt{ (\sigma(f)^2 + mean(f)) \cdot (\sigma(g)^2 + mean(g)) - (mean(f) \cdot mean(g))^2} $$ -### Divisions +#### Divisions Divisions are tricky, and in general we don't have good expressions to characterize properties of ratios. In particular, the ratio of two normals is a Cauchy distribution, which doesn't have to have a mean. -## Probability density functions (pdfs) +### Probability density functions (pdfs) Specifying the pdf of the sum/multiplication/... of distributions as a function of the pdfs of the individual arguments can still be done. But it requires integration. My sense is that this is still doable, and I (Nuño) provide some _pseudocode_ to do this. -### Sums +#### Sums Let $f, g$ be two independently distributed functions. Then, the pdf of their sum, evaluated at a point $z$, expressed as $(f + g)(z)$, is given by: @@ -110,15 +114,31 @@ let pdfOfSum = (pdf1, pdf2, cdf1, cdf2, z) => { }; ``` -## Cumulative density functions +### Cumulative density functions TODO -## Inverse cumulative density functions +### Inverse cumulative density functions TODO -# To do: +## `pdf`, `cdf`, and `inv` + +With $\forall dist, pdf := x \mapsto \texttt{pdf}(dist, x) \land cdf := x \mapsto \texttt{cdf}(dist, x) \land inv := p \mapsto \texttt{inv}(dist, p)$, + +### `cdf` and `inv` are inverses + +$$ +\forall x \in (0,1), cdf(inv(x)) = x \land \forall x \in \texttt{dom}(cdf), x = inv(cdf(x)) +$$ + +### The codomain of `cdf` equals the open interval `(0,1)` equals the codomain of `pdf` + +$$ +\texttt{cod}(cdf) = (0,1) = \texttt{cod}(pdf) +$$ + +## To do: - Provide sources or derivations, useful as this document becomes more complicated - Provide definitions for the probability density function, exponential, inverse, log, etc. diff --git a/packages/website/docs/Internal/ProcessingConfidenceIntervals.md b/packages/website/docs/Internal/Processing-Confidence-Intervals.md similarity index 78% rename from packages/website/docs/Internal/ProcessingConfidenceIntervals.md rename to packages/website/docs/Internal/Processing-Confidence-Intervals.md index 99e72e5a..01afeb79 100644 --- a/packages/website/docs/Internal/ProcessingConfidenceIntervals.md +++ b/packages/website/docs/Internal/Processing-Confidence-Intervals.md @@ -1,6 +1,9 @@ -# Processing confidence intervals +--- +title: Processing Confidence Intervals +author: Nuño Sempere +--- -This page explains what we are doing when we take a 95% confidence interval, and we get a mean and a standard deviation from it +This page explains what we are doing when we take a 90% confidence interval, and we get a mean and a standard deviation from it. ## For normals @@ -19,10 +22,7 @@ module Normal = { We know that for a normal with mean $\mu$ and standard deviation $\sigma$, $$ - -a \cdot Normal(\mu, \sigma) = Normal(a\cdot \mu, |a|\cdot \sigma) - - +a \cdot Normal(\mu, \sigma) = Normal(a \cdot \mu, |a| \cdot \sigma) $$ We can now look at the inverse cdf of a $Normal(0,1)$. We find that the 95% point is reached at $1.6448536269514722$. ([source](https://stackoverflow.com/questions/20626994/how-to-calculate-the-inverse-of-the-normal-cumulative-distribution-function-in-p)) This means that the 90% confidence interval is $[-1.6448536269514722, 1.6448536269514722]$, which has a width of $2 \cdot 1.6448536269514722$. @@ -30,3 +30,5 @@ We can now look at the inverse cdf of a $Normal(0,1)$. We find that the 95% poin So then, if we take a $Normal(0,1)$ and we multiply it by $\frac{(high -. low)}{(2. *. 1.6448536269514722)}$, it's 90% confidence interval will be multiplied by the same amount. Then we just have to shift it by the mean to get our target normal. ## For lognormals + +TODO diff --git a/packages/website/docs/Introduction.md b/packages/website/docs/Introduction.md index ecfe7f02..91cfd919 100644 --- a/packages/website/docs/Introduction.md +++ b/packages/website/docs/Introduction.md @@ -1,10 +1,16 @@ --- sidebar_position: 1 +title: Introduction --- -# Squiggle +Squiggle is an _estimation language_, and a syntax for _calculating and expressing beliefs_ involving uncertainty. It has use cases in forecasting and writing evaluations. -Squiggle is a language for writing calculations under uncertainty. It has use -cases in forecasting and writing better evaluations. +## Get started -The best way to get started with Squiggle is to [try it out yourself](https://playground.squiggle-language.com/). +- [Gallery](https://www.squiggle-language.com/docs/Discussions/Gallery) +- [Squiggle playground](https://squiggle-language.com/playground) +- [Language basics](https://www.squiggle-language.com/docs/Features/Language) +- [Squiggle functions source of truth](https://www.squiggle-language.com/docs/Features/Functions) +- [Known bugs](https://www.squiggle-language.com/docs/Discussions/Bugs) +- [Original lesswrong sequence](https://www.lesswrong.com/s/rDe8QE5NvXcZYzgZ3) +- [Author your squiggle models as Observable notebooks](https://observablehq.com/@hazelfire/squiggle) diff --git a/packages/website/docusaurus.config.js b/packages/website/docusaurus.config.js index c5d03327..4971b1b0 100644 --- a/packages/website/docusaurus.config.js +++ b/packages/website/docusaurus.config.js @@ -49,7 +49,7 @@ const config = { sidebarPath: require.resolve("./sidebars.js"), // Please change this to your repo. editUrl: - "https://github.com/quantified-uncertainty/squiggle/tree/master/packages/website/", + "https://github.com/quantified-uncertainty/squiggle/tree/develop/packages/website/", remarkPlugins: [math], rehypePlugins: [katex], }, @@ -57,7 +57,7 @@ const config = { showReadingTime: true, // Please change this to your repo. editUrl: - "https://github.com/quantified-uncertainty/squiggle/tree/master/packages/website/", + "https://github.com/quantified-uncertainty/squiggle/tree/develop/packages/website/", }, theme: { customCss: require.resolve("./src/css/custom.css"), @@ -73,7 +73,7 @@ const config = { title: "Squiggle", logo: { alt: "Squiggle Logo", - src: "img/logo.svg", + src: "img/quri-logo.png", }, items: [ { @@ -85,7 +85,7 @@ const config = { { to: "/blog", label: "Blog", position: "left" }, { to: "/playground", label: "Playground", position: "left" }, { - href: "https://github.com/QURIresearch/squiggle", + href: "https://github.com/quantified-uncertainty/squiggle", label: "GitHub", position: "right", }, @@ -103,7 +103,7 @@ const config = { }, { label: "GitHub", - href: "https://github.com/QURIresearch/squiggle", + href: "https://github.com/quantified-uncertainty/squiggle", }, ], }, diff --git a/packages/website/package.json b/packages/website/package.json index 986dfb22..c830fc27 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -2,6 +2,7 @@ "name": "squiggle-website", "version": "0.0.0", "private": true, + "license": "MIT", "scripts": { "start": "docusaurus start", "build": "docusaurus build", @@ -13,11 +14,11 @@ "dependencies": { "@docusaurus/core": "2.0.0-beta.18", "@docusaurus/preset-classic": "2.0.0-beta.18", - "@quri/squiggle-components": "0.1.8", + "@quri/squiggle-components": "0.2.9", "clsx": "^1.1.1", "prism-react-renderer": "^1.2.1", - "react": "^18.0.0", - "react-dom": "^18.0.0", + "react": "^18.1.0", + "react-dom": "^18.1.0", "remark-math": "^3", "rehype-katex": "^5", "hast-util-is-element": "2.1.2" diff --git a/packages/website/src/pages/index.js b/packages/website/src/pages/index.js index 452c7ef0..4cbddcac 100644 --- a/packages/website/src/pages/index.js +++ b/packages/website/src/pages/index.js @@ -22,10 +22,7 @@ function HomepageHeader() { export default function Home() { const { siteConfig } = useDocusaurusContext(); return ( - +
diff --git a/packages/website/src/pages/playground.js b/packages/website/src/pages/playground.js index 4f36e4c3..52bac323 100644 --- a/packages/website/src/pages/playground.js +++ b/packages/website/src/pages/playground.js @@ -10,7 +10,7 @@ export default function PlaygroundPage() { maxWidth: 2000, }} > - + ); diff --git a/packages/website/static/img/docusaurus.png b/packages/website/static/img/docusaurus.png deleted file mode 100644 index f458149e..00000000 Binary files a/packages/website/static/img/docusaurus.png and /dev/null differ diff --git a/packages/website/static/img/favicon.ico b/packages/website/static/img/favicon.ico index c01d54bc..d88e28c4 100644 Binary files a/packages/website/static/img/favicon.ico and b/packages/website/static/img/favicon.ico differ diff --git a/packages/website/static/img/logo.svg b/packages/website/static/img/logo.svg deleted file mode 100644 index 9db6d0d0..00000000 --- a/packages/website/static/img/logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/website/static/img/quri-logo-with-QURI-written-underneath.png b/packages/website/static/img/quri-logo-with-QURI-written-underneath.png new file mode 100644 index 00000000..14932c80 Binary files /dev/null and b/packages/website/static/img/quri-logo-with-QURI-written-underneath.png differ diff --git a/packages/website/static/img/quri-logo.png b/packages/website/static/img/quri-logo.png new file mode 100644 index 00000000..d88e28c4 Binary files /dev/null and b/packages/website/static/img/quri-logo.png differ diff --git a/packages/website/static/img/undraw_docusaurus_mountain.svg b/packages/website/static/img/undraw_docusaurus_mountain.svg deleted file mode 100644 index 431cef2f..00000000 --- a/packages/website/static/img/undraw_docusaurus_mountain.svg +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/website/static/img/undraw_docusaurus_react.svg b/packages/website/static/img/undraw_docusaurus_react.svg deleted file mode 100644 index e4170504..00000000 --- a/packages/website/static/img/undraw_docusaurus_react.svg +++ /dev/null @@ -1,169 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/website/static/img/undraw_docusaurus_tree.svg b/packages/website/static/img/undraw_docusaurus_tree.svg deleted file mode 100644 index a05cc03d..00000000 --- a/packages/website/static/img/undraw_docusaurus_tree.svg +++ /dev/null @@ -1 +0,0 @@ -docu_tree \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index e72d8035..283b1121 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1271,7 +1271,7 @@ core-js-pure "^3.20.2" regenerator-runtime "^0.13.4" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.8", "@babel/runtime@^7.16.3", "@babel/runtime@^7.17.8", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.8", "@babel/runtime@^7.16.3", "@babel/runtime@^7.17.8", "@babel/runtime@^7.17.9", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": version "7.17.9" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.9.tgz#d19fbf802d01a8cb6cf053a64e472d42c434ba72" integrity sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg== @@ -1334,6 +1334,18 @@ resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== +"@cspotcode/source-map-consumer@0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b" + integrity sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg== + +"@cspotcode/source-map-support@0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz#4789840aa859e46d2f3173727ab707c66bf344f5" + integrity sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA== + dependencies: + "@cspotcode/source-map-consumer" "0.8.0" + "@csstools/normalize.css@*": version "12.0.0" resolved "https://registry.yarnpkg.com/@csstools/normalize.css/-/normalize.css-12.0.0.tgz#a9583a75c3f150667771f30b60d9f059473e62c4" @@ -1928,11 +1940,6 @@ resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== -"@glennsl/bs-json@^5.0.2": - version "5.0.4" - resolved "https://registry.yarnpkg.com/@glennsl/bs-json/-/bs-json-5.0.4.tgz#8a80906f3b5e04d78dc06722e5987ff6499c89a8" - integrity sha512-Th9DetZjRlMZrb74kgGJ44oWcoFyOTE884WlSuXft0Cd+J09vHRxiB7eVyK7Gthb4cSevsBBJDHYAbGGL25wPw== - "@glennsl/rescript-jest@^0.9.0": version "0.9.1" resolved "https://registry.yarnpkg.com/@glennsl/rescript-jest/-/rescript-jest-0.9.1.tgz#a85a6f0e4c3b79010b5a917c3652aa70d374e4d1" @@ -1978,6 +1985,13 @@ js-yaml "^3.13.1" resolve-from "^5.0.0" +"@istanbuljs/nyc-config-typescript@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@istanbuljs/nyc-config-typescript/-/nyc-config-typescript-1.0.2.tgz#1f5235b28540a07219ae0dd42014912a0b19cf89" + integrity sha512-iKGIyMoyJuFnJRSVTZ78POIRvNnwZaWIf8vG4ZS3rQq58MMDrqEX2nnzx0R28V2X8JvmKYiqY9FP2hlJsm8A0w== + dependencies: + "@istanbuljs/schema" "^0.1.2" + "@istanbuljs/schema@^0.1.2": version "0.1.3" resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" @@ -3668,19 +3682,19 @@ lodash "^4.17.15" redent "^3.0.0" -"@testing-library/react@^13.0.1": - version "13.0.1" - resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-13.0.1.tgz#00d223e182923d341a9610590561fb9dd1324110" - integrity sha512-zeHx3PohYYp+4bTJwrixQY8zSBZjWUGwYc7OhD1EpWTHS92RleApLoP72NdwaWxOrM1P1Uezt3XvGf6t2XSWPQ== +"@testing-library/react@^13.1.1": + version "13.1.1" + resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-13.1.1.tgz#6c1635e25acca8ca5be8ee3b19ad1391681c5846" + integrity sha512-8mirlAa0OKaUvnqnZF6MdAh2tReYA2KtWVw1PKvaF5EcCZqgK5pl8iF+3uW90JdG5Ua2c2c2E2wtLdaug3dsVg== dependencies: "@babel/runtime" "^7.12.5" "@testing-library/dom" "^8.5.0" "@types/react-dom" "^18.0.0" -"@testing-library/user-event@^14.0.4": - version "14.1.0" - resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.1.0.tgz#db479c06271b72a4d41cf595ec2ad7ff078c1d72" - integrity sha512-+CGfMXlVM+OwREHDEsfTGsXIMI+rjr3a7YBUSutq7soELht+8kQrM5k46xa/WLfHdtX/wqsDIleL6bi4i+xz0w== +"@testing-library/user-event@^14.1.1": + version "14.1.1" + resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.1.1.tgz#e1ff6118896e4b22af31e5ea2f9da956adde23d8" + integrity sha512-XrjH/iEUqNl9lF2HX9YhPNV7Amntkcnpw0Bo1KkRzowNDcgSN9i0nm4Q8Oi5wupgdfPaJNMAWa61A+voD6Kmwg== "@tootallnate/once@1": version "1.1.2" @@ -3692,6 +3706,26 @@ resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== +"@tsconfig/node10@^1.0.7": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9" + integrity sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg== + +"@tsconfig/node12@^1.0.7": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.9.tgz#62c1f6dee2ebd9aead80dc3afa56810e58e1a04c" + integrity sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw== + +"@tsconfig/node14@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.1.tgz#95f2d167ffb9b8d2068b0b235302fafd4df711f2" + integrity sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg== + +"@tsconfig/node16@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e" + integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA== + "@types/aria-query@^4.2.0": version "4.2.2" resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.2.tgz#ed4e0ad92306a704f9fb132a0cfcf77486dbe2bc" @@ -3934,10 +3968,10 @@ resolved "https://registry.yarnpkg.com/@types/katex/-/katex-0.11.1.tgz#34de04477dcf79e2ef6c8d23b41a3d81f9ebeaf5" integrity sha512-DUlIj2nk0YnJdlWgsFuVKcX27MLW0KbKmGVoUHmFr+74FYYNUDAaj9ZqTADvsbE8rfxuVmSFc7KczYn5Y09ozg== -"@types/lodash@^4.14.181": - version "4.14.181" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.181.tgz#d1d3740c379fda17ab175165ba04e2d03389385d" - integrity sha512-n3tyKthHJbkiWhDZs3DkhkCzt2MexYHXlX0td5iMplyfwketaOeKboEVBqzceH7juqvEg3q5oUoBFxSLu7zFag== +"@types/lodash@^4.14.182": + version "4.14.182" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.182.tgz#05301a4d5e62963227eaafe0ce04dd77c54ea5c2" + integrity sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q== "@types/mdast@^3.0.0": version "3.0.10" @@ -3964,10 +3998,10 @@ "@types/node" "*" form-data "^3.0.0" -"@types/node@*", "@types/node@^17.0.24", "@types/node@^17.0.5": - version "17.0.24" - resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.24.tgz#20ba1bf69c1b4ab405c7a01e950c4f446b05029f" - integrity sha512-aveCYRQbgTH9Pssp1voEP7HiuWlD2jW2BO56w+bVrJn04i61yh6mRfoKO6hEYQD9vF+W8Chkwc6j1M36uPkx4g== +"@types/node@*", "@types/node@^17.0.29", "@types/node@^17.0.5": + version "17.0.29" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.29.tgz#7f2e1159231d4a077bb660edab0fde373e375a3d" + integrity sha512-tx5jMmMFwx7wBwq/V7OohKDVb/JwJU5qCVkeLMh1//xycAJ/ESuw9aJ9SEtlCZDYi2pBfe4JkisSoAtbOsBNAA== "@types/node@^14.0.10": version "14.18.13" @@ -4034,10 +4068,17 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== -"@types/react-dom@^18.0.0", "@types/react-dom@^18.0.1": - version "18.0.1" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.1.tgz#cb3cc10ea91141b12c71001fede1017acfbce4db" - integrity sha512-jCwTXvHtRLiyVvKm9aEdHXs8rflVOGd5Sl913JZrPshfXjn8NYsTNZOz70bCsA31IR0TOqwi3ad+X4tSCBoMTw== +"@types/react-dom@^18.0.0": + version "18.0.0" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.0.tgz#b13f8d098e4b0c45df4f1ed123833143b0c71141" + integrity sha512-49897Y0UiCGmxZqpC8Blrf6meL8QUla6eb+BBhn69dTXlmuOlzkfr7HHY/O8J25e1lTUMs+YYxSlVDAaGHCOLg== + dependencies: + "@types/react" "*" + +"@types/react-dom@^18.0.2": + version "18.0.2" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.2.tgz#2d6b46557aa30257e87e67a6d952146d15979d79" + integrity sha512-UxeS+Wtj5bvLRREz9tIgsK4ntCuLDo0EcAcACgw3E+9wE8ePDr9uQpq53MfcyxyIS55xJ+0B6mDS8c4qkkHLBg== dependencies: "@types/react" "*" @@ -4075,9 +4116,9 @@ "@types/react" "*" "@types/react@*", "@types/react@^16.9.19", "@types/react@^18.0.1", "@types/react@^18.0.3": - version "18.0.5" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.5.tgz#1a4d4b705ae6af5aed369dec22800b20f89f5301" - integrity sha512-UPxNGInDCIKlfqBrm8LDXYWNfLHwIdisWcsH5GpMyGjhEDLFgTtlRBaoWuCua9HcyuE0rMkmAeZ3FXV1pYLIYQ== + version "18.0.8" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.8.tgz#a051eb380a9fbcaa404550543c58e1cf5ce4ab87" + integrity sha512-+j2hk9BzCOrrOSJASi5XiOyBbERk9jG5O73Ya4M0env5Ixi6vUNli4qy994AINcEF+1IEHISYFfIT4zwr++LKw== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -4668,7 +4709,7 @@ acorn-walk@^7.0.0, acorn-walk@^7.1.1, acorn-walk@^7.2.0: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== -acorn-walk@^8.0.0: +acorn-walk@^8.0.0, acorn-walk@^8.1.1: version "8.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== @@ -4885,10 +4926,10 @@ ansi-to-html@^0.6.11: dependencies: entities "^2.0.0" -antd@^4.19.3: - version "4.19.5" - resolved "https://registry.yarnpkg.com/antd/-/antd-4.19.5.tgz#38d08f3e1391a7a69c2ca76f50968bb12ec2ac93" - integrity sha512-C4H/VJqlVO5iMvHZyiV27R8SbPs4jsOKCGPhDXIHUry/RnUCbMmVeQaPRfUIxSI1NbqDflsuQfevPtz1svyIlg== +antd@^4.20.1: + version "4.20.1" + resolved "https://registry.yarnpkg.com/antd/-/antd-4.20.1.tgz#6cd5a406c7172d61a5d0693ea52ee908650cf674" + integrity sha512-asKxOV0a6AijqonbcXkO08/q+XvqS/HmGfaRIS6ZH1ALR3FS2q+kTW52rJZO9rfoOb/ldPhEBVSWiNrbiB+uCQ== dependencies: "@ant-design/colors" "^6.0.0" "@ant-design/icons" "^4.7.0" @@ -4899,39 +4940,40 @@ antd@^4.19.3: copy-to-clipboard "^3.2.0" lodash "^4.17.21" memoize-one "^6.0.0" - moment "^2.25.3" - rc-cascader "~3.2.1" + moment "^2.29.2" + rc-cascader "~3.5.0" rc-checkbox "~2.3.0" rc-collapse "~3.1.0" - rc-dialog "~8.6.0" + rc-dialog "~8.8.1" rc-drawer "~4.4.2" - rc-dropdown "~3.3.2" - rc-field-form "~1.25.0" - rc-image "~5.2.5" + rc-dropdown "~3.5.0" + rc-field-form "~1.26.1" + rc-image "~5.6.0" rc-input "~0.0.1-alpha.5" rc-input-number "~7.3.0" - rc-mentions "~1.6.1" - rc-menu "~9.3.2" - rc-motion "^2.4.4" - rc-notification "~4.5.7" + rc-mentions "~1.7.0" + rc-menu "~9.5.5" + rc-motion "^2.5.1" + rc-notification "~4.6.0" rc-pagination "~3.1.9" rc-picker "~2.6.4" rc-progress "~3.2.1" rc-rate "~2.9.0" rc-resize-observer "^1.2.0" - rc-select "~14.0.2" - rc-slider "~10.0.0-alpha.4" + rc-segmented "~2.0.0" + rc-select "~14.1.1" + rc-slider "~10.0.0" rc-steps "~4.1.0" rc-switch "~3.2.0" - rc-table "~7.23.0" - rc-tabs "~11.10.0" + rc-table "~7.24.0" + rc-tabs "~11.13.0" rc-textarea "~0.3.0" rc-tooltip "~5.1.1" - rc-tree "~5.4.3" - rc-tree-select "~5.1.1" + rc-tree "~5.5.0" + rc-tree-select "~5.3.0" rc-trigger "^5.2.10" rc-upload "~4.3.0" - rc-util "^5.19.3" + rc-util "^5.20.0" scroll-into-view-if-needed "^2.2.25" anymatch@^2.0.0: @@ -4955,6 +4997,13 @@ app-root-dir@^1.0.2: resolved "https://registry.yarnpkg.com/app-root-dir/-/app-root-dir-1.0.2.tgz#38187ec2dea7577fff033ffcb12172692ff6e118" integrity sha1-OBh+wt6nV3//Az/8sSFyaS/24Rg= +append-transform@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-2.0.0.tgz#99d9d29c7b38391e6f428d28ce136551f0b77e12" + integrity sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg== + dependencies: + default-require-extensions "^3.0.0" + "aproba@^1.0.3 || ^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" @@ -4965,6 +5014,11 @@ aproba@^1.1.1: resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== +archy@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" + integrity sha1-+cjBN1fMHde8N5rHeyxipcKGjEA= + are-we-there-yet@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c" @@ -4973,6 +5027,11 @@ are-we-there-yet@^2.0.0: delegates "^1.0.0" readable-stream "^3.6.0" +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + arg@^5.0.0, arg@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.1.tgz#eb0c9a8f77786cad2af8ff2b862899842d7b6adb" @@ -4990,6 +5049,11 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== +argv@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/argv/-/argv-0.0.2.tgz#ecbd16f8949b157183711b1bda334f37840185ab" + integrity sha1-7L0W+JSbFXGDcRsb2jNPN4QBhas= + aria-query@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-4.2.2.tgz#0d2ca6c9aceb56b8977e9fed6aed7e15bbd2f83b" @@ -5162,11 +5226,6 @@ async-validator@^4.0.2: resolved "https://registry.yarnpkg.com/async-validator/-/async-validator-4.0.7.tgz#034a0fd2103a6b2ebf010da75183bec299247afe" integrity sha512-Pj2IR7u8hmUEDOwB++su6baaRi+QvsgajuFB9j95foM1N2gy5HM4z60hfusIO0fBPG5uLAEl6yCJr1jNSVugEQ== -async@0.9.x: - version "0.9.2" - resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" - integrity sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0= - async@^2.6.2: version "2.6.4" resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" @@ -5174,6 +5233,11 @@ async@^2.6.2: dependencies: lodash "^4.17.14" +async@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.3.tgz#ac53dafd3f4720ee9e8a160628f18ea91df196c9" + integrity sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g== + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -5711,6 +5775,13 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + braces@^2.3.1, braces@^2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" @@ -5960,6 +6031,16 @@ cacheable-request@^6.0.0: normalize-url "^4.1.0" responselike "^1.0.2" +caching-transform@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/caching-transform/-/caching-transform-4.0.0.tgz#00d297a4206d71e2163c39eaffa8157ac0651f0f" + integrity sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA== + dependencies: + hasha "^5.0.0" + make-dir "^3.0.0" + package-hash "^4.0.0" + write-file-atomic "^3.0.0" + call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" @@ -5991,7 +6072,7 @@ camelcase-css@2.0.1, camelcase-css@^2.0.1: resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5" integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== -camelcase@^5.3.1: +camelcase@^5.0.0, camelcase@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== @@ -6074,6 +6155,11 @@ chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.2: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.0.1.tgz#ca57d71e82bb534a296df63bbacc4a1c22b2a4b6" + integrity sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w== + char-regex@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" @@ -6280,6 +6366,15 @@ cli-table3@^0.6.1: optionalDependencies: "@colors/colors" "1.5.0" +cliui@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" + integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^6.2.0" + cliui@^7.0.2: version "7.0.4" resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" @@ -6329,10 +6424,16 @@ coa@^2.0.2: chalk "^2.4.1" q "^1.1.2" -codejar@^3.2.3: - version "3.6.0" - resolved "https://registry.yarnpkg.com/codejar/-/codejar-3.6.0.tgz#be491d4db4d723da24f1bcd735ecad09e0f6c36d" - integrity sha512-30iPkdz4Y3d2qVMpMKsvEREtfUBH6JHvW2aWeoCBR67DUoZqSQLIvcAlLWZuTG7i7DonJkbCqkBnJPPhbj+J6w== +codecov@3.8.3: + version "3.8.3" + resolved "https://registry.yarnpkg.com/codecov/-/codecov-3.8.3.tgz#9c3e364b8a700c597346ae98418d09880a3fdbe7" + integrity sha512-Y8Hw+V3HgR7V71xWH2vQ9lyS358CbGCldWlJFR0JirqoGtOoas3R3/OclRTvgUYFK29mmJICDPauVKmpqbwhOA== + dependencies: + argv "0.0.2" + ignore-walk "3.0.4" + js-yaml "3.14.1" + teeny-request "7.1.1" + urlgrey "1.0.0" collapse-white-space@^1.0.2: version "1.0.6" @@ -6458,7 +6559,7 @@ commondir@^1.0.1: resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= -complex.js@^2.1.0: +complex.js@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/complex.js/-/complex.js-2.1.1.tgz#0675dac8e464ec431fb2ab7d30f41d889fb25c31" integrity sha512-8njCHOTtFFLtegk6zQo0kkVX1rngygb/KQI6z1qZxlFI3scluC+LVTCFbrkWjBv4vvLlbQ9t88IPMC6k95VTTg== @@ -6731,6 +6832,11 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: safe-buffer "^5.0.1" sha.js "^2.4.8" +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + cross-env@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf" @@ -7251,6 +7357,11 @@ debug@^3.0.0, debug@^3.1.1, debug@^3.2.7: dependencies: ms "^2.1.1" +decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + decimal.js@^10.2.1, decimal.js@^10.3.1: version "10.3.1" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" @@ -7300,6 +7411,13 @@ default-gateway@^6.0.3: dependencies: execa "^5.0.0" +default-require-extensions@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-3.0.0.tgz#e03f93aac9b2b6443fc52e5e4a37b3ad9ad8df96" + integrity sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg== + dependencies: + strip-bom "^4.0.0" + defer-to-connect@^1.0.1: version "1.1.3" resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" @@ -7460,6 +7578,11 @@ diff-sequences@^27.5.1: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + diffie-hellman@^5.0.0: version "5.0.3" resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" @@ -7500,20 +7623,6 @@ dns-packet@^5.2.2: dependencies: "@leichtgewicht/ip-codec" "^2.0.1" -docsify@^4.12.2: - version "4.12.2" - resolved "https://registry.yarnpkg.com/docsify/-/docsify-4.12.2.tgz#749115d2ff7d358780ea865e01f4a0162423d67f" - integrity sha512-hpRez5upcvkYigT2zD8P5kH5t9HpSWL8yn/ZU/g04/WfAfxVNW6CPUVOOF1EsQUDxTRuyNTFOb6uUv+tPij3tg== - dependencies: - dompurify "^2.3.1" - marked "^1.2.9" - medium-zoom "^1.0.6" - opencollective-postinstall "^2.0.2" - prismjs "^1.23.0" - strip-indent "^3.0.0" - tinydate "^1.3.0" - tweezer.js "^1.4.0" - doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" @@ -7611,11 +7720,6 @@ domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: dependencies: domelementtype "^2.2.0" -dompurify@^2.3.1: - version "2.3.6" - resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.6.tgz#2e019d7d7617aacac07cbbe3d88ae3ad354cf875" - integrity sha512-OFP2u/3T1R5CEgWCEONuJ1a5+MFKnOYpkywpUSxv/dj1LeBT1erK+JwM7zK0ROy2BRhqVCf0LRw/kHqKuMkVGg== - domutils@1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" @@ -7713,11 +7817,11 @@ ee-first@1.1.1: integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= ejs@^3.1.6: - version "3.1.6" - resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.6.tgz#5bfd0a0689743bb5268b3550cceeebbc1702822a" - integrity sha512-9lt9Zse4hPucPkoP7FHDF0LQAlGyF9JVpnClFLFH3aSSbxmyoqINRpp/9wePWJTUl4KOQwRL72Iw3InHPDkoGw== + version "3.1.7" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.7.tgz#c544d9c7f715783dd92f0bddcf73a59e6962d006" + integrity sha512-BIar7R6abbUxDA3bfXrO4DSgwo8I+fB5/1zgujl3HLLjwd6+9iOnrT+t3grn2qbk9vOgBubXOFwX2m9axoFaGw== dependencies: - jake "^10.6.1" + jake "^10.8.5" electron-to-chromium@^1.4.84: version "1.4.107" @@ -7928,6 +8032,11 @@ es5-shim@^4.5.13: resolved "https://registry.yarnpkg.com/es5-shim/-/es5-shim-4.6.5.tgz#2124bb073b7cede2ed23b122a1fd87bb7b0bb724" integrity sha512-vfQ4UAai8szn0sAubCy97xnZ4sJVDD1gt/Grn736hg8D7540wemIb1YPrYZSTqlM2H69EQX1or4HU/tSwRTI3w== +es6-error@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" + integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== + es6-shim@^0.35.5: version "0.35.6" resolved "https://registry.yarnpkg.com/es6-shim/-/es6-shim-0.35.6.tgz#d10578301a83af2de58b9eadb7c2c9945f7388a0" @@ -8417,6 +8526,13 @@ extglob@^2.0.4: snapdragon "^0.8.1" to-regex "^3.0.1" +fast-check@2.25.0, fast-check@^2.17.0: + version "2.25.0" + resolved "https://registry.yarnpkg.com/fast-check/-/fast-check-2.25.0.tgz#5146601851bf3be0953bd17eb2b7d547936c6561" + integrity sha512-wRUT2KD2lAmT75WNIJIHECawoUUMHM0I5jrlLXGtGeqmPL8jl/EldUDjY1VCp6fDY8yflyfUeIOsOBrIbIiArg== + dependencies: + pure-rand "^5.0.1" + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3, fast-deep-equal@~3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -8465,7 +8581,7 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= -fast-url-parser@1.1.3: +fast-url-parser@1.1.3, fast-url-parser@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/fast-url-parser/-/fast-url-parser-1.1.3.tgz#f4af3ea9f34d8a271cf58ad2b3759f431f0b318d" integrity sha1-9K8+qfNNiicc9YrSs3WfQx8LMY0= @@ -8572,11 +8688,11 @@ file-uri-to-path@1.0.0: integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== filelist@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.2.tgz#80202f21462d4d1c2e214119b1807c1bc0380e5b" - integrity sha512-z7O0IS8Plc39rTCq6i6iHxk43duYOn8uFJiWSewIq0Bww1RNybVHSCjahmcC87ZqAm4OTvFzlzeGu3XAzG1ctQ== + version "1.0.3" + resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.3.tgz#448607750376484932f67ef1b9ff07386b036c83" + integrity sha512-LwjCsruLWQULGYKy7TX0OPtrL9kLpojOFKc5VCTxdFTV7w5zbsgqVKfnkKG7Qgjtq50gKfO56hJv88OfcGb70Q== dependencies: - minimatch "^3.0.4" + minimatch "^5.0.1" filesize@^8.0.6: version "8.0.7" @@ -8622,7 +8738,7 @@ find-cache-dir@^2.0.0, find-cache-dir@^2.1.0: make-dir "^2.0.0" pkg-dir "^3.0.0" -find-cache-dir@^3.3.1: +find-cache-dir@^3.2.0, find-cache-dir@^3.3.1: version "3.3.2" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== @@ -8789,6 +8905,11 @@ from2@^2.1.0: inherits "^2.0.1" readable-stream "^2.0.0" +fromentries@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/fromentries/-/fromentries-1.3.2.tgz#e4bca6808816bf8f93b52750f1127f5a6fd86e3a" + integrity sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg== + fs-extra@^0.30.0: version "0.30.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.30.0.tgz#f233ffcc08d4da7d432daa449776989db1df93f0" @@ -8914,7 +9035,7 @@ gentype@^4.3.0: resolved "https://registry.yarnpkg.com/gentype/-/gentype-4.3.0.tgz#ebac3abcdde2ce2a8fc85611b11568a4cb349c8d" integrity sha512-lqkc1ZS/Iog4uslRD4De47OV54Hu61vEBsirMKxRlgHIRvm8u6RqsdKxJ7JdJdrzmtKgPNvq1He69SozzW+6dQ== -get-caller-file@^2.0.5: +get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== @@ -9286,6 +9407,14 @@ hash.js@^1.0.0, hash.js@^1.0.3: inherits "^2.0.3" minimalistic-assert "^1.0.1" +hasha@^5.0.0: + version "5.2.2" + resolved "https://registry.yarnpkg.com/hasha/-/hasha-5.2.2.tgz#a48477989b3b327aea3c04f53096d816d97522a1" + integrity sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ== + dependencies: + is-stream "^2.0.0" + type-fest "^0.8.0" + hast-to-hyperscript@^9.0.0: version "9.0.1" resolved "https://registry.yarnpkg.com/hast-to-hyperscript/-/hast-to-hyperscript-9.0.1.tgz#9b67fd188e4c81e8ad66f803855334173920218d" @@ -9617,7 +9746,7 @@ http-parser-js@>=0.5.1: resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.6.tgz#2e02406ab2df8af8a7abfba62e0da01c62b95afd" integrity sha512-vDlkRPDJn93swjcjqMSaGSPABbIarsr1TLAui/gLDXzV5VsJNdXNzMYDyNBLQkjWQCJ1uizu8T2oDMhmGt0PRA== -http-proxy-agent@^4.0.1: +http-proxy-agent@^4.0.0, http-proxy-agent@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== @@ -9712,6 +9841,13 @@ iferr@^0.1.5: resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= +ignore-walk@3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.4.tgz#c9a09f69b7c7b479a5d74ac1a3c0d4236d2a6335" + integrity sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ== + dependencies: + minimatch "^3.0.4" + ignore@^4.0.3: version "4.0.6" resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" @@ -10309,11 +10445,28 @@ isobject@^4.0.0: resolved "https://registry.yarnpkg.com/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0" integrity sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA== -istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.0.1, istanbul-lib-coverage@^3.2.0: +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.0.0-alpha.1, istanbul-lib-coverage@^3.0.1, istanbul-lib-coverage@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== +istanbul-lib-hook@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz#8f84c9434888cc6b1d0a9d7092a76d239ebf0cc6" + integrity sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ== + dependencies: + append-transform "^2.0.0" + +istanbul-lib-instrument@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" + integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== + dependencies: + "@babel/core" "^7.7.5" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.0.0" + semver "^6.3.0" + istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz#7b49198b657b27a730b8e9cb601f1e1bff24c59a" @@ -10325,6 +10478,19 @@ istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: istanbul-lib-coverage "^3.2.0" semver "^6.3.0" +istanbul-lib-processinfo@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz#e1426514662244b2f25df728e8fd1ba35fe53b9c" + integrity sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw== + dependencies: + archy "^1.0.0" + cross-spawn "^7.0.0" + istanbul-lib-coverage "^3.0.0-alpha.1" + make-dir "^3.0.0" + p-map "^3.0.0" + rimraf "^3.0.0" + uuid "^3.3.3" + istanbul-lib-report@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" @@ -10364,12 +10530,12 @@ iterate-value@^1.0.2: es-get-iterator "^1.0.2" iterate-iterator "^1.0.1" -jake@^10.6.1: - version "10.8.4" - resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.4.tgz#f6a8b7bf90c6306f768aa82bb7b98bf4ca15e84a" - integrity sha512-MtWeTkl1qGsWUtbl/Jsca/8xSoK3x0UmS82sNbjqxxG/de/M/3b1DntdjHgPMC50enlTNwXOCRqPXLLt5cCfZA== +jake@^10.8.5: + version "10.8.5" + resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46" + integrity sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw== dependencies: - async "0.9.x" + async "^3.2.3" chalk "^4.0.2" filelist "^1.0.1" minimatch "^3.0.4" @@ -10878,7 +11044,7 @@ js-tokens@^3.0.2: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= -js-yaml@^3.13.1: +js-yaml@3.14.1, js-yaml@^3.13.1: version "3.14.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== @@ -11257,6 +11423,11 @@ lodash.flatten@^4.2.0: resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= +lodash.flattendeep@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" + integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI= + lodash.flow@^3.3.0: version "3.5.0" resolved "https://registry.yarnpkg.com/lodash.flow/-/lodash.flow-3.5.0.tgz#87bf40292b8cf83e4e8ce1a3ae4209e20071675a" @@ -11400,7 +11571,7 @@ make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0: dependencies: semver "^6.0.0" -make-error@1.x: +make-error@1.x, make-error@^1.1.1: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== @@ -11446,18 +11617,13 @@ markdown-to-jsx@^7.1.3: resolved "https://registry.yarnpkg.com/markdown-to-jsx/-/markdown-to-jsx-7.1.7.tgz#a5f22102fb12241c8cea1ca6a4050bb76b23a25d" integrity sha512-VI3TyyHlGkO8uFle0IOibzpO1c1iJDcXcS/zBrQrXQQvJ2tpdwVzVZ7XdKsyRz1NdRmre4dqQkMZzUHaKIG/1w== -marked@^1.2.9: - version "1.2.9" - resolved "https://registry.yarnpkg.com/marked/-/marked-1.2.9.tgz#53786f8b05d4c01a2a5a76b7d1ec9943d29d72dc" - integrity sha512-H8lIX2SvyitGX+TRdtS06m1jHMijKN/XjfH6Ooii9fvxMlh8QdqBfBDkGUpMWH2kQNrtixjzYUa3SH8ROTgRRw== - -mathjs@10.4.3: - version "10.4.3" - resolved "https://registry.yarnpkg.com/mathjs/-/mathjs-10.4.3.tgz#456ae944204809e8e8266ff265b1ef724d03f90e" - integrity sha512-C50lWorCOplBec9Ik5fzhHuOx4G4+mtdz3r1G2I1/r8yj+CpYFXLXNqTdg59oKmIF1tKcIzpxlC4s2dGL7f3pg== +mathjs@10.5.0: + version "10.5.0" + resolved "https://registry.yarnpkg.com/mathjs/-/mathjs-10.5.0.tgz#f81d0518fe7b4b2a0b85e1125b8ecfc364fb0292" + integrity sha512-gRnSY9psN9zgiB2QV9F4XbuX5hwjxY5Ou7qoTFWDbn2vZ3UEs+sjfK/SRg2WP30TNfZWpwlGdp8H1knFJnpFdA== dependencies: - "@babel/runtime" "^7.17.8" - complex.js "^2.1.0" + "@babel/runtime" "^7.17.9" + complex.js "^2.1.1" decimal.js "^10.3.1" escape-latex "^1.2.0" fraction.js "^4.2.0" @@ -11533,11 +11699,6 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= -medium-zoom@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/medium-zoom/-/medium-zoom-1.0.6.tgz#9247f21ca9313d8bbe9420aca153a410df08d027" - integrity sha512-UdiUWfvz9fZMg1pzf4dcuqA0W079o0mpqbTnOz5ip4VGYX96QjmbM+OgOU/0uOzAytxC0Ny4z+VcYQnhdifimg== - mem@^8.1.1: version "8.1.1" resolved "https://registry.yarnpkg.com/mem/-/mem-8.1.1.tgz#cf118b357c65ab7b7e0817bdf00c8062297c0122" @@ -11741,6 +11902,13 @@ minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" +minimatch@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" + integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== + dependencies: + brace-expansion "^2.0.1" + minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: version "1.2.6" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" @@ -11826,10 +11994,10 @@ moduleserve@0.9.1: send "^0.17.1" serve-static "^1.14.1" -moment@^2.24.0, moment@^2.25.3: - version "2.29.2" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.2.tgz#00910c60b20843bcba52d37d58c628b47b1f20e4" - integrity sha512-UgzG4rvxYpN15jgCmVJwac49h9ly9NurikMWGPdVxm8GZD6XjkKPxDTjQQ43gtGgnV3X0cAyWDdP2Wexoquifg== +moment@^2.24.0, moment@^2.29.2: + version "2.29.3" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.3.tgz#edd47411c322413999f7a5940d526de183c031f3" + integrity sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw== move-concurrently@^1.0.1: version "1.0.1" @@ -11996,6 +12164,13 @@ node-libs-browser@^2.2.1: util "^0.11.0" vm-browserify "^1.0.1" +node-preload@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/node-preload/-/node-preload-0.2.1.tgz#c03043bb327f417a18fee7ab7ee57b408a144301" + integrity sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ== + dependencies: + process-on-spawn "^1.0.0" + node-releases@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.3.tgz#225ee7488e4a5e636da8da52854844f9d716ca96" @@ -12091,6 +12266,39 @@ nwsapi@^2.2.0: resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== +nyc@^15.1.0: + version "15.1.0" + resolved "https://registry.yarnpkg.com/nyc/-/nyc-15.1.0.tgz#1335dae12ddc87b6e249d5a1994ca4bdaea75f02" + integrity sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A== + dependencies: + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.2" + caching-transform "^4.0.0" + convert-source-map "^1.7.0" + decamelize "^1.2.0" + find-cache-dir "^3.2.0" + find-up "^4.1.0" + foreground-child "^2.0.0" + get-package-type "^0.1.0" + glob "^7.1.6" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-hook "^3.0.0" + istanbul-lib-instrument "^4.0.0" + istanbul-lib-processinfo "^2.0.2" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.0.2" + make-dir "^3.0.0" + node-preload "^0.2.1" + p-map "^3.0.0" + process-on-spawn "^1.0.0" + resolve-from "^5.0.0" + rimraf "^3.0.0" + signal-exit "^3.0.2" + spawn-wrap "^2.0.0" + test-exclude "^6.0.0" + yargs "^15.0.2" + object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -12248,11 +12456,6 @@ open@^8.0.9, open@^8.4.0: is-docker "^2.1.1" is-wsl "^2.2.0" -opencollective-postinstall@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz#7a0fff978f6dbfa4d006238fbac98ed4198c3259" - integrity sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q== - opener@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" @@ -12421,6 +12624,16 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +package-hash@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/package-hash/-/package-hash-4.0.0.tgz#3537f654665ec3cc38827387fc904c163c54f506" + integrity sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ== + dependencies: + graceful-fs "^4.1.15" + hasha "^5.0.0" + lodash.flattendeep "^4.4.0" + release-zalgo "^1.0.0" + package-json@^6.3.0: version "6.5.0" resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" @@ -13412,7 +13625,7 @@ prism-react-renderer@^1.2.1, prism-react-renderer@^1.3.1: resolved "https://registry.yarnpkg.com/prism-react-renderer/-/prism-react-renderer-1.3.1.tgz#88fc9d0df6bed06ca2b9097421349f8c2f24e30d" integrity sha512-xUeDMEz074d0zc5y6rxiMp/dlC7C+5IDDlaEUlcBOFE2wddz7hz5PNupb087mPwTt7T9BrFmewObfCBuf/LKwQ== -prismjs@^1.21.0, prismjs@^1.23.0, prismjs@^1.27.0, prismjs@~1.27.0: +prismjs@^1.21.0, prismjs@^1.27.0, prismjs@~1.27.0: version "1.27.0" resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.27.0.tgz#bb6ee3138a0b438a3653dd4d6ce0cc6510a45057" integrity sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA== @@ -13422,6 +13635,13 @@ process-nextick-args@~2.0.0: resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== +process-on-spawn@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/process-on-spawn/-/process-on-spawn-1.0.0.tgz#95b05a23073d30a17acfdc92a440efd2baefdc93" + integrity sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg== + dependencies: + fromentries "^1.2.0" + process@^0.11.10: version "0.11.10" resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" @@ -13573,6 +13793,11 @@ pure-color@^1.2.0: resolved "https://registry.yarnpkg.com/pure-color/-/pure-color-1.3.0.tgz#1fe064fb0ac851f0de61320a8bf796836422f33e" integrity sha1-H+Bk+wrIUfDeYTIKi/eWg2Qi8z4= +pure-rand@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-5.0.1.tgz#97a287b4b4960b2a3448c0932bf28f2405cac51d" + integrity sha512-ksWccjmXOHU2gJBnH0cK1lSYdvSZ0zLoCMSz/nTGh6hDvCSgcRxDyIcOBD6KNxFz3xhMPm/T267Tbe2JRymKEQ== + q@^1.1.2: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" @@ -13664,11 +13889,6 @@ range-parser@^1.2.1, range-parser@~1.2.1: resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -rationale@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/rationale/-/rationale-0.2.0.tgz#555ed4f3cc7cd0245faeac041d3769f1857e4f3d" - integrity sha512-Pd8w5Inv1JhTfRyx03zs486CEAn6UKXvvOtxVRLsewngsBSffo3MQwUKYS75L/8vPt98wmf7iaZROx362/f7Bw== - raw-body@2.4.3: version "2.4.3" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.3.tgz#8f80305d11c2a0a545c2d9d89d7a0286fcead43c" @@ -13699,16 +13919,16 @@ rc-align@^4.0.0: rc-util "^5.3.0" resize-observer-polyfill "^1.5.1" -rc-cascader@~3.2.1: - version "3.2.9" - resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-3.2.9.tgz#b993fa2829d77e9cb98cf4b7711e13a1b1812db6" - integrity sha512-Mvkegzf506PD7qc38kg2tGllIBXs5dio3DPg+NER7SiOfCXBCATWYEs0CbUp8JDQgYHoHF0vPvFMYtxFTJuWaw== +rc-cascader@~3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-3.5.0.tgz#a49b632bc2d0c8ef31b212c8ddd0bea346e64877" + integrity sha512-rpXnWCfvk7Frh2dBzMoA0c7i0nn6aJU7L2NZo8R8pNkrT0sKgytQSpdtPWP+Pq8IkvwbEd8BU8Z8OnOljcqgZg== dependencies: "@babel/runtime" "^7.12.5" array-tree-filter "^2.1.0" classnames "^2.3.1" - rc-select "~14.0.0-alpha.23" - rc-tree "~5.4.3" + rc-select "~14.1.0" + rc-tree "~5.5.0" rc-util "^5.6.1" rc-checkbox@~2.3.0: @@ -13730,15 +13950,15 @@ rc-collapse@~3.1.0: rc-util "^5.2.1" shallowequal "^1.1.0" -rc-dialog@~8.6.0: - version "8.6.0" - resolved "https://registry.yarnpkg.com/rc-dialog/-/rc-dialog-8.6.0.tgz#3b228dac085de5eed8c6237f31162104687442e7" - integrity sha512-GSbkfqjqxpZC5/zc+8H332+q5l/DKUhpQr0vdX2uDsxo5K0PhvaMEVjyoJUTkZ3+JstEADQji1PVLVb/2bJeOQ== +rc-dialog@~8.8.0, rc-dialog@~8.8.1: + version "8.8.1" + resolved "https://registry.yarnpkg.com/rc-dialog/-/rc-dialog-8.8.1.tgz#cd8897fbee1de0eab6d237a6abe1e4db8d09dd72" + integrity sha512-7M1WKZCjfIABKEaJVskdYvb80z+RX7I11PeSjPVfLOOaJAmIepvDEd0alBtOZvOL3fZFWlMs4JVZtp9LZgONxA== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.6" rc-motion "^2.3.0" - rc-util "^5.6.1" + rc-util "^5.21.0" rc-drawer@~4.4.2: version "4.4.3" @@ -13749,43 +13969,33 @@ rc-drawer@~4.4.2: classnames "^2.2.6" rc-util "^5.7.0" -rc-dropdown@^3.2.0: - version "3.4.1" - resolved "https://registry.yarnpkg.com/rc-dropdown/-/rc-dropdown-3.4.1.tgz#909e8c666a9f994bd804147aaf7f8f5859dae0db" - integrity sha512-Q+1s64b21H5Ye1/1MVY9hKrdsv2MJhrtrnZ4R2O3TqeHoJTddvkDp9VmjMYFEKLdkKzYZ7BIA+9bvNB5dAILXg== +rc-dropdown@~3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/rc-dropdown/-/rc-dropdown-3.5.0.tgz#6ff2e6aaacd3641aa4040cfa941ab9634ad5f1ed" + integrity sha512-HHMpzO6AJt3I2jBG8fFK9LqFMQhHn/V09AzQnqCCV8Fp22tNCS98Obelcc4C8T6ZlZR+/w01im0BQVP3o1Y+Cw== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.6" rc-trigger "^5.0.4" rc-util "^5.17.0" -rc-dropdown@~3.3.2: - version "3.3.3" - resolved "https://registry.yarnpkg.com/rc-dropdown/-/rc-dropdown-3.3.3.tgz#17ba32ebd066ae397b00e9e4d570c7c21daed88f" - integrity sha512-UNe68VpvtrpU0CS4jh5hD4iGqzi4Pdp7uOya6+H3QIEZxe7K+Xs11BNjZm6W4MaL0jTmzUj+bxvnq5bP3rRoVQ== - dependencies: - "@babel/runtime" "^7.10.1" - classnames "^2.2.6" - rc-trigger "^5.0.4" - rc-util "^5.17.0" - -rc-field-form@~1.25.0: - version "1.25.2" - resolved "https://registry.yarnpkg.com/rc-field-form/-/rc-field-form-1.25.2.tgz#de418194b7aca2f1b6e0e059edd97b5cf624f68a" - integrity sha512-FXGScWibDlwIlKY15T1YOA7VTtMJwqxxXdDjHB56ZNx7wGbE4vK+Fe2zcymyakGZD0ej8NUP5LGr7qBVWaVpUQ== +rc-field-form@~1.26.1: + version "1.26.2" + resolved "https://registry.yarnpkg.com/rc-field-form/-/rc-field-form-1.26.2.tgz#69d92811eed09f9e1f74704695b13253bb2ae534" + integrity sha512-Q1QdpLAt/kxd119kJwGfFvn/ZIzjzTBJsCscy5k0z3g+eRMHkI0Exij6SE2D42N7FAzVkvuXTvzqWSiFGeer7g== dependencies: "@babel/runtime" "^7.8.4" async-validator "^4.0.2" rc-util "^5.8.0" -rc-image@~5.2.5: - version "5.2.5" - resolved "https://registry.yarnpkg.com/rc-image/-/rc-image-5.2.5.tgz#44e6ffc842626827960e7ab72e1c0d6f3a8ce440" - integrity sha512-qUfZjYIODxO0c8a8P5GeuclYXZjzW4hV/5hyo27XqSFo1DmTCs2HkVeQObkcIk5kNsJtgsj1KoPThVsSc/PXOw== +rc-image@~5.6.0: + version "5.6.1" + resolved "https://registry.yarnpkg.com/rc-image/-/rc-image-5.6.1.tgz#60662a8fc0fe91d7ebd7fd5ee972d5b6226850e2" + integrity sha512-nmcobNCfmeevsep6eL7KNHVwFdLz4As4Vx0o90nnUFNzZ9Pqost1s10gOf4Wl6XW2iMR9LU6ztm5EazM7yA4Gg== dependencies: "@babel/runtime" "^7.11.2" classnames "^2.2.6" - rc-dialog "~8.6.0" + rc-dialog "~8.8.0" rc-util "^5.0.6" rc-input-number@~7.3.0: @@ -13806,22 +14016,22 @@ rc-input@~0.0.1-alpha.5: classnames "^2.2.1" rc-util "^5.18.1" -rc-mentions@~1.6.1: - version "1.6.5" - resolved "https://registry.yarnpkg.com/rc-mentions/-/rc-mentions-1.6.5.tgz#d9516abd19a757c674df1c88a3459628fe95a149" - integrity sha512-CUU4+q+awG2pA0l/tG2kPB2ytWbKQUkFxVeKwacr63w7crE/yjfzrFXxs/1fxhyEbQUWdAZt/L25QBieukYQ5w== +rc-mentions@~1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/rc-mentions/-/rc-mentions-1.7.0.tgz#717be883e92b9085df900ab5a3ffab7379247bfa" + integrity sha512-d3tZWCQIseQrn5ZpnUuaeKTQctgGwVzcEUVpVswxvnsLB1/e2H12xHzVqH87AvPkHMs9m3oFZINbuC5Qxevv6g== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.6" - rc-menu "~9.3.2" + rc-menu "~9.5.1" rc-textarea "^0.3.0" rc-trigger "^5.0.4" rc-util "^5.0.1" -rc-menu@~9.3.2: - version "9.3.2" - resolved "https://registry.yarnpkg.com/rc-menu/-/rc-menu-9.3.2.tgz#bb842d37ebf71da912bea201cf7ef0a27267ad49" - integrity sha512-h3m45oY1INZyqphGELkdT0uiPnFzxkML8m0VMhJnk2fowtqfiT7F5tJLT3znEVaPIY80vMy1bClCkgq8U91CzQ== +rc-menu@~9.5.1, rc-menu@~9.5.5: + version "9.5.5" + resolved "https://registry.yarnpkg.com/rc-menu/-/rc-menu-9.5.5.tgz#aa2f151d4191ed089dc1a8141fe365c9b77d61a9" + integrity sha512-wj2y2BAKwSMyWXO3RBf9sNN5V+DFWxFl45Ma6qQEHA5nwwh7p07bNgc6AAJc+L1+LAz+rWz3AU8PYyT17hMHCw== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" @@ -13831,24 +14041,24 @@ rc-menu@~9.3.2: rc-util "^5.12.0" shallowequal "^1.1.0" -rc-motion@^2.0.0, rc-motion@^2.0.1, rc-motion@^2.2.0, rc-motion@^2.3.0, rc-motion@^2.3.4, rc-motion@^2.4.3, rc-motion@^2.4.4: - version "2.4.9" - resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.4.9.tgz#fa6e4b044b971845ffb83696e1c4d11b81bf132f" - integrity sha512-lrIpBQQ5gIDVedaubnhXuTjC3zpW7HvC/34KyvcHlf6fBjuBlwv45PbonFhmk4Rgu7gLQYrKoMGgFVXqxxyLCw== +rc-motion@^2.0.0, rc-motion@^2.0.1, rc-motion@^2.2.0, rc-motion@^2.3.0, rc-motion@^2.3.4, rc-motion@^2.4.3, rc-motion@^2.4.4, rc-motion@^2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.5.1.tgz#3eceb7d891079c0f67a72639d30e168b91839e03" + integrity sha512-h3GKMjFJkK+4z6fNfVlIMrb7WFCZsreivVvHOBb38cKcpKDx5g3kpHwn5Ekbo1+g0nnC02Dtap2trfCAPGxllw== dependencies: "@babel/runtime" "^7.11.1" classnames "^2.2.1" - rc-util "^5.19.2" + rc-util "^5.21.0" -rc-notification@~4.5.7: - version "4.5.7" - resolved "https://registry.yarnpkg.com/rc-notification/-/rc-notification-4.5.7.tgz#265e6e6a0c1a0fac63d6abd4d832eb8ff31522f1" - integrity sha512-zhTGUjBIItbx96SiRu3KVURcLOydLUHZCPpYEn1zvh+re//Tnq/wSxN4FKgp38n4HOgHSVxcLEeSxBMTeBBDdw== +rc-notification@~4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/rc-notification/-/rc-notification-4.6.0.tgz#4e76fc2d0568f03cc93ac18c9e20763ebe29fa46" + integrity sha512-xF3MKgIoynzjQAO4lqsoraiFo3UXNYlBfpHs0VWvwF+4pimen9/H1DYLN2mfRWhHovW6gRpla73m2nmyIqAMZQ== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" rc-motion "^2.2.0" - rc-util "^5.0.1" + rc-util "^5.20.1" rc-overflow@^1.0.0, rc-overflow@^1.2.0: version "1.2.4" @@ -13910,10 +14120,20 @@ rc-resize-observer@^1.0.0, rc-resize-observer@^1.1.0, rc-resize-observer@^1.2.0: rc-util "^5.15.0" resize-observer-polyfill "^1.5.1" -rc-select@~14.0.0-alpha.23, rc-select@~14.0.0-alpha.8, rc-select@~14.0.2: - version "14.0.6" - resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-14.0.6.tgz#93be0b185a9d66dc84795e079121f0f65310d8bf" - integrity sha512-HMb2BwfTvBxMmIWTR/afP4bcRJLbVKFSBW/VFfL5Z+kdV2XlrYdlliK2uHY7pRRvW16PPGwmOwGfV+eoulPINw== +rc-segmented@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/rc-segmented/-/rc-segmented-2.0.0.tgz#209b55bec85c1a8b1821c30e62d3ebef4da04b52" + integrity sha512-YsdS+aP7E6ZMEY35WSlewJIsrjPbBSP4X/7RvZtzLExKDZwFvXdCPCbWFVDNks4jOYY9TUPYt7qlVifEu9/zXA== + dependencies: + "@babel/runtime" "^7.11.1" + classnames "^2.2.1" + rc-motion "^2.4.4" + rc-util "^5.17.0" + +rc-select@~14.1.0, rc-select@~14.1.1: + version "14.1.1" + resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-14.1.1.tgz#87a51ce515aba5cfa083ae0f5be15e7c550ad93f" + integrity sha512-l2TSSy/rwvfob0SmQ0sPQ1pUMUq65u6U4Y9lc9dvQOMSMzDSga4b3tEgIgzN1YKzakV65wGXMOBVecjixPEZ4Q== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" @@ -13923,7 +14143,7 @@ rc-select@~14.0.0-alpha.23, rc-select@~14.0.0-alpha.8, rc-select@~14.0.2: rc-util "^5.16.1" rc-virtual-list "^3.2.0" -rc-slider@~10.0.0-alpha.4: +rc-slider@~10.0.0: version "10.0.0" resolved "https://registry.yarnpkg.com/rc-slider/-/rc-slider-10.0.0.tgz#8ffe1dd3c8799c9d1f81ac808976f18af3dca206" integrity sha512-Bk54UIKWW4wyhHcL8ehAxt+wX+n69dscnHTX6Uv0FMxSke/TGrlkZz1LSIWblCpfE2zr/dwR2Ca8nZGk3U+Tbg== @@ -13952,10 +14172,10 @@ rc-switch@~3.2.0: classnames "^2.2.1" rc-util "^5.0.1" -rc-table@~7.23.0: - version "7.23.2" - resolved "https://registry.yarnpkg.com/rc-table/-/rc-table-7.23.2.tgz#f6f906e8fafb05ddbfdd69d450feb875ce260a7b" - integrity sha512-opc2IBJOetsPSdNI+u1Lh9yY4Ks+EMgo1oJzZN+yIV4fRcgP81tHtxdPOVvXPFI4rUMO8CKnmHbGPU7jxMRAeg== +rc-table@~7.24.0: + version "7.24.1" + resolved "https://registry.yarnpkg.com/rc-table/-/rc-table-7.24.1.tgz#15ecabc9d69f8300b988caa52986e3b215150f2b" + integrity sha512-DRWpv5z5pmOaTmy5GqWoskeV1thaOu5HuD+2f61b/CkbBqlgJR3cygc5R/Qvd2uVW6pHU0lYulhmz0VLVFm+rw== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.5" @@ -13963,15 +14183,15 @@ rc-table@~7.23.0: rc-util "^5.14.0" shallowequal "^1.1.0" -rc-tabs@~11.10.0: - version "11.10.8" - resolved "https://registry.yarnpkg.com/rc-tabs/-/rc-tabs-11.10.8.tgz#832d3425bde232b9c4447075b5deef3e2fefa48f" - integrity sha512-uK+x+eJ8WM4jiXoqGa+P+JUQX2Wlkj9f0o/5dyOw42B6YLnHJN80uTVcCeAmtA1N0xjPW0GNSZvUm4SU3jAYpw== +rc-tabs@~11.13.0: + version "11.13.0" + resolved "https://registry.yarnpkg.com/rc-tabs/-/rc-tabs-11.13.0.tgz#083eed578f8ad02dc0d462d73da487fe32e3a573" + integrity sha512-aUw1Pq0B1a2zGX4o/m3yrQycZcCLgDp6gKwn8IAU07q148RRONsVGxi0oLVVe5SE51kOB+j0bk1RX43ZBdZNgA== dependencies: "@babel/runtime" "^7.11.2" classnames "2.x" - rc-dropdown "^3.2.0" - rc-menu "~9.3.2" + rc-dropdown "~3.5.0" + rc-menu "~9.5.1" rc-resize-observer "^1.0.0" rc-util "^5.5.0" @@ -13994,21 +14214,21 @@ rc-tooltip@^5.0.1, rc-tooltip@~5.1.1: "@babel/runtime" "^7.11.2" rc-trigger "^5.0.0" -rc-tree-select@~5.1.1: - version "5.1.5" - resolved "https://registry.yarnpkg.com/rc-tree-select/-/rc-tree-select-5.1.5.tgz#ed51cc45eb490d18d67eba6864e9c7321199fcc0" - integrity sha512-OXAwCFO0pQmb48NcjUJtiX6rp4FroCXMfzqPmuVVoBGBV/uwO1TPyb+uBZ2/972zkCA8u4je5M5Qx51sL8y7jg== +rc-tree-select@~5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/rc-tree-select/-/rc-tree-select-5.3.0.tgz#6edd19d1066ad2bfa212f043c3ff701b93828026" + integrity sha512-UN6CUBulmch+CsihnJ73+DtWijEB1hVTC8sdVxq6E0teVAkHQZUvDj+cwZShtShAKvWwXy73PZ1hIHEUrmVcKw== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" - rc-select "~14.0.0-alpha.8" - rc-tree "~5.4.3" + rc-select "~14.1.0" + rc-tree "~5.5.0" rc-util "^5.16.1" -rc-tree@~5.4.3: - version "5.4.4" - resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-5.4.4.tgz#2ea3663ad3c566aef79a46ba6a1e050d24323e01" - integrity sha512-2qoObRgp31DBXmVzMJmo4qmwP20XEa4hR3imWQtRPcgN3pmljW3WKFmZRrYdOFHz7CyTnRsFZR065bBkIoUpiA== +rc-tree@~5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-5.5.0.tgz#ba7c8aea2ad29f40a9c7168e490300f7a50c0f22" + integrity sha512-vpKeFsDyj7weik8UPseCTaSNAPt939qn1dQd8goSbRDajbjJEja0v/WFXyRhOiF1HLemNTfqMz4MYc9qlqyNXg== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" @@ -14036,10 +14256,10 @@ rc-upload@~4.3.0: classnames "^2.2.5" rc-util "^5.2.0" -rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.12.0, rc-util@^5.14.0, rc-util@^5.15.0, rc-util@^5.16.1, rc-util@^5.17.0, rc-util@^5.18.1, rc-util@^5.19.2, rc-util@^5.19.3, rc-util@^5.2.0, rc-util@^5.2.1, rc-util@^5.3.0, rc-util@^5.4.0, rc-util@^5.5.0, rc-util@^5.6.1, rc-util@^5.7.0, rc-util@^5.8.0, rc-util@^5.9.4, rc-util@^5.9.8: - version "5.20.1" - resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.20.1.tgz#323590df56175f60b1a67d2ba76f04c3c2cb84cd" - integrity sha512-2IEyErPAYl0Up5gBu71e8IkOs+/SL9XRUvnGhtsr7IHlXLx2OsbQKTDpWacJbzLCmNcgJylDGj1kiklx+zagRA== +rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.12.0, rc-util@^5.14.0, rc-util@^5.15.0, rc-util@^5.16.1, rc-util@^5.17.0, rc-util@^5.18.1, rc-util@^5.19.2, rc-util@^5.2.0, rc-util@^5.2.1, rc-util@^5.20.0, rc-util@^5.20.1, rc-util@^5.21.0, rc-util@^5.3.0, rc-util@^5.4.0, rc-util@^5.5.0, rc-util@^5.6.1, rc-util@^5.7.0, rc-util@^5.8.0, rc-util@^5.9.4, rc-util@^5.9.8: + version "5.21.2" + resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.21.2.tgz#fa23277ba84e5561af2febdca64de3fc2b3e1528" + integrity sha512-QuuZ2tKMScGtxSx3rLzgPGGDZm/np7phMqA7OcDidSf44abvSk+AdtdD7ZvQPvCEtdC6nCSI5tEVnUaYjjD9/w== dependencies: "@babel/runtime" "^7.12.5" react-is "^16.12.0" @@ -14064,10 +14284,10 @@ rc@^1.2.8: minimist "^1.2.0" strip-json-comments "~2.0.1" -react-ace@10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/react-ace/-/react-ace-10.0.0.tgz#1760e302604cff35ba40963db43eb027513b6572" - integrity sha512-AUoA2OsKOCv8fXLqcFM232dF/Z8w14bwPUZ9z5I2zjBfqfZOZLqxnhXN+qKL6VrQXs1DLUvalGOuM5TABAFOCA== +react-ace@10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/react-ace/-/react-ace-10.1.0.tgz#d348eac2b16475231779070b6cd16768deed565f" + integrity sha512-VkvUjZNhdYTuKOKQpMIZi7uzZZVgzCjM7cLYu6F64V0mejY8a2XTyPUIMszC6A4trbeMIHbK5fYFcT/wkP/8VA== dependencies: ace-builds "^1.4.14" diff-match-patch "^1.0.5" @@ -14097,13 +14317,6 @@ react-base16-styling@^0.6.0: lodash.flow "^3.3.0" pure-color "^1.2.0" -react-codejar@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/react-codejar/-/react-codejar-1.1.2.tgz#b55789f8c7e5360bb63f3d2501c99e49453845cf" - integrity sha512-xGmwZ3ij1AQNkpeJUgOIqFzgZx9Nl4/onflOt6FjJrexzRMkBowAqmLTlLzZGdA8QmCSJf7hSlrClHZGFC8b4A== - dependencies: - codejar "^3.2.3" - react-colorful@^5.1.2: version "5.5.1" resolved "https://registry.yarnpkg.com/react-colorful/-/react-colorful-5.5.1.tgz#29d9c4e496f2ca784dd2bb5053a3a4340cfaf784" @@ -14160,13 +14373,13 @@ react-docgen@^5.0.0: node-dir "^0.1.10" strip-indent "^3.0.0" -react-dom@^18.0.0: - version "18.0.0" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.0.0.tgz#26b88534f8f1dbb80853e1eabe752f24100d8023" - integrity sha512-XqX7uzmFo0pUceWFCt7Gff6IyIMzFUn7QMZrbrQfGxtaxXZIcGQzoNpRLE3fQLnS4XzLLPMZX2T9TRcSrasicw== +react-dom@^18.1.0: + version "18.1.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.1.0.tgz#7f6dd84b706408adde05e1df575b3a024d7e8a2f" + integrity sha512-fU1Txz7Budmvamp7bshe4Zi32d0ll7ect+ccxNu9FlObT605GOEB8BfO4tmRJ39R5Zj831VCpvQ05QPBW5yb+w== dependencies: loose-envify "^1.1.0" - scheduler "^0.21.0" + scheduler "^0.22.0" react-draggable@^4.4.3: version "4.4.4" @@ -14414,10 +14627,10 @@ react-vega@^7.5.0: fast-deep-equal "^3.1.1" vega-embed "^6.5.1" -react@^18.0.0: - version "18.0.0" - resolved "https://registry.yarnpkg.com/react/-/react-18.0.0.tgz#b468736d1f4a5891f38585ba8e8fb29f91c3cb96" - integrity sha512-x+VL6wbT4JRVPm7EGxXhZ8w8LTROaxPXOqhlGyVSrv0sB1jkyFGgXxJ8LVoPRLvPR6/CIZGFmfzqUa2NYeMr2A== +react@^18.0.0, react@^18.1.0: + version "18.1.0" + resolved "https://registry.yarnpkg.com/react/-/react-18.1.0.tgz#6f8620382decb17fdc5cc223a115e2adbf104890" + integrity sha512-4oL8ivCz5ZEPyclFQXaNksK3adutVS8l2xzZU0cqEFrE9Sb7fC0EFK5uEk74wIreL1DERyjvsU915j1pcT2uEQ== dependencies: loose-envify "^1.1.0" @@ -14483,6 +14696,11 @@ reading-time@^1.5.0: resolved "https://registry.yarnpkg.com/reading-time/-/reading-time-1.5.0.tgz#d2a7f1b6057cb2e169beaf87113cc3411b5bc5bb" integrity sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg== +reanalyze@^2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/reanalyze/-/reanalyze-2.19.0.tgz#b34b16bd7249a07dc931bb3cc58410247bb36a03" + integrity sha512-ycVr19BH6b2J70BmG6nUIzrFWiBNLUCmip6D2NEPLpnJimF6wagE+BIksI6vcTCovqWjgNMKhFCbZKwm+5/8VQ== + rechoir@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" @@ -14648,6 +14866,13 @@ relateurl@^0.2.7: resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk= +release-zalgo@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/release-zalgo/-/release-zalgo-1.0.0.tgz#09700b7e5074329739330e535c5a90fb67851730" + integrity sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA= + dependencies: + es6-error "^4.0.1" + remark-admonitions@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/remark-admonitions/-/remark-admonitions-1.2.1.tgz#87caa1a442aa7b4c0cafa04798ed58a342307870" @@ -14791,11 +15016,23 @@ require-from-string@^2.0.2: resolved "https://registry.yarnpkg.com/require-like/-/require-like-0.1.2.tgz#ad6f30c13becd797010c468afa775c0c0a6b47fa" integrity sha1-rW8wwTvs15cBDEaK+ndcDAprR/o= +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + requires-port@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= +rescript-fast-check@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/rescript-fast-check/-/rescript-fast-check-1.1.1.tgz#ef153cb01254b2f01a738faf85b73327d423d5e1" + integrity sha512-wxeW0TsL/prkRvEYGbhEiLaLKmYJaECyDcKEWh65hDqP2i76lARzVW3QmYujSYK4OnjAC70dln3X6UC/2m2Huw== + dependencies: + fast-check "^2.17.0" + rescript@^9.1.4: version "9.1.4" resolved "https://registry.yarnpkg.com/rescript/-/rescript-9.1.4.tgz#1eb126f98d6c16942c0bf0df67c050198e580515" @@ -15045,10 +15282,10 @@ saxes@^5.0.1: dependencies: xmlchars "^2.2.0" -scheduler@^0.21.0: - version "0.21.0" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.21.0.tgz#6fd2532ff5a6d877b6edb12f00d8ab7e8f308820" - integrity sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ== +scheduler@^0.22.0: + version "0.22.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.22.0.tgz#83a5d63594edf074add9a7198b1bae76c3db01b8" + integrity sha512-6QAm1BgQI88NPYymgGQLCZgvep4FyePDWFpXVK+zNSUgHwlqpJy8VEh8Et0KxTACS4VWwMousBElAZOH9nkkoQ== dependencies: loose-envify "^1.1.0" @@ -15544,6 +15781,18 @@ space-separated-tokens@^1.0.0: resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz#85f32c3d10d9682007e917414ddc5c26d1aa6899" integrity sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA== +spawn-wrap@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/spawn-wrap/-/spawn-wrap-2.0.0.tgz#103685b8b8f9b79771318827aa78650a610d457e" + integrity sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg== + dependencies: + foreground-child "^2.0.0" + is-windows "^1.0.2" + make-dir "^3.0.0" + rimraf "^3.0.0" + signal-exit "^3.0.2" + which "^2.0.1" + spdx-correct@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" @@ -15685,6 +15934,13 @@ stream-each@^1.1.0: end-of-stream "^1.1.0" stream-shift "^1.0.0" +stream-events@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/stream-events/-/stream-events-1.0.5.tgz#bbc898ec4df33a4902d892333d47da9bf1c406d5" + integrity sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg== + dependencies: + stubs "^3.0.0" + stream-http@^2.7.2: version "2.8.3" resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" @@ -15884,6 +16140,11 @@ strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= +stubs@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/stubs/-/stubs-3.0.0.tgz#e8d2ba1fa9c90570303c030b6900f7d5f89abe5b" + integrity sha1-6NK6H6nJBXAwPAMLaQD31fiavls= + style-loader@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-1.3.0.tgz#828b4a3b3b7e7aa5847ce7bae9e874512114249e" @@ -16081,6 +16342,17 @@ tar@^6.0.2: mkdirp "^1.0.3" yallist "^4.0.0" +teeny-request@7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/teeny-request/-/teeny-request-7.1.1.tgz#2b0d156f4a8ad81de44303302ba8d7f1f05e20e6" + integrity sha512-iwY6rkW5DDGq8hE2YgNQlKbptYpY5Nn2xecjQiNjOXWbKzPGUfmeUBCSQbbr306d7Z7U2N0TPl+/SwYRfua1Dg== + dependencies: + http-proxy-agent "^4.0.0" + https-proxy-agent "^5.0.0" + node-fetch "^2.6.1" + stream-events "^1.0.5" + uuid "^8.0.0" + telejson@^5.3.2, telejson@^5.3.3: version "5.3.3" resolved "https://registry.yarnpkg.com/telejson/-/telejson-5.3.3.tgz#fa8ca84543e336576d8734123876a9f02bf41d2e" @@ -16237,11 +16509,6 @@ tiny-warning@^1.0.0, tiny-warning@^1.0.3: resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== -tinydate@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/tinydate/-/tinydate-1.3.0.tgz#e6ca8e5a22b51bb4ea1c3a2a4fd1352dbd4c57fb" - integrity sha512-7cR8rLy2QhYHpsBDBVYnnWXm8uRTr38RoZakFSW7Bs7PzfMPNZthuMLkwqZv7MTu8lhQ91cOFYS5a7iFj2oR3w== - tmpl@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" @@ -16388,16 +16655,35 @@ ts-jest@^27.1.4: semver "7.x" yargs-parser "20.x" -ts-loader@^9.2.8: - version "9.2.8" - resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.2.8.tgz#e89aa32fa829c5cad0a1d023d6b3adecd51d5a48" - integrity sha512-gxSak7IHUuRtwKf3FIPSW1VpZcqF9+MBrHOvBp9cjHh+525SjtCIJKVGjRKIAfxBwDGDGCFF00rTfzB1quxdSw== +ts-loader@^9.2.8, ts-loader@^9.2.9: + version "9.2.9" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.2.9.tgz#0653e07fa1b4f225d0ca57a84fddbfd43d930f9e" + integrity sha512-b0+vUY2/enb0qYtDQuNlDnJ9900NTiPiJcDJ6sY7ax1CCCwXfYIqPOMm/BwW7jsF1km+Oz8W9s31HLuD+FLIMg== dependencies: chalk "^4.1.0" enhanced-resolve "^5.0.0" micromatch "^4.0.0" semver "^7.3.4" +ts-node@^10.7.0: + version "10.7.0" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.7.0.tgz#35d503d0fab3e2baa672a0e94f4b40653c2463f5" + integrity sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A== + dependencies: + "@cspotcode/source-map-support" "0.7.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.0" + yn "3.1.1" + ts-pnp@^1.1.6: version "1.2.0" resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92" @@ -16444,11 +16730,6 @@ tty-browserify@0.0.0: resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY= -tweezer.js@^1.4.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/tweezer.js/-/tweezer.js-1.5.0.tgz#ca50ac5215022203fd3be4d28617e8e2305f5c0c" - integrity sha512-aSiJz7rGWNAQq7hjMK9ZYDuEawXupcCWgl3woQQSoDP2Oh8O4srWb/uO1PzzHIsrPEOqrjJ2sUb9FERfzuBabQ== - type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -16488,7 +16769,7 @@ type-fest@^0.6.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== -type-fest@^0.8.1: +type-fest@^0.8.0, type-fest@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== @@ -16804,6 +17085,13 @@ url@^0.11.0: punycode "1.3.2" querystring "0.2.0" +urlgrey@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/urlgrey/-/urlgrey-1.0.0.tgz#72d2f904482d0b602e3c7fa599343d699bbe1017" + integrity sha512-hJfIzMPJmI9IlLkby8QrsCykQ+SXDeO2W5Q9QTW3QpqZVTx4a/K7p8/5q+/isD8vsbVaFgql/gvAoQCRQ2Cb5w== + dependencies: + fast-url-parser "^1.1.3" + use-composed-ref@^1.0.0: version "1.2.1" resolved "https://registry.yarnpkg.com/use-composed-ref/-/use-composed-ref-1.2.1.tgz#9bdcb5ccd894289105da2325e1210079f56bf849" @@ -16883,16 +17171,21 @@ uuid-browser@^3.1.0: resolved "https://registry.yarnpkg.com/uuid-browser/-/uuid-browser-3.1.0.tgz#0f05a40aef74f9e5951e20efbf44b11871e56410" integrity sha1-DwWkCu90+eWVHiDvv0SxGHHlZBA= -uuid@^3.3.2: +uuid@^3.3.2, uuid@^3.3.3: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -uuid@^8.3.2: +uuid@^8.0.0, uuid@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +v8-compile-cache-lib@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + v8-compile-cache@^2.0.3: version "2.3.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" @@ -17733,6 +18026,11 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + which@^1.2.9, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" @@ -17966,6 +18264,15 @@ worker-rpc@^0.1.0: dependencies: microevent.ts "~0.1.1" +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -18066,6 +18373,31 @@ yargs-parser@20.x, yargs-parser@^20.2.2, yargs-parser@^20.2.7: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== +yargs-parser@^18.1.2: + version "18.1.3" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs@^15.0.2: + version "15.4.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" + integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== + dependencies: + cliui "^6.0.0" + decamelize "^1.2.0" + find-up "^4.1.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^4.2.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^18.1.2" + yargs@^16.2.0: version "16.2.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" @@ -18092,6 +18424,11 @@ yargs@~17.2.1: y18n "^5.0.5" yargs-parser "^20.2.2" +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"