Merge pull request #1057 from quantified-uncertainty/develop

V0.3.1
This commit is contained in:
Ozzie Gooen 2022-09-01 11:23:27 -07:00 committed by GitHub
commit 4ecb692e80
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
63 changed files with 3224 additions and 2348 deletions

87
.github/workflows/ci-cachix.yml vendored Normal file
View File

@ -0,0 +1,87 @@
name: Nix build
on:
push:
branches:
- master
- develop
pull_request:
branches:
- master
- develop
- reducer-dev
- epic-reducer-project
jobs:
flake-lints:
name: All lint
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Install nix
uses: cachix/install-nix-action@v17
with:
nix_path: nixpkgs=channel:nixos-22.05
- name: Use cachix
uses: cachix/cachix-action@v10
with:
name: quantified-uncertainty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
- name: Check that lang lints
run: nix build .#lang-lint
- name: Check that components lints
run: nix build .#components-lint
- name: Check that website lints
run: nix build .#docusaurus-lint
- name: Check that vscode extension lints
run: nix build .#vscode-lint
- name: Check that cli lints
run: nix build .#cli-lint
flake-packages:
name: Builds, tests, and bundles
runs-on: ubuntu-latest
needs: flake-lints
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Install nix
uses: cachix/install-nix-action@v17
with:
nix_path: nixpkgs=channel:nixos-22.05
- name: Use cachix
uses: cachix/cachix-action@v10
with:
name: quantified-uncertainty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
- name: Check all lang tests
run: nix build .#lang-test
- name: Check that lang bundles
run: nix build .#lang-bundle
- name: Check that components builds
run: nix build .#components
- name: Check that components bundles
run: nix build .#components-bundle
flake-devshells:
name: Development shell environment
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Install nix
uses: cachix/install-nix-action@v17
with:
nix_path: nixpkgs=channel:nixos-22.05
- name: Use cachix
uses: cachix/cachix-action@v10
with:
name: quantified-uncertainty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
- name: Build js devshell
run: nix develop .#js --profile just-js
- name: Build js & wasm devshell
run: nix develop --profile full-shell

View File

@ -10,6 +10,7 @@ on:
- master - master
- develop - develop
- reducer-dev - reducer-dev
- epic-reducer-project
jobs: jobs:
pre_check: pre_check:
@ -48,26 +49,26 @@ jobs:
with: with:
paths: '["packages/cli/**"]' paths: '["packages/cli/**"]'
lang-lint: # lang-lint:
name: Language lint # name: Language lint
runs-on: ubuntu-latest # runs-on: ubuntu-latest
needs: pre_check # needs: pre_check
if: ${{ needs.pre_check.outputs.should_skip_lang != 'true' }} # if: ${{ needs.pre_check.outputs.should_skip_lang != 'true' }}
defaults: # defaults:
run: # run:
shell: bash # shell: bash
working-directory: packages/squiggle-lang # working-directory: packages/squiggle-lang
steps: # steps:
- uses: actions/checkout@v3 # - uses: actions/checkout@v3
- name: Install Dependencies # - name: Install Dependencies
run: cd ../../ && yarn # run: cd ../../ && yarn
- name: Check rescript lint # - name: Check rescript lint
run: yarn lint:rescript # run: yarn lint:rescript
- name: Check javascript, typescript, and markdown lint # - name: Check javascript, typescript, and markdown lint
uses: creyD/prettier_action@v4.2 # uses: creyD/prettier_action@v4.2
with: # with:
dry: true # dry: true
prettier_options: --check packages/squiggle-lang # prettier_options: --check packages/squiggle-lang
lang-build-test-bundle: lang-build-test-bundle:
name: Language build, test, and bundle name: Language build, test, and bundle
@ -97,95 +98,96 @@ jobs:
- name: Upload typescript coverage report - name: Upload typescript coverage report
run: yarn coverage:ts:ci run: yarn coverage:ts:ci
components-lint: # components-lint:
name: Components lint # name: Components lint
runs-on: ubuntu-latest # runs-on: ubuntu-latest
needs: pre_check # needs: pre_check
if: ${{ needs.pre_check.outputs.should_skip_components != 'true' }} # if: ${{ needs.pre_check.outputs.should_skip_components != 'true' }}
defaults: # defaults:
run: # run:
shell: bash # shell: bash
working-directory: packages/components # working-directory: packages/components
steps: # steps:
- uses: actions/checkout@v3 # - uses: actions/checkout@v3
- name: Check javascript, typescript, and markdown lint # - name: Check javascript, typescript, and markdown lint
uses: creyD/prettier_action@v4.2 # uses: creyD/prettier_action@v4.2
with: # with:
dry: true # dry: true
prettier_options: --check packages/components --ignore-path packages/components/.prettierignore # prettier_options: --check packages/components --ignore-path packages/components/.prettierignore
#
# components-bundle-build:
# name: Components bundle and build
# runs-on: ubuntu-latest
# needs: pre_check
# if: ${{ (needs.pre_check.outputs.should_skip_components != 'true') || (needs.pre_check.outputs.should_skip_lang != 'true') }}
# defaults:
# run:
# shell: bash
# working-directory: packages/components
# steps:
# - uses: actions/checkout@v3
# - name: Install dependencies from monorepo level
# run: cd ../../ && yarn
# - name: Build rescript codebase in squiggle-lang
# run: cd ../squiggle-lang && yarn build
# - name: Run webpack
# run: yarn bundle
# - name: Build storybook
# run: yarn build
components-bundle-build: # website-lint:
name: Components bundle and build # name: Website lint
runs-on: ubuntu-latest # runs-on: ubuntu-latest
needs: pre_check # needs: pre_check
if: ${{ (needs.pre_check.outputs.should_skip_components != 'true') || (needs.pre_check.outputs.should_skip_lang != 'true') }} # if: ${{ needs.pre_check.outputs.should_skip_website != 'true' }}
defaults: # defaults:
run: # run:
shell: bash # shell: bash
working-directory: packages/components # working-directory: packages/website
steps: # steps:
- uses: actions/checkout@v3 # - uses: actions/checkout@v3
- name: Install dependencies from monorepo level # - name: Check javascript, typescript, and markdown lint
run: cd ../../ && yarn # uses: creyD/prettier_action@v4.2
- name: Build rescript codebase in squiggle-lang # with:
run: cd ../squiggle-lang && yarn build # dry: true
- name: Run webpack # prettier_options: --check packages/website
run: yarn bundle #
- name: Build storybook # website-build:
run: yarn build # name: Website build
# runs-on: ubuntu-latest
website-lint: # needs: pre_check
name: Website lint # if: ${{ (needs.pre_check.outputs.should_skip_website != 'true') || (needs.pre_check.outputs.should_skip_lang != 'true') || (needs.pre_check.outputs.should_skip_components != 'true') }}
runs-on: ubuntu-latest # defaults:
needs: pre_check # run:
if: ${{ needs.pre_check.outputs.should_skip_website != 'true' }} # shell: bash
defaults: # working-directory: packages/website
run: # steps:
shell: bash # - uses: actions/checkout@v3
working-directory: packages/website # - name: Install dependencies from monorepo level
steps: # run: cd ../../ && yarn
- uses: actions/checkout@v3 # - name: Build rescript in squiggle-lang
- name: Check javascript, typescript, and markdown lint # run: cd ../squiggle-lang && yarn build
uses: creyD/prettier_action@v4.2 # - name: Build components
with: # run: cd ../components && yarn build
dry: true # - name: Build website assets
prettier_options: --check packages/website # run: yarn build
#
website-build: # vscode-ext-lint:
name: Website build # name: VS Code extension lint
runs-on: ubuntu-latest # runs-on: ubuntu-latest
needs: pre_check # needs: pre_check
if: ${{ (needs.pre_check.outputs.should_skip_website != 'true') || (needs.pre_check.outputs.should_skip_lang != 'true') || (needs.pre_check.outputs.should_skip_components != 'true') }} # if: ${{ needs.pre_check.outputs.should_skip_vscodeext != 'true' }}
defaults: # defaults:
run: # run:
shell: bash # shell: bash
working-directory: packages/website # working-directory: packages/vscode-ext
steps: # steps:
- uses: actions/checkout@v3 # - uses: actions/checkout@v3
- name: Install dependencies from monorepo level # - name: Check javascript, typescript, and markdown lint
run: cd ../../ && yarn # uses: creyD/prettier_action@v4.2
- name: Build rescript in squiggle-lang # with:
run: cd ../squiggle-lang && yarn build # dry: true
- name: Build components # prettier_options: --check packages/vscode-ext
run: cd ../components && yarn build
- name: Build website assets
run: yarn build
vscode-ext-lint:
name: VS Code extension lint
runs-on: ubuntu-latest
needs: pre_check
if: ${{ needs.pre_check.outputs.should_skip_vscodeext != 'true' }}
defaults:
run:
shell: bash
working-directory: packages/vscode-ext
steps:
- uses: actions/checkout@v3
- name: Install dependencies from monorepo level
run: cd ../../ && yarn
- name: Lint the VSCode Extension source code
run: yarn lint
vscode-ext-build: vscode-ext-build:
name: VS Code extension build name: VS Code extension build
@ -202,20 +204,19 @@ jobs:
run: cd ../../ && yarn run: cd ../../ && yarn
- name: Build - name: Build
run: yarn compile run: yarn compile
# cli-lint:
cli-lint: # name: CLI lint
name: CLI lint # runs-on: ubuntu-latest
runs-on: ubuntu-latest # needs: pre_check
needs: pre_check # if: ${{ needs.pre_check.outputs.should_skip_cli != 'true' }}
if: ${{ needs.pre_check.outputs.should_skip_cli != 'true' }} # defaults:
defaults: # run:
run: # shell: bash
shell: bash # working-directory: packages/cli
working-directory: packages/cli # steps:
steps: # - uses: actions/checkout@v3
- uses: actions/checkout@v3 # - name: Check javascript, typescript, and markdown lint
- name: Check javascript, typescript, and markdown lint # uses: creyD/prettier_action@v4.2
uses: creyD/prettier_action@v4.2 # with:
with: # dry: true
dry: true # prettier_options: --check packages/cli
prettier_options: --check packages/cli

View File

@ -3,7 +3,7 @@ name: Run Release Please
on: on:
push: push:
branches: branches:
- develop - master
jobs: jobs:
pre_check: pre_check:
@ -55,21 +55,22 @@ jobs:
token: ${{secrets.GITHUB_TOKEN}} token: ${{secrets.GITHUB_TOKEN}}
command: manifest-pr command: manifest-pr
path: packages/squiggle-lang path: packages/squiggle-lang
bump-patch-for-minor-pre-major: true # bump-patch-for-minor-pre-major: true
skip-github-release: true skip-github-release: true
# - name: Publish: Checkout source - name: Publish- Checkout source
# uses: actions/checkout@v2 uses: actions/checkout@v3
# # these if statements ensure that a publication only occurs when # these if statements ensure that a publication only occurs when
# # a new release is created: # a new release is created:
# if: ${{ steps.release.outputs.release_created }} if: ${{ steps.release.outputs.release_created }}
# - name: Publish: Install dependencies - name: Publish- Install dependencies
# run: yarn run: yarn
# if: ${{ steps.release.outputs.release_created }} if: ${{ steps.release.outputs.release_created }}
# - name: Publish - name: Publish
# run: cd packages/squiggle-lang && yarn publish run: cd packages/squiggle-lang && yarn publish
# env: env:
# NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
# if: ${{ steps.release.outputs.release_created }} if: ${{ steps.release.outputs.release_created }}
relplz-components: relplz-components:
name: for components name: for components
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -82,20 +83,20 @@ jobs:
token: ${{secrets.GITHUB_TOKEN}} token: ${{secrets.GITHUB_TOKEN}}
command: manifest-pr command: manifest-pr
path: packages/components path: packages/components
bump-patch-for-minor-pre-major: true # bump-patch-for-minor-pre-major: true
skip-github-release: true skip-github-release: true
# - name: Publish: Checkout source - name: Publish- Checkout source
# uses: actions/checkout@v2 uses: actions/checkout@v3
# # these if statements ensure that a publication only occurs when # these if statements ensure that a publication only occurs when
# # a new release is created: # a new release is created:
# if: ${{ steps.release.outputs.release_created }} if: ${{ steps.release.outputs.release_created }}
# - name: Publish: Install dependencies - name: Publish- Install dependencies
# run: yarn run: yarn
# if: ${{ steps.release.outputs.release_created }} if: ${{ steps.release.outputs.release_created }}
# - name: Publish - name: Publish
# run: cd packages/components && yarn publish run: cd packages/components && yarn publish
# env: env:
# NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
relplz-website: relplz-website:
name: for website name: for website
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -108,7 +109,7 @@ jobs:
token: ${{secrets.GITHUB_TOKEN}} token: ${{secrets.GITHUB_TOKEN}}
command: manifest-pr command: manifest-pr
path: packages/website path: packages/website
bump-patch-for-minor-pre-major: true # bump-patch-for-minor-pre-major: true
skip-github-release: true skip-github-release: true
relplz-vscodeext: relplz-vscodeext:
name: for vscode-ext name: for vscode-ext
@ -122,7 +123,7 @@ jobs:
token: ${{secrets.GITHUB_TOKEN}} token: ${{secrets.GITHUB_TOKEN}}
command: manifest-pr command: manifest-pr
path: packages/vscode-ext path: packages/vscode-ext
bump-patch-for-minor-pre-major: true # bump-patch-for-minor-pre-major: true
skip-github-release: true skip-github-release: true
relplz-cl: relplz-cl:
name: for cli name: for cli

1
.gitignore vendored
View File

@ -7,3 +7,4 @@ yarn-error.log
**/.sync.ffs_db **/.sync.ffs_db
.direnv .direnv
.log .log
result

View File

@ -1,15 +1,16 @@
.direnv .direnv
*.bs.js *.bs.js
*.gen.tsx *.gen.tsx
packages/*/dist
packages/components/storybook-static packages/components/storybook-static
node_modules node_modules
packages/*/node_modules packages/*/node_modules
packages/website/.docusaurus packages/website/.docusaurus
packages/squiggle-lang/lib packages/squiggle-lang/lib
packages/squiggle-lang/.nyc_output/
packages/squiggle-lang/coverage/ packages/squiggle-lang/coverage/
packages/squiggle-lang/.cache/ packages/squiggle-lang/.cache/
packages/website/build/ packages/website/build/
packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_GeneratedParser.js packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_GeneratedParser.js
packages/vscode-ext/media/vendor/ packages/vscode-ext/media/vendor/
packages/squiggle-lang/.nyc_output/
packages/*/dist
result

View File

@ -1,7 +1,7 @@
{ {
"packages/cli": "0.0.3", "packages/cli": "0.0.3",
"packages/components": "0.2.24", "packages/components": "0.3.1",
"packages/squiggle-lang": "0.2.11", "packages/squiggle-lang": "0.3.0",
"packages/vscode-ext": "0.3.1", "packages/vscode-ext": "0.3.1",
"packages/website": "0.2.1" "packages/website": "0.3.0"
} }

79
flake.lock Normal file
View File

@ -0,0 +1,79 @@
{
"nodes": {
"flake-utils": {
"locked": {
"lastModified": 1659877975,
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_2": {
"locked": {
"lastModified": 1659877975,
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"gentype": {
"inputs": {
"flake-utils": "flake-utils_2",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1661855866,
"narHash": "sha256-+q0OOTyaq8eOn9BOWdPOCtSDOISW4A59v3mq3JOZyug=",
"owner": "rescript-association",
"repo": "genType",
"rev": "6b5f164b4f6ced456019b7579a0ab7e0a86518ad",
"type": "github"
},
"original": {
"owner": "rescript-association",
"repo": "genType",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1661617163,
"narHash": "sha256-NN9Ky47j8ohgPhA9JZyfkYIbbAo6RJkGz+7h8/exVpE=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "0ba2543f8c855d7be8e90ef6c8dc89c1617e8a08",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixos-22.05",
"type": "indirect"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"gentype": "gentype",
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

99
flake.nix Normal file
View File

@ -0,0 +1,99 @@
{
description = "Squiggle packages";
inputs = {
nixpkgs.url = "nixpkgs/nixos-22.05";
gentype = {
url = "github:rescript-association/genType";
inputs.nixpkgs.follows = "nixpkgs";
};
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, gentype, flake-utils }:
let
version = builtins.substring 0 8 self.lastModifiedDate;
overlays = [
(final: prev: {
# set the node version here
nodejs = prev.nodejs-18_x;
# The override is the only way to get it into mkYarnModules
})
];
commonFn = pkgs: {
buildInputs = with pkgs; [ nodejs yarn ];
prettier = with pkgs.nodePackages; [ prettier ];
which = [ pkgs.which ];
};
gentypeOutputFn = pkgs: gentype.outputs.packages.${pkgs.system}.default;
langFn = { pkgs, ... }:
# Probably doesn't work on i686-linux
import ./nix/squiggle-lang.nix {
inherit pkgs commonFn gentypeOutputFn;
};
componentsFn = { pkgs, ... }:
import ./nix/squiggle-components.nix { inherit pkgs commonFn langFn; };
websiteFn = { pkgs, ... }:
import ./nix/squiggle-website.nix {
inherit pkgs commonFn langFn componentsFn;
};
vscodeextFn = { pkgs, ... }:
import ./nix/squiggle-vscode.nix {
inherit pkgs commonFn langFn componentsFn;
};
cliFn = { pkgs, ... }:
import ./nix/squiggle-cli.nix {
inherit pkgs commonFn;
};
# local machines
localFlakeOutputs = { pkgs, ... }:
let
lang = langFn pkgs;
components = componentsFn pkgs;
website = websiteFn pkgs;
vscodeext = vscodeextFn pkgs;
cli = cliFn pkgs;
in {
# validating
checks = flake-utils.lib.flattenTree {
lang-lint = lang.lint;
lang-test = lang.test;
components-lint = components.lint;
docusaurus-lint = website.lint;
cli-lint = cli.lint;
};
# building
packages = flake-utils.lib.flattenTree {
default = components.build;
lang = lang.build;
lang-bundle = lang.bundle;
lang-test = lang.test;
components = components.build;
components-bundle = components.bundle;
# Lint
lang-lint = lang.lint;
components-lint = components.lint;
docusaurus-lint = website.lint;
vscode-lint = vscodeext.lint;
cli-lint = cli.lint;
};
# developing
devShells = let shellNix = import ./nix/shell.nix { inherit pkgs; };
in flake-utils.lib.flattenTree {
default = shellNix.all;
js = shellNix.just-js;
};
};
in flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs {
inherit system;
overlays = overlays;
};
in localFlakeOutputs pkgs);
}

1
nix/README.md Normal file
View File

@ -0,0 +1 @@
Visit `quantified-uncertainty.cachix.org` for information about how to add our binary cache to your local dev environment.

25
nix/shell.nix Normal file
View File

@ -0,0 +1,25 @@
{ pkgs }:
with pkgs;
let
js = [ yarn nodejs nodePackages.ts-node ];
rust = [
wasm-pack
cargo
rustup
pkg-config
libressl
rustfmt
wasmtime
binaryen
wasm-bindgen-cli
];
in {
all = mkShell {
name = "squiggle_yarn-wasm-devshell";
buildInputs = builtins.concatLists [ js rust [ nixfmt ] ];
};
just-js = mkShell {
name = "squiggle_yarn-devshell";
buildInputs = js ++ [ nixfmt ];
};
}

13
nix/squiggle-cli.nix Normal file
View File

@ -0,0 +1,13 @@
{ pkgs, commonFn }:
rec {
common = commonFn pkgs;
lint = pkgs.stdenv.mkDerivation {
name = "squiggle-cli-lint";
buildInputs = common.buildInputs ++ common.prettier;
src = ../packages/cli;
buildPhase = "prettier --check .";
installPhase = "mkdir -p $out";
};
}

View File

@ -0,0 +1,75 @@
{ pkgs, commonFn, langFn }:
rec {
common = commonFn pkgs;
lang = langFn pkgs;
componentsPackageJson = let
raw = pkgs.lib.importJSON ../packages/components/package.json;
modified =
pkgs.lib.recursiveUpdate raw { dependencies.react-dom = "^18.2.0"; };
packageJsonString = builtins.toJSON modified;
in pkgs.writeText "packages/components/patched-package.json"
packageJsonString;
yarn-source = pkgs.mkYarnPackage {
name = "squiggle-components_yarnsource";
buildInputs = common.buildInputs;
src = ../packages/components;
packageJSON = componentsPackageJson;
yarnLock = ../yarn.lock;
packageResolutions."@quri/squiggle-lang" = lang.build;
};
lint = pkgs.stdenv.mkDerivation {
name = "squiggle-components-lint";
src = ../packages/components;
buildInputs = common.buildInputs ++ common.prettier;
buildPhase = "yarn lint";
installPhase = "mkdir -p $out";
};
build = pkgs.stdenv.mkDerivation {
name = "squiggle-components-build";
src = yarn-source + "/libexec/@quri/squiggle-components";
buildInputs = common.buildInputs;
buildPhase = ''
cp -r node_modules/@quri/squiggle-lang deps/@quri
pushd deps/@quri/squiggle-components
yarn --offline build:cjs
yarn --offline build:css
popd
'';
installPhase = ''
mkdir -p $out
# annoying hack because permissions on transitive dependencies later on
mv deps/@quri/squiggle-components/node_modules deps/@quri/squiggle-components/NODE_MODULES
mv node_modules deps/@quri/squiggle-components
# patching .gitignore so flake keeps build artefacts
sed -i /dist/d deps/@quri/squiggle-components/.gitignore
cp -r deps/@quri/squiggle-components/. $out
'';
};
bundle = pkgs.stdenv.mkDerivation {
name = "squiggle-components-bundle";
src = yarn-source + "/libexec/@quri/squiggle-components";
buildInputs = common.buildInputs;
buildPhase = ''
cp -r node_modules/@quri/squiggle-lang deps/@quri
pushd deps/@quri/squiggle-components
yarn --offline bundle
popd
'';
installPhase = ''
mkdir -p $out
# annoying hack because permissions on transitive dependencies later on
mv deps/@quri/squiggle-components/node_modules deps/@quri/squiggle-components/NODE_MODULES
mv node_modules deps/@quri/squiggle-components
# patching .gitignore so flake keeps build artefacts
sed -i /dist/d deps/@quri/squiggle-components/.gitignore
cp -r deps/@quri/squiggle-components/. $out
'';
};
}

125
nix/squiggle-lang.nix Normal file
View File

@ -0,0 +1,125 @@
{ pkgs, commonFn, gentypeOutputFn }:
rec {
common = commonFn pkgs;
langPackageJson = let
raw = pkgs.lib.importJSON ../packages/squiggle-lang/package.json;
modified = pkgs.lib.recursiveUpdate raw {
devDependencies."@types/lodash" = "^4.14.167";
};
packageJsonString = builtins.toJSON modified;
in pkgs.writeText "packages/squiggle-lang/patched-package.json"
packageJsonString;
yarn-source = pkgs.mkYarnPackage {
name = "squiggle-lang_yarnsource";
src = ../packages/squiggle-lang;
packageJSON = langPackageJson;
yarnLock = ../yarn.lock;
pkgConfig = {
rescript = {
buildInputs = common.which
++ (if pkgs.system != "i686-linux" then [ pkgs.gcc_multi ] else [ ]);
postInstall = ''
echo "PATCHELF'ING RESCRIPT EXECUTABLES (INCL NINJA)"
# Patching interpreter for linux/*.exe's
THE_LD=$(patchelf --print-interpreter $(which mkdir))
patchelf --set-interpreter $THE_LD linux/*.exe && echo "- patched interpreter for linux/*.exe's"
# Replacing needed shared library for linux/ninja.exe
THE_SO=$(find /nix/store/*/lib64 -name libstdc++.so.6 | head -n 1)
patchelf --replace-needed libstdc++.so.6 $THE_SO linux/ninja.exe && echo "- replaced needed for linux/ninja.exe"
'';
};
bisect_ppx = {
buildInputs = common.which;
postInstall = ''
echo "PATCHELF'ING BISECT_PPX EXECUTABLE"
THE_LD=$(patchelf --print-interpreter $(which mkdir))
patchelf --set-interpreter $THE_LD bin/linux/ppx
patchelf --set-interpreter $THE_LD bin/linux/bisect-ppx-report
cp bin/linux/ppx ppx
'';
};
gentype = {
postInstall = ''
mv gentype.exe ELFLESS-gentype.exe
cp ${gentypeOutputFn pkgs}/src/GenType.exe gentype.exe
'';
};
};
};
lint = pkgs.stdenv.mkDerivation {
name = "squiggle-lang-lint";
src = yarn-source + "/libexec/@quri/squiggle-lang/deps/@quri/squiggle-lang";
buildInputs = common.buildInputs ++ common.prettier;
buildPhase = ''
yarn lint:prettier
yarn lint:rescript
'';
installPhase = "mkdir -p $out";
};
build = pkgs.stdenv.mkDerivation {
name = "squiggle-lang-build";
# `peggy` is in the `node_modules` that's adjacent to `deps`.
src = yarn-source + "/libexec/@quri/squiggle-lang";
buildInputs = common.buildInputs;
buildPhase = ''
# so that the path to ppx doesn't need to be patched.
mv node_modules deps
pushd deps/@quri/squiggle-lang
yarn --offline build:peggy
yarn --offline build:rescript
yarn --offline build:typescript
# custom gitignore so that the flake keeps build artefacts
mv .gitignore GITIGNORE
sed -i /Reducer_Peggy_GeneratedParser.js/d GITIGNORE
sed -i /\*.bs.js/d GITIGNORE
sed -i /\*.gen.ts/d GITIGNORE
sed -i /\*.gen.tsx/d GITIGNORE
sed -i /\*.gen.js/d GITIGNORE
sed -i /helpers.js/d GITIGNORE
popd
'';
installPhase = ''
mkdir -p $out
# mkdir -p $out/node_modules
mv deps/@quri/squiggle-lang/GITIGNORE deps/@quri/squiggle-lang/.gitignore
# annoying hack because permissions on transitive dependencies later on
mv deps/@quri/squiggle-lang/node_modules deps/@quri/squiggle-lang/NODE_MODULES
mv deps/node_modules deps/@quri/squiggle-lang
# the proper install phase
cp -r deps/@quri/squiggle-lang/. $out
'';
};
test = pkgs.stdenv.mkDerivation {
name = "squiggle-lang-test";
src = build;
buildInputs = common.buildInputs;
buildPhase = ''
yarn --offline test
'';
installPhase = ''
mkdir -p $out
cp -r . $out
'';
};
bundle = pkgs.stdenv.mkDerivation {
name = "squiggle-lang-bundle";
src = test;
buildInputs = common.buildInputs;
buildPhase = ''
yarn --offline bundle
'';
installPhase = ''
mkdir -p $out
cp -r dist $out
cp *.json $out/dist
'';
};
}

24
nix/squiggle-vscode.nix Normal file
View File

@ -0,0 +1,24 @@
{ pkgs, commonFn, langFn, componentsFn }:
rec {
common = commonFn pkgs;
lang = langFn pkgs;
components = componentsFn pkgs;
yarn-source = pkgs.mkYarnPackage {
name = "squiggle-vscodeext_yarnsource";
src = ../packages/vscode-ext;
packageJson = ../packages/vscode-ext/package.json;
yarnLock = ../yarn.lock;
packageResolutions."@quri/squiggle-lang" = lang.build;
packageResolutions."@quri/squiggle-components" = components.build;
};
lint = pkgs.stdenv.mkDerivation {
name = "squiggle-vscode-lint";
buildInputs = common.buildInputs ++ common.prettier;
src =
../packages/vscode-ext; # yarn-source + "/libexec/vscode-squiggle/deps/vscode-squiggle";
buildPhase = "prettier --check .";
installPhase = "mkdir -p $out";
};
}

30
nix/squiggle-website.nix Normal file
View File

@ -0,0 +1,30 @@
{ pkgs, commonFn, langFn, componentsFn }:
rec {
common = commonFn pkgs;
lang = langFn pkgs;
components = componentsFn pkgs;
websitePackageJson = let
raw = pkgs.lib.importJSON ../packages/website/package.json;
modified = pkgs.lib.recursiveUpdate raw {
dependencies.postcss-import = "^14.1.0";
dependencies.tailwindcss = "^3.1.8";
};
packageJsonString = builtins.toJSON modified;
in pkgs.writeText "packages/website/patched-package.json" packageJsonString;
yarn-source = pkgs.mkYarnPackage {
name = "squiggle-website_yarnsource";
src = ../packages/website;
packageJSON = websitePackageJson;
yarnLock = ../yarn.lock;
packageResolutions."@quri/squiggle-lang" = lang.build;
packageResolutions."@quri/squiggle-components" = components.build;
};
lint = pkgs.stdenv.mkDerivation {
name = "squiggle-website-lint";
buildInputs = common.buildInputs ++ common.prettier;
src = ../packages/website;
buildPhase = "yarn lint";
installPhase = "mkdir -p $out";
};
}

View File

@ -5,14 +5,14 @@
# We need to patchelf rescript executables. https://github.com/NixOS/nixpkgs/issues/107375 # We need to patchelf rescript executables. https://github.com/NixOS/nixpkgs/issues/107375
set -x set -x
fhsShellName="squiggle-development" fhsShellName="squiggle-fhs-development"
fhsShellDotNix="{pkgs ? import <nixpkgs> {} }: (pkgs.buildFHSUserEnv { name = \"${fhsShellName}\"; targetPkgs = pkgs: [pkgs.yarn]; runScript = \"yarn\"; }).env" fhsShellDotNix="{pkgs ? import <nixpkgs> {} }: (pkgs.buildFHSUserEnv { name = \"${fhsShellName}\"; targetPkgs = pkgs: [pkgs.yarn pkgs.glibc]; runScript = \"yarn\"; }).env"
nix-shell - <<<"$fhsShellDotNix" nix-shell - <<<"$fhsShellDotNix"
theLd=$(patchelf --print-interpreter $(which mkdir)) theLd=$(patchelf --print-interpreter $(which mkdir))
patchelf --set-interpreter $theLd ./node_modules/gentype/gentype.exe patchelf --set-interpreter $theLd ./node_modules/gentype/gentype.exe
patchelf --set-interpreter $theLd ./node_modules/rescript/linux/*.exe patchelf --set-interpreter $theLd ./node_modules/rescript/linux/*.exe
patchelf --set-interpreter $theLd ./node_modules/bisect_ppx/ppx patchelf --set-interpreter $theLd ./node_modules/bisect_ppx/ppx
patchelf --set-interpreter $theLd ./node_moduels/bisect_ppx/bisect-ppx-report patchelf --set-interpreter $theLd ./node_modules/bisect_ppx/bisect-ppx-report
theSo=$(find /nix/store/*$fhsShellName*/lib64 -name libstdc++.so.6 | grep $fhsShellName | head -n 1) theSo=$(find /nix/store/*$fhsShellName*/lib64 -name libstdc++.so.6 | head -n 1)
patchelf --replace-needed libstdc++.so.6 $theSo ./node_modules/rescript/linux/ninja.exe patchelf --replace-needed libstdc++.so.6 $theSo ./node_modules/rescript/linux/ninja.exe

View File

@ -1,26 +1,26 @@
{ {
"name": "@quri/squiggle-components", "name": "@quri/squiggle-components",
"version": "0.2.24", "version": "0.3.2",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@floating-ui/react-dom": "^1.0.0", "@floating-ui/react-dom": "^1.0.0",
"@floating-ui/react-dom-interactions": "^0.9.1", "@floating-ui/react-dom-interactions": "^0.9.3",
"@headlessui/react": "^1.6.6", "@headlessui/react": "^1.6.6",
"@heroicons/react": "^1.0.6", "@heroicons/react": "^1.0.6",
"@hookform/resolvers": "^2.9.7", "@hookform/resolvers": "^2.9.7",
"@quri/squiggle-lang": "^0.2.8", "@quri/squiggle-lang": "^0.3.0",
"@react-hook/size": "^2.1.2", "@react-hook/size": "^2.1.2",
"clsx": "^1.2.1", "clsx": "^1.2.1",
"framer-motion": "^7.0.0", "framer-motion": "^7.2.1",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"react": "^18.1.0", "react": "^18.1.0",
"react-ace": "^10.1.0", "react-ace": "^10.1.0",
"react-hook-form": "^7.34.0", "react-hook-form": "^7.34.2",
"react-use": "^17.4.0", "react-use": "^17.4.0",
"react-vega": "^7.6.0", "react-vega": "^7.6.0",
"vega": "^5.22.1", "vega": "^5.22.1",
"vega-embed": "^6.21.0", "vega-embed": "^6.21.0",
"vega-lite": "^5.4.0", "vega-lite": "^5.5.0",
"vscode-uri": "^3.0.3", "vscode-uri": "^3.0.3",
"yup": "^0.32.11" "yup": "^0.32.11"
}, },
@ -36,12 +36,12 @@
"@storybook/react": "^6.5.10", "@storybook/react": "^6.5.10",
"@testing-library/jest-dom": "^5.16.5", "@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.3.0", "@testing-library/react": "^13.3.0",
"@testing-library/user-event": "^14.4.2", "@testing-library/user-event": "^14.4.3",
"@types/jest": "^27.5.0", "@types/jest": "^27.5.0",
"@types/lodash": "^4.14.182", "@types/lodash": "^4.14.184",
"@types/node": "^18.6.4", "@types/node": "^18.7.13",
"@types/react": "^18.0.9", "@types/react": "^18.0.9",
"@types/styled-components": "^5.1.24", "@types/styled-components": "^5.1.26",
"@types/webpack": "^5.28.0", "@types/webpack": "^5.28.0",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"mini-css-extract-plugin": "^2.6.1", "mini-css-extract-plugin": "^2.6.1",
@ -54,11 +54,11 @@
"tailwindcss": "^3.1.8", "tailwindcss": "^3.1.8",
"ts-loader": "^9.3.0", "ts-loader": "^9.3.0",
"tsconfig-paths-webpack-plugin": "^4.0.0", "tsconfig-paths-webpack-plugin": "^4.0.0",
"typescript": "^4.7.4", "typescript": "^4.8.2",
"web-vitals": "^2.1.4", "web-vitals": "^3.0.0",
"webpack": "^5.74.0", "webpack": "^5.74.0",
"webpack-cli": "^4.10.0", "webpack-cli": "^4.10.0",
"webpack-dev-server": "^4.9.3" "webpack-dev-server": "^4.10.0"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^16.8.0 || ^17 || ^18", "react": "^16.8.0 || ^17 || ^18",

View File

@ -4,6 +4,8 @@ import {
result, result,
distributionError, distributionError,
distributionErrorToString, distributionErrorToString,
squiggleExpression,
resultMap,
} from "@quri/squiggle-lang"; } from "@quri/squiggle-lang";
import { Vega } from "react-vega"; import { Vega } from "react-vega";
import { ErrorAlert } from "./Alert"; import { ErrorAlert } from "./Alert";
@ -14,6 +16,8 @@ import {
DistributionChartSpecOptions, DistributionChartSpecOptions,
} from "../lib/distributionSpecBuilder"; } from "../lib/distributionSpecBuilder";
import { NumberShower } from "./NumberShower"; import { NumberShower } from "./NumberShower";
import { Plot, parsePlot } from "../lib/plotParser";
import { flattenResult } from "../lib/utility";
import { hasMassBelowZero } from "../lib/distributionUtils"; import { hasMassBelowZero } from "../lib/distributionUtils";
export type DistributionPlottingSettings = { export type DistributionPlottingSettings = {
@ -23,26 +27,41 @@ export type DistributionPlottingSettings = {
} & DistributionChartSpecOptions; } & DistributionChartSpecOptions;
export type DistributionChartProps = { export type DistributionChartProps = {
distribution: Distribution; plot: Plot;
width?: number; width?: number;
height: number; height: number;
} & DistributionPlottingSettings; } & DistributionPlottingSettings;
export function defaultPlot(distribution: Distribution): Plot {
return { distributions: [{ name: "default", distribution }] };
}
export function makePlot(record: {
[key: string]: squiggleExpression;
}): Plot | void {
const plotResult = parsePlot(record);
if (plotResult.tag === "Ok") {
return plotResult.value;
}
}
export const DistributionChart: React.FC<DistributionChartProps> = (props) => { export const DistributionChart: React.FC<DistributionChartProps> = (props) => {
const { const { plot, height, showSummary, width, logX, actions = false } = props;
distribution,
height,
showSummary,
width,
logX,
actions = false,
} = props;
const shape = distribution.pointSet();
const [sized] = useSize((size) => { const [sized] = useSize((size) => {
if (shape.tag === "Error") { let shapes = flattenResult(
plot.distributions.map((x) =>
resultMap(x.distribution.pointSet(), (shape) => ({
name: x.name,
// color: x.color, // not supported yet
continuous: shape.continuous,
discrete: shape.discrete,
}))
)
);
if (shapes.tag === "Error") {
return ( return (
<ErrorAlert heading="Distribution Error"> <ErrorAlert heading="Distribution Error">
{distributionErrorToString(shape.value)} {distributionErrorToString(shapes.value)}
</ErrorAlert> </ErrorAlert>
); );
} }
@ -56,24 +75,29 @@ export const DistributionChart: React.FC<DistributionChartProps> = (props) => {
); );
widthProp = 20; widthProp = 20;
} }
const domain = shapes.value.flatMap((shape) =>
shape.discrete.concat(shape.continuous)
);
return ( return (
<div style={{ width: widthProp }}> <div style={{ width: widthProp }}>
{logX && hasMassBelowZero(shape.value) ? ( {logX && shapes.value.some(hasMassBelowZero) ? (
<ErrorAlert heading="Log Domain Error"> <ErrorAlert heading="Log Domain Error">
Cannot graph distribution with negative values on logarithmic scale. Cannot graph distribution with negative values on logarithmic scale.
</ErrorAlert> </ErrorAlert>
) : ( ) : (
<Vega <Vega
spec={spec} spec={spec}
data={{ con: shape.value.continuous, dis: shape.value.discrete }} data={{ data: shapes.value, domain }}
width={widthProp - 10} width={widthProp - 10}
height={height} height={height}
actions={actions} actions={actions}
/> />
)} )}
<div className="flex justify-center"> <div className="flex justify-center">
{showSummary && <SummaryTable distribution={distribution} />} {showSummary && plot.distributions.length === 1 && (
<SummaryTable distribution={plot.distributions[0].distribution} />
)}
</div> </div>
</div> </div>
); );

View File

@ -16,6 +16,7 @@ import * as percentilesSpec from "../vega-specs/spec-percentiles.json";
import { import {
DistributionChart, DistributionChart,
DistributionPlottingSettings, DistributionPlottingSettings,
defaultPlot,
} from "./DistributionChart"; } from "./DistributionChart";
import { NumberShower } from "./NumberShower"; import { NumberShower } from "./NumberShower";
import { ErrorAlert } from "./Alert"; import { ErrorAlert } from "./Alert";
@ -179,7 +180,7 @@ export const FunctionChart1Dist: React.FC<FunctionChart1DistProps> = ({
let showChart = let showChart =
mouseItem.tag === "Ok" && mouseItem.value.tag === "distribution" ? ( mouseItem.tag === "Ok" && mouseItem.value.tag === "distribution" ? (
<DistributionChart <DistributionChart
distribution={mouseItem.value.value} plot={defaultPlot(mouseItem.value.value)}
width={400} width={400}
height={50} height={50}
{...distributionPlotSettings} {...distributionPlotSettings}

View File

@ -37,10 +37,7 @@ import { InputItem } from "./ui/InputItem";
import { Text } from "./ui/Text"; import { Text } from "./ui/Text";
import { ViewSettings, viewSettingsSchema } from "./ViewSettings"; import { ViewSettings, viewSettingsSchema } from "./ViewSettings";
import { HeadedSection } from "./ui/HeadedSection"; import { HeadedSection } from "./ui/HeadedSection";
import { import { defaultTickFormat } from "../lib/distributionSpecBuilder";
defaultColor,
defaultTickFormat,
} from "../lib/distributionSpecBuilder";
import { Button } from "./ui/Button"; import { Button } from "./ui/Button";
type PlaygroundProps = SquiggleChartProps & { type PlaygroundProps = SquiggleChartProps & {
@ -234,13 +231,12 @@ export const PlaygroundContext = React.createContext<PlaygroundContextShape>({
export const SquigglePlayground: FC<PlaygroundProps> = ({ export const SquigglePlayground: FC<PlaygroundProps> = ({
defaultCode = "", defaultCode = "",
height = 500, height = 500,
showSummary = false, showSummary = true,
logX = false, logX = false,
expY = false, expY = false,
title, title,
minX, minX,
maxX, maxX,
color = defaultColor,
tickFormat = defaultTickFormat, tickFormat = defaultTickFormat,
distributionChartActions, distributionChartActions,
code: controlledCode, code: controlledCode,
@ -268,7 +264,6 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
title, title,
minX, minX,
maxX, maxX,
color,
tickFormat, tickFormat,
distributionChartActions, distributionChartActions,
showSummary, showSummary,

View File

@ -1,7 +1,7 @@
import React from "react"; import React from "react";
import { squiggleExpression, declaration } from "@quri/squiggle-lang"; import { squiggleExpression, declaration } from "@quri/squiggle-lang";
import { NumberShower } from "../NumberShower"; import { NumberShower } from "../NumberShower";
import { DistributionChart } from "../DistributionChart"; import { DistributionChart, defaultPlot, makePlot } from "../DistributionChart";
import { FunctionChart, FunctionChartSettings } from "../FunctionChart"; import { FunctionChart, FunctionChartSettings } from "../FunctionChart";
import clsx from "clsx"; import clsx from "clsx";
import { VariableBox } from "./VariableBox"; import { VariableBox } from "./VariableBox";
@ -102,7 +102,7 @@ export const ExpressionViewer: React.FC<Props> = ({
{(settings) => { {(settings) => {
return ( return (
<DistributionChart <DistributionChart
distribution={expression.value} plot={defaultPlot(expression.value)}
{...settings.distributionPlotSettings} {...settings.distributionPlotSettings}
height={settings.height} height={settings.height}
width={width} width={width}
@ -241,9 +241,9 @@ export const ExpressionViewer: React.FC<Props> = ({
case "module": { case "module": {
return ( return (
<VariableList path={path} heading="Module"> <VariableList path={path} heading="Module">
{(settings) => {(_) =>
Object.entries(expression.value) Object.entries(expression.value)
.filter(([key, r]) => !key.match(/^(Math|System)\./)) .filter(([key, _]) => !key.match(/^(Math|System)\./))
.map(([key, r]) => ( .map(([key, r]) => (
<ExpressionViewer <ExpressionViewer
key={key} key={key}
@ -257,24 +257,61 @@ export const ExpressionViewer: React.FC<Props> = ({
); );
} }
case "record": case "record":
return ( const plot = makePlot(expression.value);
<VariableList path={path} heading="Record"> if (plot) {
{(settings) => return (
Object.entries(expression.value).map(([key, r]) => ( <VariableBox
<ExpressionViewer path={path}
key={key} heading={"Plot"}
path={[...path, key]} renderSettingsMenu={({ onChange }) => {
expression={r} let disableLogX = plot.distributions.some((x) => {
width={width !== undefined ? width - 20 : width} let pointSet = x.distribution.pointSet();
/> return (
)) pointSet.tag === "Ok" && hasMassBelowZero(pointSet.value)
} );
</VariableList> });
); return (
<ItemSettingsMenu
path={path}
onChange={onChange}
disableLogX={disableLogX}
withFunctionSettings={false}
/>
);
}}
>
{(settings) => {
return (
<DistributionChart
plot={plot}
{...settings.distributionPlotSettings}
height={settings.height}
width={width}
/>
);
}}
</VariableBox>
);
} else {
return (
<VariableList path={path} heading="Record">
{(_) =>
Object.entries(expression.value).map(([key, r]) => (
<ExpressionViewer
key={key}
path={[...path, key]}
expression={r}
width={width !== undefined ? width - 20 : width}
/>
))
}
</VariableList>
);
}
case "array": case "array":
return ( return (
<VariableList path={path} heading="Array"> <VariableList path={path} heading="Array">
{(settings) => {(_) =>
expression.value.map((r, i) => ( expression.value.map((r, i) => (
<ExpressionViewer <ExpressionViewer
key={i} key={i}

View File

@ -6,10 +6,7 @@ import { Modal } from "../ui/Modal";
import { ViewSettings, viewSettingsSchema } from "../ViewSettings"; import { ViewSettings, viewSettingsSchema } from "../ViewSettings";
import { Path, pathAsString } from "./utils"; import { Path, pathAsString } from "./utils";
import { ViewerContext } from "./ViewerContext"; import { ViewerContext } from "./ViewerContext";
import { import { defaultTickFormat } from "../../lib/distributionSpecBuilder";
defaultColor,
defaultTickFormat,
} from "../../lib/distributionSpecBuilder";
import { PlaygroundContext } from "../SquigglePlayground"; import { PlaygroundContext } from "../SquigglePlayground";
type Props = { type Props = {
@ -46,7 +43,6 @@ const ItemSettingsModal: React.FC<
tickFormat: tickFormat:
mergedSettings.distributionPlotSettings.format || defaultTickFormat, mergedSettings.distributionPlotSettings.format || defaultTickFormat,
title: mergedSettings.distributionPlotSettings.title, title: mergedSettings.distributionPlotSettings.title,
color: mergedSettings.distributionPlotSettings.color || defaultColor,
minX: mergedSettings.distributionPlotSettings.minX, minX: mergedSettings.distributionPlotSettings.minX,
maxX: mergedSettings.distributionPlotSettings.maxX, maxX: mergedSettings.distributionPlotSettings.maxX,
distributionChartActions: mergedSettings.distributionPlotSettings.actions, distributionChartActions: mergedSettings.distributionPlotSettings.actions,
@ -66,7 +62,6 @@ const ItemSettingsModal: React.FC<
expY: vars.expY, expY: vars.expY,
format: vars.tickFormat, format: vars.tickFormat,
title: vars.title, title: vars.title,
color: vars.color,
minX: vars.minX, minX: vars.minX,
maxX: vars.maxX, maxX: vars.maxX,
actions: vars.distributionChartActions, actions: vars.distributionChartActions,

View File

@ -5,10 +5,7 @@ import { InputItem } from "./ui/InputItem";
import { Checkbox } from "./ui/Checkbox"; import { Checkbox } from "./ui/Checkbox";
import { HeadedSection } from "./ui/HeadedSection"; import { HeadedSection } from "./ui/HeadedSection";
import { Text } from "./ui/Text"; import { Text } from "./ui/Text";
import { import { defaultTickFormat } from "../lib/distributionSpecBuilder";
defaultColor,
defaultTickFormat,
} from "../lib/distributionSpecBuilder";
export const viewSettingsSchema = yup.object({}).shape({ export const viewSettingsSchema = yup.object({}).shape({
chartHeight: yup.number().required().positive().integer().default(350), chartHeight: yup.number().required().positive().integer().default(350),
@ -18,7 +15,6 @@ export const viewSettingsSchema = yup.object({}).shape({
expY: yup.boolean().required(), expY: yup.boolean().required(),
tickFormat: yup.string().default(defaultTickFormat), tickFormat: yup.string().default(defaultTickFormat),
title: yup.string(), title: yup.string(),
color: yup.string().default(defaultColor).required(),
minX: yup.number(), minX: yup.number(),
maxX: yup.number(), maxX: yup.number(),
distributionChartActions: yup.boolean(), distributionChartActions: yup.boolean(),
@ -114,12 +110,6 @@ export const ViewSettings: React.FC<{
register={register} register={register}
label="Tick Format" label="Tick Format"
/> />
<InputItem
name="color"
type="color"
register={register}
label="Color"
/>
</div> </div>
</HeadedSection> </HeadedSection>
</div> </div>

View File

@ -1,8 +1,8 @@
import clsx from "clsx"; import clsx from "clsx";
import React from "react"; import React from "react";
import { Path, UseFormRegister } from "react-hook-form"; import { Path, UseFormRegister, FieldValues } from "react-hook-form";
export function Checkbox<T>({ export function Checkbox<T extends FieldValues>({
name, name,
label, label,
register, register,

View File

@ -1,7 +1,7 @@
import React from "react"; import React from "react";
import { Path, UseFormRegister } from "react-hook-form"; import { Path, UseFormRegister, FieldValues } from "react-hook-form";
export function InputItem<T>({ export function InputItem<T extends FieldValues>({
name, name,
label, label,
type, type,

View File

@ -10,8 +10,6 @@ export type DistributionChartSpecOptions = {
minX?: number; minX?: number;
/** The maximum x coordinate shown on the chart */ /** The maximum x coordinate shown on the chart */
maxX?: number; maxX?: number;
/** The color of the chart */
color?: string;
/** The title of the chart */ /** The title of the chart */
title?: string; title?: string;
/** The formatting of the ticks */ /** The formatting of the ticks */
@ -25,36 +23,14 @@ export let linearXScale: LinearScale = {
range: "width", range: "width",
zero: false, zero: false,
nice: false, nice: false,
domain: { domain: { data: "domain", field: "x" },
fields: [
{
data: "con",
field: "x",
},
{
data: "dis",
field: "x",
},
],
},
}; };
export let linearYScale: LinearScale = { export let linearYScale: LinearScale = {
name: "yscale", name: "yscale",
type: "linear", type: "linear",
range: "height", range: "height",
zero: true, zero: true,
domain: { domain: { data: "domain", field: "y" },
fields: [
{
data: "con",
field: "y",
},
{
data: "dis",
field: "y",
},
],
},
}; };
export let logXScale: LogScale = { export let logXScale: LogScale = {
@ -65,18 +41,7 @@ export let logXScale: LogScale = {
base: 10, base: 10,
nice: false, nice: false,
clamp: true, clamp: true,
domain: { domain: { data: "domain", field: "x" },
fields: [
{
data: "con",
field: "x",
},
{
data: "dis",
field: "x",
},
],
},
}; };
export let expYScale: PowScale = { export let expYScale: PowScale = {
@ -86,29 +51,16 @@ export let expYScale: PowScale = {
range: "height", range: "height",
zero: true, zero: true,
nice: false, nice: false,
domain: { domain: { data: "domain", field: "y" },
fields: [
{
data: "con",
field: "y",
},
{
data: "dis",
field: "y",
},
],
},
}; };
export const defaultTickFormat = ".9~s"; export const defaultTickFormat = ".9~s";
export const defaultColor = "#739ECC";
export function buildVegaSpec( export function buildVegaSpec(
specOptions: DistributionChartSpecOptions specOptions: DistributionChartSpecOptions
): VisualizationSpec { ): VisualizationSpec {
let { const {
format = defaultTickFormat, format = defaultTickFormat,
color = defaultColor,
title, title,
minX, minX,
maxX, maxX,
@ -127,20 +79,32 @@ export function buildVegaSpec(
let spec: VisualizationSpec = { let spec: VisualizationSpec = {
$schema: "https://vega.github.io/schema/vega/v5.json", $schema: "https://vega.github.io/schema/vega/v5.json",
description: "A basic area chart example", description: "Squiggle plot chart",
width: 500, width: 500,
height: 100, height: 100,
padding: 5, padding: 5,
data: [ data: [
{ {
name: "con", name: "data",
}, },
{ {
name: "dis", name: "domain",
}, },
], ],
signals: [], signals: [],
scales: [xScale, expY ? expYScale : linearYScale], scales: [
xScale,
expY ? expYScale : linearYScale,
{
name: "color",
type: "ordinal",
domain: {
data: "data",
field: "name",
},
range: { scheme: "blues" },
},
],
axes: [ axes: [
{ {
orient: "bottom", orient: "bottom",
@ -152,108 +116,178 @@ export function buildVegaSpec(
domainOpacity: 0.0, domainOpacity: 0.0,
format: format, format: format,
tickCount: 10, tickCount: 10,
labelOverlap: "greedy",
}, },
], ],
marks: [ marks: [
{ {
type: "area", name: "all_distributions",
type: "group",
from: { from: {
data: "con", facet: {
}, name: "distribution_facet",
encode: { data: "data",
update: { groupby: ["name"],
interpolate: { value: "linear" },
x: {
scale: "xscale",
field: "x",
},
y: {
scale: "yscale",
field: "y",
},
y2: {
scale: "yscale",
value: 0,
},
fill: {
value: color,
},
fillOpacity: {
value: 1,
},
}, },
}, },
marks: [
{
name: "continuous_distribution",
type: "group",
from: {
facet: {
name: "continuous_facet",
data: "distribution_facet",
field: "continuous",
},
},
encode: {
update: {},
},
marks: [
{
name: "continuous_area",
type: "area",
from: {
data: "continuous_facet",
},
encode: {
update: {
interpolate: { value: "linear" },
x: {
scale: "xscale",
field: "x",
},
y: {
scale: "yscale",
field: "y",
},
fill: {
scale: "color",
field: { parent: "name" },
},
y2: {
scale: "yscale",
value: 0,
},
fillOpacity: {
value: 1,
},
},
},
},
],
},
{
name: "discrete_distribution",
type: "group",
from: {
facet: {
name: "discrete_facet",
data: "distribution_facet",
field: "discrete",
},
},
marks: [
{
type: "rect",
from: {
data: "discrete_facet",
},
encode: {
enter: {
width: {
value: 1,
},
},
update: {
x: {
scale: "xscale",
field: "x",
},
y: {
scale: "yscale",
field: "y",
},
y2: {
scale: "yscale",
value: 0,
},
fill: {
scale: "color",
field: { parent: "name" },
},
},
},
},
{
type: "symbol",
from: {
data: "discrete_facet",
},
encode: {
enter: {
shape: {
value: "circle",
},
size: [{ value: 100 }],
tooltip: {
signal: "{ probability: datum.y, value: datum.x }",
},
},
update: {
x: {
scale: "xscale",
field: "x",
},
y: {
scale: "yscale",
field: "y",
},
fill: {
scale: "color",
field: { parent: "name" },
},
},
},
},
],
},
],
}, },
],
legends: [
{ {
type: "rect", fill: "color",
from: { orient: "top",
data: "dis", labelFontSize: 12,
},
encode: { encode: {
enter: { symbols: {
width: { update: {
value: 1, fill: [
{ test: "length(domain('color')) == 1", value: "transparent" },
{ scale: "color", field: "value" },
],
}, },
}, },
update: { labels: {
x: { interactive: true,
scale: "xscale", update: {
field: "x", fill: [
}, { test: "length(domain('color')) == 1", value: "transparent" },
y: { { value: "black" },
scale: "yscale", ],
field: "y",
},
y2: {
scale: "yscale",
value: 0,
},
fill: {
value: "#2f65a7",
},
},
},
},
{
type: "symbol",
from: {
data: "dis",
},
encode: {
enter: {
shape: {
value: "circle",
},
size: [{ value: 100 }],
tooltip: {
signal: "{ probability: datum.y, value: datum.x }",
},
},
update: {
x: {
scale: "xscale",
field: "x",
},
y: {
scale: "yscale",
field: "y",
},
fill: {
value: "#1e4577",
}, },
}, },
}, },
}, },
], ],
}; ...(title && {
if (title) {
spec = {
...spec,
title: { title: {
text: title, text: title,
}, },
}; }),
} };
return spec; return spec;
} }

View File

@ -0,0 +1,72 @@
import * as yup from "yup";
import { Distribution, result, squiggleExpression } from "@quri/squiggle-lang";
export type LabeledDistribution = {
name: string;
distribution: Distribution;
color?: string;
};
export type Plot = {
distributions: LabeledDistribution[];
};
function error<a, b>(err: b): result<a, b> {
return { tag: "Error", value: err };
}
function ok<a, b>(x: a): result<a, b> {
return { tag: "Ok", value: x };
}
const schema = yup
.object()
.strict()
.noUnknown()
.shape({
distributions: yup.object().shape({
tag: yup.mixed().oneOf(["array"]),
value: yup
.array()
.of(
yup.object().shape({
tag: yup.mixed().oneOf(["record"]),
value: yup.object({
name: yup.object().shape({
tag: yup.mixed().oneOf(["string"]),
value: yup.string().required(),
}),
// color: yup
// .object({
// tag: yup.mixed().oneOf(["string"]),
// value: yup.string().required(),
// })
// .default(undefined),
distribution: yup.object({
tag: yup.mixed().oneOf(["distribution"]),
value: yup.mixed(),
}),
}),
})
)
.required(),
}),
});
export function parsePlot(record: {
[key: string]: squiggleExpression;
}): result<Plot, string> {
try {
const plotRecord = schema.validateSync(record);
return ok({
distributions: plotRecord.distributions.value.map((x) => ({
name: x.value.name.value,
// color: x.value.color?.value, // not supported yet
distribution: x.value.distribution.value,
})),
});
} catch (e) {
const message = e instanceof Error ? e.message : "Unknown error";
return error(message);
}
}

View File

@ -0,0 +1,37 @@
import { result } from "@quri/squiggle-lang";
export function flattenResult<a, b>(x: result<a, b>[]): result<a[], b> {
if (x.length === 0) {
return { tag: "Ok", value: [] };
} else {
if (x[0].tag === "Error") {
return x[0];
} else {
let rest = flattenResult(x.splice(1));
if (rest.tag === "Error") {
return rest;
} else {
return { tag: "Ok", value: [x[0].value].concat(rest.value) };
}
}
}
}
export function resultBind<a, b, c>(
x: result<a, b>,
fn: (y: a) => result<c, b>
): result<c, b> {
if (x.tag === "Ok") {
return fn(x.value);
} else {
return x;
}
}
export function all(arr: boolean[]): boolean {
return arr.reduce((x, y) => x && y, true);
}
export function some(arr: boolean[]): boolean {
return arr.reduce((x, y) => x || y, false);
}

View File

@ -93,6 +93,33 @@ could be continuous, discrete or mixed.
</Story> </Story>
</Canvas> </Canvas>
## Multiple plots
<Canvas>
<Story
name="Multiple plots"
args={{
code: `
{
distributions: [
{
name: "one",
distribution: mx(0.5, normal(0,1))
},
{
name: "two",
distribution: mx(2, normal(5, 2)),
}
]
}
`,
width,
}}
>
{Template.bind({})}
</Story>
</Canvas>
## Constants ## Constants
A constant is a simple number as a result. This has special formatting rules A constant is a simple number as a result. This has special formatting rules

View File

@ -0,0 +1,21 @@
open Jest
open TestHelpers
describe("E.A.getByFmap", () => {
makeTest("Empty list returns None", E.A.getByFmap([], x => x + 1, x => mod(x, 2) == 0), None)
makeTest(
"Never predicate returns None",
E.A.getByFmap([1, 2, 3, 4, 5, 6], x => x + 1, _ => false),
None,
)
makeTest(
"function evaluates",
E.A.getByFmap([1, 1, 1, 1, 1, 1, 1, 2, 1, 1], x => 3 * x, x => x > 4),
Some(6),
)
makeTest(
"always predicate returns fn(fst(a))",
E.A.getByFmap([0, 1, 2, 3, 4, 5, 6], x => 10 + x, _ => true),
Some(10),
)
})

View File

@ -1,4 +0,0 @@
open Jest
open Expect
test("todo", () => expect("1")->toBe("1"))

View File

@ -68,3 +68,5 @@ myTypeCheckTest(test, "number | string", "1", "Ok")
myTypeCheckTest(test, "date | string", "1", "Expected type: (date | string) but got: 1") myTypeCheckTest(test, "date | string", "1", "Expected type: (date | string) but got: 1")
myTypeCheckTest(test, "number<-min(10)", "10", "Ok") myTypeCheckTest(test, "number<-min(10)", "10", "Ok")
myTypeCheckTest(test, "number<-min(10)", "0", "Expected type: number<-min(10) but got: 0") myTypeCheckTest(test, "number<-min(10)", "0", "Expected type: number<-min(10) but got: 0")
myTypeCheckTest(test, "any", "0", "Ok")
myTypeCheckTest(test, "any", "'a'", "Ok")

View File

@ -0,0 +1,123 @@
open Jest
open Expect
module DispatchT = Reducer_Dispatch_T
module Expression = Reducer_Expression
module ExpressionT = Reducer_Expression_T
module TypeCompile = Reducer_Type_Compile
module TypeChecker = Reducer_Type_TypeChecker
open ReducerInterface_InternalExpressionValue
type errorValue = Reducer_ErrorValue.errorValue
// Let's build a function to replace switch statements
// In dispatchChainPiece, we execute an return the result of execution if there is a type match.
// Otherwise we return None so that the call chain can continue.
// So we want to build a function like
// dispatchChainPiece = (call: functionCall, environment): option<result<internalExpressionValue, errorValue>>
// Now lets make the dispatchChainPiece itself.
// Note that I am not passing the reducer to the dispatchChainPiece as an argument because it is in the context anyway.
// Keep in mind that reducerFn is necessary for map/reduce so dispatchChainPiece should have a reducerFn in context.
let makeMyDispatchChainPiece = (reducer: ExpressionT.reducerFn): DispatchT.dispatchChainPiece => {
// Let's have a pure implementations
module Implementation = {
let stringConcat = (a: string, b: string): string => Js.String2.concat(a, b)
let arrayConcat = (
a: Js.Array2.t<internalExpressionValue>,
b: Js.Array2.t<internalExpressionValue>,
): Js.Array2.t<internalExpressionValue> => Js.Array2.concat(a, b)
let plot = _r => "yey, plotted"
}
let extractStringString = args =>
switch args {
| [IEvString(a), IEvString(b)] => (a, b)
| _ => raise(Reducer_Exception.ImpossibleException("extractStringString developer error"))
}
let extractArrayArray = args =>
switch args {
| [IEvArray(a), IEvArray(b)] => (a, b)
| _ => raise(Reducer_Exception.ImpossibleException("extractArrayArray developer error"))
}
// Let's bridge the pure implementation to expression values
module Bridge = {
let stringConcat: DispatchT.genericIEvFunction = (args, _environment) => {
let (a, b) = extractStringString(args)
Implementation.stringConcat(a, b)->IEvString->Ok
}
let arrayConcat: DispatchT.genericIEvFunction = (args, _environment) => {
let (a, b) = extractArrayArray(args)
Implementation.arrayConcat(a, b)->IEvArray->Ok
}
let plot: DispatchT.genericIEvFunction = (args, _environment) => {
switch args {
// Just assume that we are doing the business of extracting and converting the deep record
| [IEvRecord(_)] => Implementation.plot({"title": "This is a plot"})->IEvString->Ok
| _ => raise(Reducer_Exception.ImpossibleException("plot developer error"))
}
}
}
// concat functions are to illustrate polymoprhism. And the plot function is to illustrate complex types
let jumpTable = [
(
"concat",
TypeCompile.fromTypeExpressionExn("string=>string=>string", reducer),
Bridge.stringConcat,
),
(
"concat",
TypeCompile.fromTypeExpressionExn("[any]=>[any]=>[any]", reducer),
Bridge.arrayConcat,
),
(
"plot",
TypeCompile.fromTypeExpressionExn(
// Nested complex types are available
// records {property: type}
// arrays [type]
// tuples [type, type]
// <- type contracts are available naturally and they become part of dispatching
// Here we are not enumerating the possibilities because type checking has a dedicated test
"{title: string, line: {width: number, color: string}}=>string",
reducer,
),
Bridge.plot,
),
]
//Here we are creating a dispatchChainPiece function that will do the actual dispatch from the jumpTable
Reducer_Dispatch_ChainPiece.makeFromTypes(jumpTable)
}
// And finally, let's write a library dispatch for our external library
// Exactly the same as the one used in real life
let _dispatch = (
call: functionCall,
environment,
reducer: Reducer_Expression_T.reducerFn,
chain,
): result<internalExpressionValue, 'e> => {
let dispatchChainPiece = makeMyDispatchChainPiece(reducer)
dispatchChainPiece(call, environment)->E.O2.defaultFn(() => chain(call, environment, reducer))
}
// What is important about this implementation?
// A) Exactly the same function jump table can be used to create type guarded lambda functions
// Guarded lambda functions will be the basis of the next version of Squiggle
// B) Complicated recursive record types are not a problem.
describe("Type Dispatch", () => {
let reducerFn = Expression.reduceExpression
let dispatchChainPiece = makeMyDispatchChainPiece(reducerFn)
test("stringConcat", () => {
let call: functionCall = ("concat", [IEvString("hello"), IEvString("world")])
let result = dispatchChainPiece(call, defaultEnvironment)
expect(result)->toEqual(Some(Ok(IEvString("helloworld"))))
})
})

View File

@ -74,6 +74,7 @@ describe("eval on distribution functions", () => {
testEval("truncateLeft(normal(5,2), 3)", "Ok(Point Set Distribution)") testEval("truncateLeft(normal(5,2), 3)", "Ok(Point Set Distribution)")
testEval("truncateRight(normal(5,2), 3)", "Ok(Point Set Distribution)") testEval("truncateRight(normal(5,2), 3)", "Ok(Point Set Distribution)")
testEval("truncate(normal(5,2), 3, 8)", "Ok(Point Set Distribution)") testEval("truncate(normal(5,2), 3, 8)", "Ok(Point Set Distribution)")
testEval("truncate(normal(5,2) |> SampleSet.fromDist, 3, 8)", "Ok(Sample Set Distribution)")
testEval("isNormalized(truncate(normal(5,2), 3, 8))", "Ok(true)") testEval("isNormalized(truncate(normal(5,2), 3, 8))", "Ok(true)")
}) })

View File

@ -63,6 +63,9 @@ describe("FunctionRegistry Library", () => {
testEvalToBe("SampleSet.fromList([3,5,2,3,5,2,3,5,2,3,3,5])", "Ok(Sample Set Distribution)") testEvalToBe("SampleSet.fromList([3,5,2,3,5,2,3,5,2,3,3,5])", "Ok(Sample Set Distribution)")
testEvalToBe("SampleSet.fromList([3,5,2,3,5,2,3,5,2,3,3,5])", "Ok(Sample Set Distribution)") testEvalToBe("SampleSet.fromList([3,5,2,3,5,2,3,5,2,3,3,5])", "Ok(Sample Set Distribution)")
testEvalToBe("SampleSet.fromFn({|| sample(normal(5,2))})", "Ok(Sample Set Distribution)") testEvalToBe("SampleSet.fromFn({|| sample(normal(5,2))})", "Ok(Sample Set Distribution)")
testEvalToBe("SampleSet.min(SampleSet.fromDist(normal(50,2)), 2)", "Ok(Sample Set Distribution)")
testEvalToBe("mean(SampleSet.min(SampleSet.fromDist(normal(50,2)), 2))", "Ok(2)")
testEvalToBe("SampleSet.max(SampleSet.fromDist(normal(50,2)), 10)", "Ok(Sample Set Distribution)")
testEvalToBe( testEvalToBe(
"addOne(t)=t+1; SampleSet.toList(SampleSet.map(SampleSet.fromList([1,2,3,4,5,6]), addOne))", "addOne(t)=t+1; SampleSet.toList(SampleSet.map(SampleSet.fromList([1,2,3,4,5,6]), addOne))",
"Ok([2,3,4,5,6,7])", "Ok([2,3,4,5,6,7])",

View File

@ -0,0 +1,20 @@
open Jest
open Expect
let makeTest = (~only=false, str, item1, item2) =>
only
? Only.test(str, () => expect(item1)->toEqual(item2))
: test(str, () => expect(item1)->toEqual(item2))
describe("Stdlib", () => {
makeTest(
"Length of Random.sample",
Stdlib.Random.sample([1.0, 2.0], {probs: [0.5, 0.5], size: 10})->E.A.length,
10,
)
makeTest(
"Random.sample returns elements from input array (will fail with very slim probability)",
Stdlib.Random.sample([1.0, 2.0], {probs: [0.5, 0.5], size: 10})->E.A.uniq->E.A.Floats.sort,
[1.0, 2.0],
)
})

View File

@ -1,6 +1,6 @@
{ {
"name": "@quri/squiggle-lang", "name": "@quri/squiggle-lang",
"version": "0.2.11", "version": "0.3.1",
"homepage": "https://squiggle-language.com", "homepage": "https://squiggle-language.com",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
@ -18,6 +18,7 @@
"benchmark": "ts-node benchmark/conversion_tests.ts", "benchmark": "ts-node benchmark/conversion_tests.ts",
"test": "jest", "test": "jest",
"test:ts": "jest __tests__/TS/", "test:ts": "jest __tests__/TS/",
"test:stdlib": "jest __tests__/Stdlib_test.bs.js",
"test:rescript": "jest --modulePathIgnorePatterns=__tests__/TS/*", "test:rescript": "jest --modulePathIgnorePatterns=__tests__/TS/*",
"test:watch": "jest --watchAll", "test:watch": "jest --watchAll",
"test:fnRegistry": "jest __tests__/SquiggleLibrary/SquiggleLibrary_FunctionRegistryLibrary_test.bs.js", "test:fnRegistry": "jest __tests__/SquiggleLibrary/SquiggleLibrary_FunctionRegistryLibrary_test.bs.js",
@ -44,18 +45,18 @@
"@stdlib/stats": "^0.0.13", "@stdlib/stats": "^0.0.13",
"jstat": "^1.9.5", "jstat": "^1.9.5",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"mathjs": "^11.0.1", "mathjs": "^11.1.0",
"pdfast": "^0.2.0" "pdfast": "^0.2.0"
}, },
"devDependencies": { "devDependencies": {
"@glennsl/rescript-jest": "^0.9.0", "@glennsl/rescript-jest": "^0.9.2",
"@istanbuljs/nyc-config-typescript": "^1.0.2", "@istanbuljs/nyc-config-typescript": "^1.0.2",
"@types/jest": "^27.5.0", "@types/jest": "^27.5.0",
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.2", "babel-plugin-transform-es2015-modules-commonjs": "^6.26.2",
"bisect_ppx": "^2.7.1", "bisect_ppx": "^2.7.1",
"chalk": "^5.0.1", "chalk": "^5.0.1",
"codecov": "^3.8.3", "codecov": "^3.8.3",
"fast-check": "^3.1.1", "fast-check": "^3.1.2",
"gentype": "^4.5.0", "gentype": "^4.5.0",
"jest": "^27.5.1", "jest": "^27.5.1",
"moduleserve": "^0.9.1", "moduleserve": "^0.9.1",
@ -68,7 +69,7 @@
"ts-jest": "^27.1.4", "ts-jest": "^27.1.4",
"ts-loader": "^9.3.0", "ts-loader": "^9.3.0",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"typescript": "^4.7.4", "typescript": "^4.8.2",
"webpack": "^5.74.0", "webpack": "^5.74.0",
"webpack-cli": "^4.10.0" "webpack-cli": "^4.10.0"
}, },

View File

@ -40,8 +40,8 @@ export type { result, shape, environment, lambdaValue, squiggleExpression };
export { parse } from "./parse"; export { parse } from "./parse";
export let defaultSamplingInputs: environment = { export let defaultSamplingInputs: environment = {
sampleCount: 10000, sampleCount: 1000,
xyPointLength: 10000, xyPointLength: 1000,
}; };
export function run( export function run(

View File

@ -216,7 +216,7 @@ let rec run = (~env: env, functionCallInfo: functionCallInfo): outputType => {
| FromFloat(subFnName, x) => reCall(~functionCallInfo=FromFloat(subFnName, x), ()) | FromFloat(subFnName, x) => reCall(~functionCallInfo=FromFloat(subFnName, x), ())
| Mixture(dists) => | Mixture(dists) =>
dists dists
->GenericDist.mixture(~scaleMultiplyFn=scaleMultiply, ~pointwiseAddFn=pointwiseAdd) ->GenericDist.mixture(~scaleMultiplyFn=scaleMultiply, ~pointwiseAddFn=pointwiseAdd, ~env)
->E.R2.fmap(r => Dist(r)) ->E.R2.fmap(r => Dist(r))
->OutputLocal.fromResult ->OutputLocal.fromResult
| FromSamples(xs) => | FromSamples(xs) =>

View File

@ -242,11 +242,19 @@ module Truncate = {
switch trySymbolicSimplification(leftCutoff, rightCutoff, t) { switch trySymbolicSimplification(leftCutoff, rightCutoff, t) {
| Some(r) => Ok(r) | Some(r) => Ok(r)
| None => | None =>
toPointSetFn(t)->E.R2.fmap(t => { switch t {
DistributionTypes.PointSet( | SampleSet(t) =>
PointSetDist.T.truncate(leftCutoff, rightCutoff, t)->PointSetDist.T.normalize, switch SampleSetDist.truncate(t, ~leftCutoff, ~rightCutoff) {
) | Ok(r) => Ok(SampleSet(r))
}) | Error(err) => Error(DistributionTypes.SampleSetError(err))
}
| _ =>
toPointSetFn(t)->E.R2.fmap(t => {
DistributionTypes.PointSet(
PointSetDist.T.truncate(leftCutoff, rightCutoff, t)->PointSetDist.T.normalize,
)
})
}
} }
} }
} }
@ -491,15 +499,30 @@ let pointwiseCombinationFloat = (
m->E.R2.fmap(r => DistributionTypes.PointSet(r)) m->E.R2.fmap(r => DistributionTypes.PointSet(r))
} }
//Note: The result should always cumulatively sum to 1. This would be good to test. //TODO: The result should always cumulatively sum to 1. This would be good to test.
//Note: If the inputs are not normalized, this will return poor results. The weights probably refer to the post-normalized forms. It would be good to apply a catch to this. //TODO: If the inputs are not normalized, this will return poor results. The weights probably refer to the post-normalized forms. It would be good to apply a catch to this.
let mixture = ( let mixture = (
values: array<(t, float)>, values: array<(t, float)>,
~scaleMultiplyFn: scaleMultiplyFn, ~scaleMultiplyFn: scaleMultiplyFn,
~pointwiseAddFn: pointwiseAddFn, ~pointwiseAddFn: pointwiseAddFn,
~env: env,
) => { ) => {
if E.A.length(values) == 0 { let allValuesAreSampleSet = v => E.A.all(((t, _)) => isSampleSetSet(t), v)
if E.A.isEmpty(values) {
Error(DistributionTypes.OtherError("Mixture error: mixture must have at least 1 element")) Error(DistributionTypes.OtherError("Mixture error: mixture must have at least 1 element"))
} else if allValuesAreSampleSet(values) {
let withSampleSetValues = values->E.A2.fmap(((value, weight)) =>
switch value {
| SampleSet(sampleSet) => Ok((sampleSet, weight))
| _ => Error("Unreachable")
}->E.R2.toExn("Mixture coding error: SampleSet expected. This should be inaccessible.")
)
let sampleSetMixture = SampleSetDist.mixture(withSampleSetValues, env.sampleCount)
switch sampleSetMixture {
| Ok(sampleSet) => Ok(DistributionTypes.SampleSet(sampleSet))
| Error(err) => Error(DistributionTypes.Error.sampleErrorToDistErr(err))
}
} else { } else {
let totalWeight = values->E.A2.fmap(E.Tuple2.second)->E.A.Floats.sum let totalWeight = values->E.A2.fmap(E.Tuple2.second)->E.A.Floats.sum
let properlyWeightedValues = let properlyWeightedValues =

View File

@ -81,6 +81,7 @@ let mixture: (
array<(t, float)>, array<(t, float)>,
~scaleMultiplyFn: scaleMultiplyFn, ~scaleMultiplyFn: scaleMultiplyFn,
~pointwiseAddFn: pointwiseAddFn, ~pointwiseAddFn: pointwiseAddFn,
~env: env,
) => result<t, error> ) => result<t, error>
let isSymbolic: t => bool let isSymbolic: t => bool

View File

@ -224,3 +224,8 @@ module T = Dist({
XYShape.Analysis.getVarianceDangerously(t, mean, getMeanOfSquares) XYShape.Analysis.getVarianceDangerously(t, mean, getMeanOfSquares)
} }
}) })
let sampleN = (t: t, n): array<float> => {
let normalized = t->T.normalize->getShape
Stdlib.Random.sample(normalized.xs, {probs: normalized.ys, size: n})
}

View File

@ -257,3 +257,7 @@ let toSparkline = (t: t, bucketCount): result<string, PointSetTypes.sparklineErr
->E.O2.fmap(Continuous.downsampleEquallyOverX(bucketCount)) ->E.O2.fmap(Continuous.downsampleEquallyOverX(bucketCount))
->E.O2.toResult(PointSetTypes.CannotSparklineDiscrete) ->E.O2.toResult(PointSetTypes.CannotSparklineDiscrete)
->E.R2.fmap(r => Continuous.getShape(r).ys->Sparklines.create()) ->E.R2.fmap(r => Continuous.getShape(r).ys->Sparklines.create())
let makeDiscrete = (d): t => Discrete(d)
let makeContinuous = (d): t => Continuous(d)
let makeMixed = (d): t => Mixed(d)

View File

@ -131,3 +131,37 @@ let max = t => T.get(t)->E.A.Floats.max
let stdev = t => T.get(t)->E.A.Floats.stdev let stdev = t => T.get(t)->E.A.Floats.stdev
let variance = t => T.get(t)->E.A.Floats.variance let variance = t => T.get(t)->E.A.Floats.variance
let percentile = (t, f) => T.get(t)->E.A.Floats.percentile(f) let percentile = (t, f) => T.get(t)->E.A.Floats.percentile(f)
let mixture = (values: array<(t, float)>, intendedLength: int) => {
let totalWeight = values->E.A2.fmap(E.Tuple2.second)->E.A.Floats.sum
let discreteSamples =
values
->Belt.Array.mapWithIndex((i, (_, weight)) => (E.I.toFloat(i), weight /. totalWeight))
->XYShape.T.fromZippedArray
->Discrete.make
->Discrete.sampleN(intendedLength)
let dists = values->E.A2.fmap(E.Tuple2.first)->E.A2.fmap(T.get)
let samples =
discreteSamples
->Belt.Array.mapWithIndex((index, distIndexToChoose) => {
let chosenDist = E.A.get(dists, E.Float.toInt(distIndexToChoose))
chosenDist->E.O.bind(E.A.get(_, index))
})
->E.A.O.openIfAllSome
samples->E.O2.toExn("Mixture unreachable error")->T.make
}
let truncateLeft = (t, f) => T.get(t)->E.A2.filter(x => x >= f)->T.make
let truncateRight = (t, f) => T.get(t)->E.A2.filter(x => x <= f)->T.make
let truncate = (t, ~leftCutoff: option<float>, ~rightCutoff: option<float>) => {
let withTruncatedLeft = t => leftCutoff |> E.O.dimap(left => truncateLeft(t, left), _ => Ok(t))
let withTruncatedRight = t => rightCutoff |> E.O.dimap(left => truncateRight(t, left), _ => Ok(t))
t->withTruncatedLeft |> E.R2.bind(withTruncatedRight)
}
let minOfTwo = (t1: t, t2: t) => map2(~fn=(a, b) => Ok(Js.Math.min_float(a, b)), ~t1, ~t2)
let maxOfTwo = (t1: t, t2: t) => map2(~fn=(a, b) => Ok(Js.Math.max_float(a, b)), ~t1, ~t2)
let minOfFloat = (t: t, f: float) => samplesMap(~fn=a => Ok(Js.Math.min_float(a, f)), t)
let maxOfFloat = (t: t, f: float) => samplesMap(~fn=a => Ok(Js.Math.max_float(a, f)), t)

View File

@ -23,6 +23,30 @@ let inputsTodist = (inputs: array<FunctionRegistry_Core.frValue>, makeDist) => {
expressionValue expressionValue
} }
module Internal = {
type t = PointSetDist.t
let toType = (r): result<
ReducerInterface_InternalExpressionValue.t,
Reducer_ErrorValue.errorValue,
> =>
switch r {
| Ok(r) => Ok(Wrappers.evDistribution(PointSet(r)))
| Error(err) => Error(REOperationError(err))
}
let doLambdaCall = (aLambdaValue, list, environment, reducer) =>
switch Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, list, environment, reducer) {
| Ok(IEvNumber(f)) => Ok(f)
| _ => Error(Operation.SampleMapNeedsNtoNFunction)
}
let mapY = (pointSetDist: t, aLambdaValue, env, reducer) => {
let fn = r => doLambdaCall(aLambdaValue, list{IEvNumber(r)}, env, reducer)
PointSetDist.T.mapYResult(~fn, pointSetDist)->toType
}
}
let library = [ let library = [
Function.make( Function.make(
~name="fromDist", ~name="fromDist",
@ -53,6 +77,27 @@ let library = [
], ],
(), (),
), ),
Function.make(
~name="mapY",
~nameSpace,
~requiresNamespace=true,
~examples=[`PointSet.mapY(mx(normal(5,2)), {|x| x + 1})`],
~output=ReducerInterface_InternalExpressionValue.EvtDistribution,
~definitions=[
FnDefinition.make(
~name="mapY",
~inputs=[FRTypeDist, FRTypeLambda],
~run=(inputs, _, env, reducer) =>
switch inputs {
| [IEvDistribution(PointSet(dist)), IEvLambda(lambda)] =>
Internal.mapY(dist, lambda, env, reducer)->E.R2.errMap(Reducer_ErrorValue.errorToString)
| _ => Error(impossibleError)
},
(),
),
],
(),
),
Function.make( Function.make(
~name="makeContinuous", ~name="makeContinuous",
~nameSpace, ~nameSpace,

View File

@ -75,7 +75,7 @@ module Internal = {
} }
} }
let library = [ let libaryBase = [
Function.make( Function.make(
~name="fromDist", ~name="fromDist",
~nameSpace, ~nameSpace,
@ -92,7 +92,7 @@ let library = [
GenericDist.toSampleSetDist(dist, env.sampleCount) GenericDist.toSampleSetDist(dist, env.sampleCount)
->E.R2.fmap(Wrappers.sampleSet) ->E.R2.fmap(Wrappers.sampleSet)
->E.R2.fmap(Wrappers.evDistribution) ->E.R2.fmap(Wrappers.evDistribution)
->E.R2.errMap(_ => "") ->E.R2.errMap(DistributionTypes.Error.toString)
| _ => Error(impossibleError) | _ => Error(impossibleError)
}, },
(), (),
@ -158,7 +158,7 @@ let library = [
| [IEvLambda(lambda)] => | [IEvLambda(lambda)] =>
switch Internal.fromFn(lambda, env, reducer) { switch Internal.fromFn(lambda, env, reducer) {
| Ok(r) => Ok(r->Wrappers.sampleSet->Wrappers.evDistribution) | Ok(r) => Ok(r->Wrappers.sampleSet->Wrappers.evDistribution)
| Error(_) => Error("issue") | Error(e) => Error(Operation.Error.toString(e))
} }
| _ => Error(impossibleError) | _ => Error(impossibleError)
}, },
@ -180,7 +180,7 @@ let library = [
~run=(inputs, _, env, reducer) => ~run=(inputs, _, env, reducer) =>
switch inputs { switch inputs {
| [IEvDistribution(SampleSet(dist)), IEvLambda(lambda)] => | [IEvDistribution(SampleSet(dist)), IEvLambda(lambda)] =>
Internal.map1(dist, lambda, env, reducer)->E.R2.errMap(_ => "") Internal.map1(dist, lambda, env, reducer)->E.R2.errMap(Reducer_ErrorValue.errorToString)
| _ => Error(impossibleError) | _ => Error(impossibleError)
}, },
(), (),
@ -207,7 +207,9 @@ let library = [
IEvDistribution(SampleSet(dist2)), IEvDistribution(SampleSet(dist2)),
IEvLambda(lambda), IEvLambda(lambda),
] => ] =>
Internal.map2(dist1, dist2, lambda, env, reducer)->E.R2.errMap(_ => "") Internal.map2(dist1, dist2, lambda, env, reducer)->E.R2.errMap(
Reducer_ErrorValue.errorToString,
)
| _ => Error(impossibleError) | _ => Error(impossibleError)
} }
}, },
@ -236,7 +238,9 @@ let library = [
IEvDistribution(SampleSet(dist3)), IEvDistribution(SampleSet(dist3)),
IEvLambda(lambda), IEvLambda(lambda),
] => ] =>
Internal.map3(dist1, dist2, dist3, lambda, env, reducer)->E.R2.errMap(_ => "") Internal.map3(dist1, dist2, dist3, lambda, env, reducer)->E.R2.errMap(
Reducer_ErrorValue.errorToString,
)
| _ => Error(impossibleError) | _ => Error(impossibleError)
}, },
(), (),
@ -259,9 +263,9 @@ let library = [
~run=(inputs, _, env, reducer) => ~run=(inputs, _, env, reducer) =>
switch inputs { switch inputs {
| [IEvArray(dists), IEvLambda(lambda)] => | [IEvArray(dists), IEvLambda(lambda)] =>
Internal.mapN(dists, lambda, env, reducer)->E.R2.errMap(_e => { Internal.mapN(dists, lambda, env, reducer)->E.R2.errMap(
"AHHH doesn't work" Reducer_ErrorValue.errorToString,
}) )
| _ => Error(impossibleError) | _ => Error(impossibleError)
}, },
(), (),
@ -270,3 +274,63 @@ let library = [
(), (),
), ),
] ]
module Comparison = {
let template = (name, inputs, run) => {
FnDefinition.make(
~name,
~inputs,
~run=(inputs, _, _, _) => {
run(inputs)
},
(),
)
}
let wrapper = r =>
r
->E.R2.fmap(r => r->Wrappers.sampleSet->Wrappers.evDistribution)
->E.R2.errMap(SampleSetDist.Error.toString)
let mkBig = (name, withDist, withFloat) =>
Function.make(
~name,
~nameSpace,
~requiresNamespace=false,
~examples=[
`SampleSet.${name}(SampleSet.fromDist(normal(5,2)), SampleSet.fromDist(normal(6,2)))`,
`SampleSet.${name}(SampleSet.fromDist(normal(5,2)), 3.0)`,
`SampleSet.${name}(4.0, SampleSet.fromDist(normal(6,2)))`,
],
~output=ReducerInterface_InternalExpressionValue.EvtDistribution,
~definitions=[
template(name, [FRTypeDist, FRTypeDist], inputs => {
switch inputs {
| [IEvDistribution(SampleSet(dist1)), IEvDistribution(SampleSet(dist2))] =>
withDist(dist1, dist2)->wrapper
| _ => Error(impossibleError)
}
}),
template(name, [FRTypeDist, FRTypeNumber], inputs => {
switch inputs {
| [IEvDistribution(SampleSet(dist)), IEvNumber(f)] => withFloat(dist, f)->wrapper
| _ => Error(impossibleError)
}
}),
template(name, [FRTypeNumber, FRTypeDist], inputs => {
switch inputs {
| [IEvNumber(f), IEvDistribution(SampleSet(dist))] => withFloat(dist, f)->wrapper
| _ => Error(impossibleError)
}
}),
],
(),
)
let library = [
mkBig("min", SampleSetDist.minOfTwo, SampleSetDist.minOfFloat),
mkBig("max", SampleSetDist.maxOfTwo, SampleSetDist.maxOfFloat),
]
}
let library = E.A.append(libaryBase, Comparison.library)

View File

@ -0,0 +1,19 @@
module TypeChecker = Reducer_Type_TypeChecker
module T = Reducer_Dispatch_T
open ReducerInterface_InternalExpressionValue
type errorValue = Reducer_ErrorValue.errorValue
let makeFromTypes = jumpTable => {
let dispatchChainPiece: T.dispatchChainPiece = ((fnName, fnArgs): functionCall, environment) => {
let jumpTableEntry = jumpTable->Js.Array2.find(elem => {
let (candidName, candidType, _) = elem
candidName == fnName && TypeChecker.checkITypeArgumentsBool(candidType, fnArgs)
})
switch jumpTableEntry {
| Some((_, _, bridgeFn)) => bridgeFn(fnArgs, environment)->Some
| _ => None
}
}
dispatchChainPiece
}

View File

@ -0,0 +1,20 @@
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module ExpressionT = Reducer_Expression_T
// Each piece of the dispatch chain computes the result or returns None so that the chain can continue
type dispatchChainPiece = (
InternalExpressionValue.functionCall,
InternalExpressionValue.environment,
) => option<result<InternalExpressionValue.t, Reducer_ErrorValue.errorValue>>
type dispatchChainPieceWithReducer = (
InternalExpressionValue.functionCall,
InternalExpressionValue.environment,
ExpressionT.reducerFn,
) => option<result<InternalExpressionValue.t, Reducer_ErrorValue.errorValue>>
// This is a switch statement case implementation: get the arguments and compute the result
type genericIEvFunction = (
array<InternalExpressionValue.t>,
InternalExpressionValue.environment,
) => result<InternalExpressionValue.t, Reducer_ErrorValue.errorValue>

View File

@ -250,7 +250,7 @@ float 'float'
d = [0-9] d = [0-9]
boolean 'boolean' boolean 'boolean'
= ('true'/'false') = ('true'/'false') ! [a-z]i ! [_$]
{ return h.nodeBoolean(text() === 'true')} { return h.nodeBoolean(text() === 'true')}
valueConstructor valueConstructor

View File

@ -38,3 +38,12 @@ let fromTypeExpression = (
(reducerFn: ExpressionT.reducerFn), (reducerFn: ExpressionT.reducerFn),
)->Belt.Result.map(T.fromIEvValue) )->Belt.Result.map(T.fromIEvValue)
} }
let fromTypeExpressionExn = (
typeExpressionSourceCode: string,
reducerFn: ExpressionT.reducerFn,
): T.t =>
switch fromTypeExpression(typeExpressionSourceCode, reducerFn) {
| Ok(value) => value
| _ => `Cannot compile ${typeExpressionSourceCode}`->Reducer_Exception.ImpossibleException->raise
}

View File

@ -7,10 +7,15 @@ open InternalExpressionValue
let rec isITypeOf = (anIType: T.iType, aValue): result<bool, T.typeErrorValue> => { let rec isITypeOf = (anIType: T.iType, aValue): result<bool, T.typeErrorValue> => {
let caseTypeIdentifier = (anUpperTypeName, aValue) => { let caseTypeIdentifier = (anUpperTypeName, aValue) => {
let aTypeName = anUpperTypeName->Js.String2.toLowerCase let aTypeName = anUpperTypeName->Js.String2.toLowerCase
let valueTypeName = aValue->valueToValueType->valueTypeToString->Js.String2.toLowerCase switch aTypeName {
switch aTypeName == valueTypeName { | "any" => Ok(true)
| true => Ok(true) | _ => {
| false => T.TypeMismatch(anIType, aValue)->Error let valueTypeName = aValue->valueToValueType->valueTypeToString->Js.String2.toLowerCase
switch aTypeName == valueTypeName {
| true => Ok(true)
| false => T.TypeMismatch(anIType, aValue)->Error
}
}
} }
} }
@ -149,6 +154,13 @@ let checkITypeArguments = (anIType: T.iType, args: array<InternalExpressionValue
} }
} }
let checkITypeArgumentsBool = (anIType: T.iType, args: array<InternalExpressionValue.t>): bool => {
switch checkITypeArguments(anIType, args) {
| Ok(_) => true
| _ => false
}
}
let checkArguments = ( let checkArguments = (
typeExpressionSourceCode: string, typeExpressionSourceCode: string,
args: array<InternalExpressionValue.t>, args: array<InternalExpressionValue.t>,

View File

@ -2,7 +2,7 @@ module Bindings = Reducer_Bindings
let bindings: Bindings.t = let bindings: Bindings.t =
[ [
("System.version", ReducerInterface_InternalExpressionValue.IEvString("0.3.0")), ("System.version", ReducerInterface_InternalExpressionValue.IEvString("0.3.1")),
]->Bindings.fromArray ]->Bindings.fromArray
let makeBindings = (previousBindings: Bindings.t): Bindings.t => let makeBindings = (previousBindings: Bindings.t): Bindings.t =>

View File

@ -220,6 +220,7 @@ module I = {
let increment = n => n + 1 let increment = n => n + 1
let decrement = n => n - 1 let decrement = n => n - 1
let toString = Js.Int.toString let toString = Js.Int.toString
let toFloat = Js.Int.toFloat
} }
exception Assertion(string) exception Assertion(string)
@ -572,12 +573,22 @@ module A = {
|> (x => Ok(x)) |> (x => Ok(x))
} }
let getByOpen = (a, op, bin) => let getByFmap = (a, fn, boolCondition) => {
switch getBy(a, r => bin(op(r))) { let i = ref(0)
| Some(r) => Some(op(r)) let finalFunctionValue = ref(None)
| None => None let length = Belt.Array.length(a)
while i.contents < length && finalFunctionValue.contents == None {
let itemWithFnApplied = Belt.Array.getUnsafe(a, i.contents) |> fn
if boolCondition(itemWithFnApplied) {
finalFunctionValue := Some(itemWithFnApplied)
}
i := i.contents + 1
} }
finalFunctionValue.contents
}
let tail = Belt.Array.sliceToEnd(_, 1) let tail = Belt.Array.sliceToEnd(_, 1)
let zip = Belt.Array.zip let zip = Belt.Array.zip
@ -680,7 +691,7 @@ module A = {
let firstSome = x => Belt.Array.getBy(x, O.isSome) let firstSome = x => Belt.Array.getBy(x, O.isSome)
let firstSomeFn = (r: array<unit => option<'a>>): option<'a> => let firstSomeFn = (r: array<unit => option<'a>>): option<'a> =>
O.flatten(getByOpen(r, l => l(), O.isSome)) O.flatten(getByFmap(r, l => l(), O.isSome))
let firstSomeFnWithDefault = (r, default) => firstSomeFn(r)->O2.default(default) let firstSomeFnWithDefault = (r, default) => firstSomeFn(r)->O2.default(default)

View File

@ -38,3 +38,12 @@ module Logistic = {
@module external variance: (float, float) => float = "@stdlib/stats/base/dists/logistic/variance" @module external variance: (float, float) => float = "@stdlib/stats/base/dists/logistic/variance"
let variance = variance let variance = variance
} }
module Random = {
type sampleArgs = {
probs: array<float>,
size: int,
}
@module external sample: (array<float>, sampleArgs) => array<float> = "@stdlib/random/sample"
let sample = sample
}

View File

@ -14,6 +14,7 @@ module.exports = {
}, },
resolve: { resolve: {
extensions: [".tsx", ".ts", ".js"], extensions: [".tsx", ".ts", ".js"],
fallback: { buffer: ["@stdlib/buffer"] },
}, },
output: { output: {
filename: "bundle.js", filename: "bundle.js",

View File

@ -1,18 +0,0 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module"
},
"plugins": ["@typescript-eslint"],
"rules": {
"@typescript-eslint/naming-convention": "warn",
"@typescript-eslint/semi": "warn",
"curly": "warn",
"eqeqeq": "warn",
"no-throw-literal": "warn",
"semi": "off"
},
"ignorePatterns": ["out", "dist", "**/*.d.ts"]
}

View File

@ -0,0 +1,3 @@
out
dist
media/vendor

View File

@ -7,7 +7,7 @@
"publisher": "QURI", "publisher": "QURI",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/quantified-uncertainty/squiggle.git" "url": "https://github.com/quantified-uncertainty/squiggle.git"
}, },
"icon": "media/vendor/icon.png", "icon": "media/vendor/icon.png",
"engines": { "engines": {
@ -121,20 +121,17 @@
"compile": "yarn run compile:vendor && yarn run compile:grammar && yarn run compile:tsc", "compile": "yarn run compile:vendor && yarn run compile:grammar && yarn run compile:tsc",
"watch": "tsc -b -watch", "watch": "tsc -b -watch",
"pretest": "yarn run compile && yarn run lint", "pretest": "yarn run compile && yarn run lint",
"lint": "eslint client/src server/src --ext ts", "lint": "prettier --check .",
"format": "eslint client/src server/src --ext ts --fix", "format": "prettier --write .",
"package": "npx vsce package --yarn" "package": "npx vsce package --yarn"
}, },
"devDependencies": { "devDependencies": {
"@types/glob": "^7.2.0", "@types/glob": "^7.2.0",
"@types/node": "18.x", "@types/node": "18.x",
"@types/vscode": "^1.70.0", "@types/vscode": "^1.70.0",
"@typescript-eslint/eslint-plugin": "^5.32.0",
"@typescript-eslint/parser": "^5.32.0",
"eslint": "^8.21.0",
"glob": "^8.0.3", "glob": "^8.0.3",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"typescript": "^4.7.4", "typescript": "^4.8.2",
"vsce-yarn-patch": "^1.66.2" "vsce-yarn-patch": "^1.66.2"
}, },
"dependencies": { "dependencies": {

View File

@ -290,12 +290,29 @@ quantile: (distribution, number) => number
quantile(normal(5, 2), 0.5); quantile(normal(5, 2), 0.5);
``` ```
### truncateLeft ### truncate
Truncates the left side of a distribution. Returns either a pointSet distribution or a symbolic distribution. Truncates both the left side and the right side of a distribution.
``` ```
truncateLeft: (distribution, l => number) => distribution truncate: (distribution, left: number, right: number) => distribution
```
<Admonition type="note" title="Implementation Details">
<p>
Sample set distributions are truncated by filtering samples, but point set
distributions are truncated using direct geometric manipulation. Uniform
distributions are truncated symbolically. Symbolic but non-uniform
distributions get converted to Point Set distributions.
</p>
</Admonition>
### truncateLeft
Truncates the left side of a distribution.
```
truncateLeft: (distribution, left: number) => distribution
``` ```
**Examples** **Examples**
@ -306,10 +323,10 @@ truncateLeft(normal(5, 2), 3);
### truncateRight ### truncateRight
Truncates the right side of a distribution. Returns either a pointSet distribution or a symbolic distribution. Truncates the right side of a distribution.
``` ```
truncateRight: (distribution, r => number) => distribution truncateRight: (distribution, right: number) => distribution
``` ```
**Examples** **Examples**
@ -388,7 +405,7 @@ The only functions that do not return normalized distributions are the pointwise
### normalize ### normalize
Normalize a distribution. This means scaling it appropriately so that it's cumulative sum is equal to 1. This only impacts Pointset distributions, because those are the only ones that can be non-normlized. Normalize a distribution. This means scaling it appropriately so that it's cumulative sum is equal to 1. This only impacts Point Set distributions, because those are the only ones that can be non-normlized.
``` ```
normalize: (distribution) => distribution normalize: (distribution) => distribution

View File

@ -46,3 +46,13 @@ PointSet.makeDiscrete([
{ x: 3, y: 0.1 }, { x: 3, y: 0.1 },
]); ]);
``` ```
### mapY
```
PointSet.mapY: (pointSetDist, (number => number)) => pointSetDist
```
```javascript
normal(5,3) |> PointSet.fromDist |> PointSet.mapY({|x| x ^ 2}) |> normalize
```

View File

@ -52,6 +52,29 @@ const config = {
themeConfig: themeConfig:
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */ /** @type {import('@docusaurus/preset-classic').ThemeConfig} */
({ ({
algolia: {
// The application ID provided by Algolia
appId: "KBED3M1CMD",
// Public API key: it is safe to commit it
apiKey: "c61bc7603893cf287ed6971983af8bad",
indexName: "squiggle_docs",
// Optional: see doc section below
contextualSearch: true,
// Optional: Specify domains where the navigation should occur through window.location instead on history.push. Useful when our Algolia config crawls multiple documentation sites and we want to navigate with window.location.href to them.
// externalUrlRegex: 'external\\.com|domain\\.com',
// Optional: Algolia search parameters
searchParameters: {},
// Optional: path for search page that enabled by default (`false` to disable it)
searchPagePath: "search",
//... other Algolia params
},
navbar: { navbar: {
title: "Squiggle", title: "Squiggle",
hideOnScroll: true, hideOnScroll: true,

View File

@ -1,6 +1,6 @@
{ {
"name": "squiggle-website", "name": "squiggle-website",
"version": "0.2.1", "version": "0.3.0",
"private": true, "private": true,
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
@ -15,7 +15,7 @@
"@docusaurus/core": "2.0.1", "@docusaurus/core": "2.0.1",
"@docusaurus/preset-classic": "2.0.1", "@docusaurus/preset-classic": "2.0.1",
"@heroicons/react": "^1.0.6", "@heroicons/react": "^1.0.6",
"@quri/squiggle-components": "^0.2.23", "@quri/squiggle-components": "^0.3",
"base64-js": "^1.5.1", "base64-js": "^1.5.1",
"clsx": "^1.2.1", "clsx": "^1.2.1",
"hast-util-is-element": "2.1.2", "hast-util-is-element": "2.1.2",

3436
yarn.lock

File diff suppressed because it is too large Load Diff