Merge branch 'reducer-dev' of github.com:umuro/squiggle into reducer-dev

This commit is contained in:
Umur Ozkul 2022-03-25 18:34:56 +01:00
commit b54ec18954
66 changed files with 24567 additions and 60005 deletions

BIN
.DS_Store vendored

Binary file not shown.

23
.github/CODEOWNERS vendored Normal file
View File

@ -0,0 +1,23 @@
# CODEOWNERS file
#
# This file is used to describe who owns what in this repository.
#
# For documentation on this file, see https://help.github.com/articles/about-codeowners/
# Mentioned users will get code review requests.
#
# IMPORTANT NOTE: in order to actually get pinged, commit access is required.
# This also holds true for GitHub teams.
# This file
/.github/CODEOWNERS @quinn-dougherty
# Any rescript code
*.res @Hazelfire @OAGr @quinn-dougherty
# Any typescript code
*.tsx @Hazelfire @OAGr @quinn-dougherty
# Any opsy files
*.json @quinn-dougherty
*.yaml @quinn-dougherty
*.yml @quinn-dougherty

14
.github/ISSUE_TEMPLATE/developer-bug.md vendored Normal file
View File

@ -0,0 +1,14 @@
---
name: Developer friction when contributing to Squiggle
about: Did your yarn scripts fail? Did the CI diverge from a README? Etc.
labels: 'ops'
---
# Description:
# The OS and version, yarn version, etc. in which this came up
# Desired behavior

7
.github/ISSUE_TEMPLATE/future.md vendored Normal file
View File

@ -0,0 +1,7 @@
---
name: Idea or feature request
about: Where would you like to see Squiggle go over the next few months, several months, or few years?
---
# Description

18
.github/ISSUE_TEMPLATE/user-bug.md vendored Normal file
View File

@ -0,0 +1,18 @@
---
name: Bug reports for Squiggle users
about: Rendering oddly, trouble with the playground, something like this?
labels: 'bug'
---
# Description:
# Steps to reproduce:
1.
2.
3.
# Expected behavior:
# What I got instead:

27
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,27 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "npm" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "daily"
- package-ecosystem: "npm" # See documentation for possible values
directory: "/packages/squiggle-lang" # Location of package manifests
schedule:
interval: "daily"
- package-ecosystem: "npm" # See documentation for possible values
directory: "/packages/components" # Location of package manifests
schedule:
interval: "daily"
- package-ecosystem: "npm" # See documentation for possible values
directory: "/packages/website" # Location of package manifests
schedule:
interval: "daily"
- package-ecosystem: "npm" # See documentation for possible values
directory: "/packages/playground" # Location of package manifests
schedule:
interval: "daily"

87
.github/workflows/ci.yaml vendored Normal file
View File

@ -0,0 +1,87 @@
name: Squiggle packages check
on: [push]
jobs:
pre_check:
name: Precheck for skipping redundant jobs
runs-on: ubuntu-latest
outputs:
should_skip_lang: ${{ steps.skip_lang_check.outputs.should_skip }}
should_skip_components: ${{ steps.skip_components_check.outputs.should_skip }}
should_skip_website: ${{ steps.skip_website_check.outputs.should_skip }}
steps:
- id: skip_lang_check
name: Check if the changes are about squiggle-lang src files
uses: fkirc/skip-duplicate-actions@master
with:
paths: '["packages/squiggle-lang/*"]'
- id: skip_components_check
name: Check if the changes are about components src files
uses: fkirc/skip-duplicate-actions@master
with:
paths: '["packages/components/*"]'
- id: skip_website_check
name: Check if the changes are about website src files
uses: fkirc/skip-duplicate-actions@master
with:
paths: '["packages/website/*"]'
lang-build-test:
name: Language build and test
runs-on: ubuntu-latest
needs: pre_check
if: ${{ needs.pre_check.outputs.should_skip_lang != 'true' }}
defaults:
run:
shell: bash
working-directory: packages/squiggle-lang
steps:
- uses: actions/checkout@v2
- name: Install dependencies from monorepo level
run: cd ../../ && yarn
- name: Build rescript codebase
run: yarn build
- name: Run tests
run: yarn test
- name: Run webpack
run: yarn bundle
components-build-test:
name: Components build and test
runs-on: ubuntu-latest
needs: [pre_check]
if: ${{ needs.pre_check.outputs.should_skip_components != 'true' }}
defaults:
run:
shell: bash
working-directory: packages/components
steps:
- uses: actions/checkout@v2
- name: Install dependencies from monorepo level
run: cd ../../ && yarn
- name: Build rescript in squiggle-lang
run: cd ../squiggle-lang && yarn build
- name: Run webpack
run: yarn bundle
- name: Build storybook
run: yarn build
website-build:
name: Website build
runs-on: ubuntu-latest
needs: pre_check
if: ${{ needs.pre_check.outputs.should_skip_website != 'true' }}
defaults:
run:
shell: bash
working-directory: packages/website
steps:
- uses: actions/checkout@v2
- name: Install dependencies from monorepo level
run: cd ../../ && yarn
- name: Build rescript in squiggle-lang
run: cd ../squiggle-lang && yarn build
- name: Build website assets
run: yarn build

70
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@ -0,0 +1,70 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '42 19 * * 0'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'javascript' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://git.io/codeql-language-support
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

View File

@ -1,19 +0,0 @@
name: Squiggle Lang Jest Tests
on: [push]
jobs:
build:
runs-on: ubuntu-latest
defaults:
run:
shell: bash
working-directory: packages/squiggle-lang
steps:
- uses: actions/checkout@v2
- name: Install Packages
run: yarn
- name: Build rescript
run: yarn run build
- name: Run tests
run: yarn test

6
.parcelrc Normal file
View File

@ -0,0 +1,6 @@
{
"extends": "@parcel/config-default",
"transformers": {
"*.res": ["@parcel/transformer-raw"]
}
}

42
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,42 @@
_The current document was written quickly and not exhaustively, yet, it's unfinished. [Template here](https://mozillascience.github.io/working-open-workshop/contributing/)_
# Contributing to Squiggle
We welcome contributions from developers, especially people in react/typescript, rescript, and interpreters/parsers. We also are keen to hear issues filed by users!
Squiggle is currently pre-alpha.
# Quick links
- [Roadmap to the alpha](https://github.com/QURIresearch/squiggle/projects/2)
- 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)
- You can email `quinn@quantifieduncertainty.org` if you need assistance in onboarding or if you have questions
# Bug reports
Anyone (with a github account) can file an issue at any time. Please allow Quinn, Sam, and Ozzie to triage, but otherwise just follow the suggestions in the issue templates.
# Project structure
Squiggle is a **monorepo** with four **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`
# Deployment ops
We use netlify, and it should only concern Quinn, Sam, and Ozzie.
# Development environment, building, testing, dev server
You need `yarn`.
TODO: fill this out based on all the different packages scripts once they cool down.
# Pull request protocol
Please work against `staging` branch. **Do not** work against `master`. Please do not merge without approval from some subset of Quinn, Sam, and Ozzie; they will be auto-pinged.

View File

@ -1,32 +1,43 @@
# Squiggle
This is an experiment DSL/language for making probabilistic estimates.
This is an experiment DSL/language for making probabilistic estimates. The full story can be found [here](https://www.lesswrong.com/s/rDe8QE5NvXcZYzgZ3).
This monorepo has several packages that can be used for various purposes. All
the packages can be found in `packages`.
`@squiggle/lang` in `packages/squiggle-lang` contains the core language, particularly
- `@quri/squiggle-lang` in `packages/squiggle-lang` contains the core language, particularly
an interface to parse squiggle expressions and return descriptions of distributions
or results.
`@squiggle/components` in `packages/components` contains React components that
- `@quri/squiggle-components` in `packages/components` contains React components that
can be passed squiggle strings as props, and return a presentation of the result
of the calculation.
`@squiggle/playground` in `packages/playground` contains a website for a playground
- `@quri/playground` in `packages/playground` contains a website for a playground
for squiggle. This website is hosted at `playground.squiggle-language.com`
`@squiggle/website` in `packages/website` The main descriptive website for squiggle,
- `@quri/squiggle-website` in `packages/website` The main descriptive website for squiggle,
it is hosted at `squiggle-language.com`.
The playground depends on the components library which then depends on the language.
This means that if you wish to work on the components library, you will need
to package the language, and for the playground to work, you will need to package
the components library and the playground.
The playground depends on the components library which then depends on the language. This means that if you wish to work on the components library, you will need to build (no need to bundle) the language, and as of this writing playground doesn't really work.
Scripts are available for you in the root directory to do important activities,
such as:
# Develop
`yarn build:lang`. Builds and packages the language
`yarn storybook:components`. Hosts the component storybook
For any project in the repo, begin by running `yarn` in the top level (TODO: is this true?)
``` sh
yarn
```
See `packages/*/README.md` to work with whatever project you're interested in.
## `codium` for `rescript`
If you have `nix` installed with `flakes` enabled, you can build a `codium` in this repo for `rescript` development, if you don't want to pollute your machine's global editor with another mode/extension.
``` sh
nix develop
codium
```
The `nix develop` shell also provides `yarn`.
# Contributing
See `CONTRIBUTING.md`.

9
flake-compat.nix Normal file
View File

@ -0,0 +1,9 @@
let
lock = builtins.fromJSON (builtins.readFile ./flake.lock);
inherit (lock.nodes.flake-compat.locked) owner repo rev narHash;
flake-compat = builtins.fetchTarball {
url = "https://github.com/${owner}/${repo}/archive/${rev}.tar.gz";
sha256 = narHash;
};
in
import flake-compat { src = ./.; }

44
flake.lock Normal file
View File

@ -0,0 +1,44 @@
{
"nodes": {
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1641205782,
"narHash": "sha256-4jY7RCWUoZ9cKD8co0/4tFARpWB+57+r1bLLvXNJliY=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "b7547d3eed6f32d06102ead8991ec52ab0a4f1a7",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1647893727,
"narHash": "sha256-pOi7VdCb+s5Cwh5CS7YEZVRgH9uCmE87J5W7iXv29Ck=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "1ec61dd4167f04be8d05c45780818826132eea0d",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-compat": "flake-compat",
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

93
flake.nix Normal file
View File

@ -0,0 +1,93 @@
{
description = "Building codium for rescript development";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
flake-compat = {
url = "github:edolstra/flake-compat";
flake = false;
};
};
outputs =
{ self
, nixpkgs
, flake-compat
}:
let
# Generate a user-friendly version number.
version = builtins.substring 0 8 self.lastModifiedDate;
# System types to support.
supportedSystems = [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" ];
# Helper function to generate an attrset '{ x86_64-linux = f "x86_64-linux"; ... }'.
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
# Nixpkgs instantiated for supported system types.
nixpkgsFor = forAllSystems (system:
import nixpkgs {
inherit system;
overlays = [ self.overlay ];
});
in
{
overlay = final: prev: { };
# the default devShell used when running `nix develop`
devShell = forAllSystems (system: self.devShells.${system}.defaultShell);
devShells = forAllSystems (system:
let
pkgs = nixpkgsFor."${system}";
in
{
# In case we don't want to provide an editor, this defaultShell will provide only coq packages we need.
defaultShell = pkgs.mkShell {
buildInputs = with pkgs; [
yarn
yarn2nix
nodePackages.npm
nodejs
(pkgs.vscode-with-extensions.override {
vscode = pkgs.vscodium;
vscodeExtensions = pkgs.vscode-utils.extensionsFromVscodeMarketplace [
{
name = "rescript-vscode";
publisher = "chenglou92";
version = "1.2.1";
sha256 = "sha256-7/YakKtJ4WhgAR4rZltrq8g4TtM5QZ2spbrEUrNoXVg=";
}
{
name = "vim";
publisher = "vscodevim";
version = "1.22.2";
sha256 = "sha256-dtIlgODzRdoMKnG9050ZcCX3w15A/R3FaMc+ZylvBbU=";
}
{
name = "vscode-typescript-next";
publisher = "ms-vscode";
version = "4.7.20220323";
sha256 = "sha256-mjiBCyg5As/XAU9I5k6jEZWGJA3P6P5o1roe2bS7aUI=";
}
{
name = "nix-ide";
publisher = "jnoortheen";
version = "0.1.20";
sha256 = "sha256-Q6X41I68m0jaCXaQGEFOoAbSUrr/wFhfCH5KrduOtZo=";
}
{
name = "json";
publisher = "ZainChen";
version = "2.0.2";
sha256 = "sha256-nC3Q8KuCtn/jg1j/NaAxWGvnKe/ykrPm2PUjfsJz8aI=";
}
{
name = "prettier-vscode";
publisher = "esbenp";
version = "9.3.0";
sha256 = "sha256-hJgPjWf7a8+ltjmXTK8U/MwqgIZqBjmcCfHsAk2G3PA=";
}
];
})
];
};
}
);
};
}

View File

@ -2,14 +2,7 @@
"private": true,
"name": "squiggle",
"scripts": {
"build:lang": "cd packages/squiggle-lang && yarn && yarn build && yarn package",
"storybook:components": "cd packages/components && yarn && yarn storybook",
"build-storybook:components": "cd packages/components && yarn && yarn build-storybook",
"build:components": "cd packages/components && yarn && yarn package",
"build:playground": "cd packages/playground && yarn && yarn parcel-build",
"ci:lang": "yarn workspace @squiggle/lang ci",
"ci:components": "yarn ci:lang && yarn workspace @squiggle/components ci",
"ci:playground": "yarn ci:components && yarn workspace @squiggle/playground ci"
"nodeclean": "rm -r node_modules && rm -r packages/*/node_modules"
},
"workspaces": [
"packages/*"

View File

@ -0,0 +1,5 @@
dist
build
node_modules
storybook-static
.storybook

View File

@ -0,0 +1 @@
{}

View File

@ -1,15 +1,11 @@
//const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
const custom = require('../webpack.config.js');
module.exports = {
/* webpackFinal: async (config) => {
config.resolve.plugins = [
...(config.resolve.plugins || []),
new TsconfigPathsPlugin({
extensions: config.resolve.extensions,
}),
];
return config;
},*/
webpackFinal: async (config) => {
config.resolve.alias = custom.resolve.alias;
return { ...config, module: { ...config.module, rules: config.module.rules.concat(custom.module.rules) } };
},
"stories": [
"../src/**/*.stories.mdx",
"../src/**/*.stories.@(js|jsx|ts|tsx)"

View File

@ -1,6 +1,25 @@
# Squiggle Components
This package contains all the components for squiggle. These can be used either as a library or hosted as a [storybook](https://storybook.js.org/).
This package contains all the components for squiggle. These can be used either
as a library or hosted as a [storybook](https://storybook.js.org/).
# Build for development
We assume that you had run `yarn` at monorepo level, installing dependencies.
To run the storybook, run `yarn` then `yarn storybook`.
You need to _prepare_ by building and bundling `squiggle-lang`
``` sh
cd ../squiggle-lang
yarn build
```
If you've otherwise done this recently you can skip those.
Run a development server
``` sh
yarn start
```
And build artefacts for production,
``` sh
yarn bundle # builds components library
yarn build # builds storybook app
```

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,9 @@
{
"name": "@quri/squiggle-components",
"version": "0.1.5",
"version": "0.1.6",
"dependencies": {
"@quri/squiggle-lang": "0.2.2",
"@testing-library/jest-dom": "^5.16.2",
"@testing-library/jest-dom": "^5.16.3",
"@testing-library/react": "^12.1.2",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.4.0",
@ -26,10 +26,10 @@
"webpack-cli": "^4.9.2"
},
"scripts": {
"storybook": "cross-env REACT_APP_FAST_REFRESH=false && start-storybook -p 6006 -s public",
"build-storybook": "build-storybook -s public",
"package": "tsc",
"ci": "yarn package"
"start": "cross-env REACT_APP_FAST_REFRESH=false && start-storybook -p 6006 -s public",
"build": "build-storybook -s public",
"bundle": "webpack",
"all": "yarn bundle && yarn build"
},
"eslintConfig": {
"extends": [
@ -69,16 +69,17 @@
"@storybook/preset-create-react-app": "^4.0.0",
"@storybook/react": "^6.4.18",
"@types/webpack": "^5.28.0",
"prettier": "^2.6.0",
"react-codejar": "^1.1.2",
"ts-loader": "^9.2.8",
"webpack": "^5.70.0",
"webpack-cli": "^4.9.2"
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.7.4"
},
"resolutions": {
"@types/react": "17.0.39"
},
"source": "./src/index.ts",
"module": "dist/index.js",
"main": "dist/bundle.js",
"types": "dist/index.d.ts"
"types": "dist/src/index.d.ts"
}

View File

@ -5,10 +5,7 @@
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Squiggle components"
/>
<meta name="description" content="Squiggle components" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<title>Squiggle Components</title>
</head>

View File

@ -1,5 +0,0 @@
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
name = "squiggle-components";
buildInputs = with pkgs; [ nodePackages.yarn nodejs ];
}

View File

@ -1,86 +1,96 @@
import * as React from 'react';
import * as _ from 'lodash';
import type { Spec } from 'vega';
import { run } from '@squiggle/lang';
import type { DistPlus, SamplingInputs } from '@squiggle/lang';
import { createClassFromSpec } from 'react-vega';
import * as chartSpecification from './spec-distributions.json'
import * as percentilesSpec from './spec-pertentiles.json'
import * as React from "react";
import _ from "lodash";
import type { Spec } from "vega";
import { run } from "@quri/squiggle-lang";
import type {
DistPlus,
SamplingInputs,
exportEnv,
exportDistribution,
} from "@quri/squiggle-lang";
import { createClassFromSpec } from "react-vega";
import * as chartSpecification from "./spec-distributions.json";
import * as percentilesSpec from "./spec-percentiles.json";
let SquiggleVegaChart = createClassFromSpec({'spec': chartSpecification as Spec});
let SquiggleVegaChart = createClassFromSpec({
spec: chartSpecification as Spec,
});
let SquigglePercentilesChart = createClassFromSpec({'spec': percentilesSpec as Spec});
let SquigglePercentilesChart = createClassFromSpec({
spec: percentilesSpec as Spec,
});
export interface SquiggleChartProps {
/** The input string for squiggle */
squiggleString : string,
squiggleString: string;
/** If the output requires monte carlo sampling, the amount of samples */
sampleCount? : number,
sampleCount?: number;
/** The amount of points returned to draw the distribution */
outputXYPoints? : number,
kernelWidth? : number,
pointDistLength? : number,
outputXYPoints?: number;
kernelWidth?: number;
pointDistLength?: number;
/** If the result is a function, where the function starts */
diagramStart? : number,
diagramStart?: number;
/** If the result is a function, where the function ends */
diagramStop? : number,
diagramStop?: number;
/** If the result is a function, how many points along the function it samples */
diagramCount? : number
diagramCount?: number;
/** variables declared before this expression */
environment?: exportEnv;
/** When the environment changes */
onEnvChange?(env: exportEnv): void;
}
export const SquiggleChart : React.FC<SquiggleChartProps> = props => {
let samplingInputs : SamplingInputs = {
sampleCount : props.sampleCount,
outputXYPoints : props.outputXYPoints,
kernelWidth : props.kernelWidth,
pointDistLength : props.pointDistLength
}
export const SquiggleChart: React.FC<SquiggleChartProps> = (props) => {
let samplingInputs: SamplingInputs = {
sampleCount: props.sampleCount,
outputXYPoints: props.outputXYPoints,
kernelWidth: props.kernelWidth,
pointDistLength: props.pointDistLength,
};
let result = run(props.squiggleString, samplingInputs);
console.log(result)
let result = run(props.squiggleString, samplingInputs, props.environment);
if (result.tag === "Ok") {
let chartResults = result.value.map(chartResult => {
console.log(chartResult)
if(chartResult["NAME"] === "Float"){
let environment = result.value.environment;
let exports = result.value.exports;
if (props.onEnvChange) props.onEnvChange(environment);
let chartResults = exports.map((chartResult: exportDistribution) => {
if (chartResult["NAME"] === "Float") {
return <MakeNumberShower precision={3} number={chartResult["VAL"]} />;
}
else if(chartResult["NAME"] === "DistPlus"){
} else if (chartResult["NAME"] === "DistPlus") {
let shape = chartResult.VAL.pointSetDist;
if(shape.tag === "Continuous"){
if (shape.tag === "Continuous") {
let xyShape = shape.value.xyShape;
let totalY = xyShape.ys.reduce((a, b) => a + b);
let total = 0;
let cdf = xyShape.ys.map(y => {
let cdf = xyShape.ys.map((y) => {
total += y;
return total / totalY;
})
let values = _.zip(cdf, xyShape.xs, xyShape.ys).map(([c, x, y ]) => ({cdf: (c * 100).toFixed(2) + "%", x: x, y: y}));
});
let values = _.zip(cdf, xyShape.xs, xyShape.ys).map(([c, x, y]) => ({
cdf: (c * 100).toFixed(2) + "%",
x: x,
y: y,
}));
return (
<SquiggleVegaChart
data={{"con": values}}
/>
);
}
else if(shape.tag === "Discrete"){
return <SquiggleVegaChart data={{ con: values }} />;
} else if (shape.tag === "Discrete") {
let xyShape = shape.value.xyShape;
let totalY = xyShape.ys.reduce((a, b) => a + b);
let total = 0;
let cdf = xyShape.ys.map(y => {
let cdf = xyShape.ys.map((y) => {
total += y;
return total / totalY;
})
let values = _.zip(cdf, xyShape.xs, xyShape.ys).map(([c, x,y]) => ({cdf: (c * 100).toFixed(2) + "%", x: x, y: y}));
});
let values = _.zip(cdf, xyShape.xs, xyShape.ys).map(([c, x, y]) => ({
cdf: (c * 100).toFixed(2) + "%",
x: x,
y: y,
}));
return (
<SquiggleVegaChart
data={{"dis": values}}
/>
);
}
else if(shape.tag === "Mixed"){
return <SquiggleVegaChart data={{ dis: values }} />;
} else if (shape.tag === "Mixed") {
let discreteShape = shape.value.discrete.xyShape;
let totalDiscrete = discreteShape.ys.reduce((a, b) => a + b);
@ -89,141 +99,150 @@ export const SquiggleChart : React.FC<SquiggleChartProps> = props => {
let continuousPoints = _.zip(continuousShape.xs, continuousShape.ys);
interface labeledPoint {
x: number,
y: number,
type: "discrete" | "continuous"
};
x: number;
y: number;
type: "discrete" | "continuous";
}
let markedDisPoints : labeledPoint[] = discretePoints.map(([x,y]) => ({x: x, y: y, type: "discrete"}))
let markedConPoints : labeledPoint[] = continuousPoints.map(([x,y]) => ({x: x, y: y, type: "continuous"}))
let markedDisPoints: labeledPoint[] = discretePoints.map(
([x, y]) => ({ x: x, y: y, type: "discrete" })
);
let markedConPoints: labeledPoint[] = continuousPoints.map(
([x, y]) => ({ x: x, y: y, type: "continuous" })
);
let sortedPoints = _.sortBy(markedDisPoints.concat(markedConPoints), 'x')
let sortedPoints = _.sortBy(
markedDisPoints.concat(markedConPoints),
"x"
);
let totalContinuous = 1 - totalDiscrete;
let totalY = continuousShape.ys.reduce((a:number, b:number) => a + b);
let totalY = continuousShape.ys.reduce(
(a: number, b: number) => a + b
);
let total = 0;
let cdf = sortedPoints.map((point: labeledPoint) => {
if(point.type == "discrete") {
if (point.type == "discrete") {
total += point.y;
return total;
}
else if (point.type == "continuous") {
total += point.y / totalY * totalContinuous;
} else if (point.type == "continuous") {
total += (point.y / totalY) * totalContinuous;
return total;
}
});
interface cdfLabeledPoint {
cdf: string,
x: number,
y: number,
type: "discrete" | "continuous"
cdf: string;
x: number;
y: number;
type: "discrete" | "continuous";
}
let cdfLabeledPoint : cdfLabeledPoint[] = _.zipWith(cdf, sortedPoints, (c: number, point: labeledPoint) => ({...point, cdf: (c * 100).toFixed(2) + "%"}))
let continuousValues = cdfLabeledPoint.filter(x => x.type == "continuous")
let discreteValues = cdfLabeledPoint.filter(x => x.type == "discrete")
let cdfLabeledPoint: cdfLabeledPoint[] = _.zipWith(
cdf,
sortedPoints,
(c: number, point: labeledPoint) => ({
...point,
cdf: (c * 100).toFixed(2) + "%",
})
);
let continuousValues = cdfLabeledPoint.filter(
(x) => x.type == "continuous"
);
let discreteValues = cdfLabeledPoint.filter(
(x) => x.type == "discrete"
);
return (
<SquiggleVegaChart
data={{"con": continuousValues, "dis": discreteValues}}
/>
<SquiggleVegaChart
data={{ con: continuousValues, dis: discreteValues }}
/>
);
}
}
else if(chartResult.NAME === "Function"){
// We are looking at a function. In this case, we draw a Percentiles chart
let start = props.diagramStart ? props.diagramStart : 0
let stop = props.diagramStop ? props.diagramStop : 10
let count = props.diagramCount ? props.diagramCount : 0.1
let step = (stop - start)/ count
let data = _.range(start, stop, step).map(x => {
if(chartResult.NAME=="Function"){
let result = chartResult.VAL(x);
if(result.tag == "Ok"){
let percentileArray = [
0.01,
0.05,
0.1,
0.2,
0.3,
0.4,
0.5,
0.6,
0.7,
0.8,
0.9,
0.95,
0.99
]
let percentiles = getPercentiles(percentileArray, result.value);
return {
"x": x,
"p1": percentiles[0],
"p5": percentiles[1],
"p10": percentiles[2],
"p20": percentiles[3],
"p30": percentiles[4],
"p40": percentiles[5],
"p50": percentiles[6],
"p60": percentiles[7],
"p70": percentiles[8],
"p80": percentiles[9],
"p90": percentiles[10],
"p95": percentiles[11],
"p99": percentiles[12]
}
}
}
return 0;
})
return <SquigglePercentilesChart data={{"facet": data}} />
}
})
return <>{chartResults}</>;
}
else if(result.tag == "Error") {
// At this point, we came across an error. What was our error?
return (<p>{"Error parsing Squiggle: " + result.value}</p>)
} else if (chartResult.NAME === "Function") {
// We are looking at a function. In this case, we draw a Percentiles chart
let start = props.diagramStart ? props.diagramStart : 0;
let stop = props.diagramStop ? props.diagramStop : 10;
let count = props.diagramCount ? props.diagramCount : 100;
let step = (stop - start) / count;
let data = _.range(start, stop, step).map((x) => {
if (chartResult.NAME == "Function") {
let result = chartResult.VAL(x);
if (result.tag == "Ok") {
let percentileArray = [
0.01, 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 0.95,
0.99,
];
let percentiles = getPercentiles(percentileArray, result.value);
return {
x: x,
p1: percentiles[0],
p5: percentiles[1],
p10: percentiles[2],
p20: percentiles[3],
p30: percentiles[4],
p40: percentiles[5],
p50: percentiles[6],
p60: percentiles[7],
p70: percentiles[8],
p80: percentiles[9],
p90: percentiles[10],
p95: percentiles[11],
p99: percentiles[12],
};
}
return null;
}
});
return <SquigglePercentilesChart data={{ facet: data.filter(x => x !== null) }} />;
}
});
return <>{chartResults}</>;
} else if (result.tag == "Error") {
// At this point, we came across an error. What was our error?
return <p>{"Error parsing Squiggle: " + result.value}</p>;
}
return (<p>{"Invalid Response"}</p>)
return <p>{"Invalid Response"}</p>;
};
function getPercentiles(percentiles:number[], t : DistPlus) {
if(t.pointSetDist.tag == "Discrete") {
function getPercentiles(percentiles: number[], t: DistPlus) {
if (t.pointSetDist.tag == "Discrete") {
let total = 0;
let maxX = _.max(t.pointSetDist.value.xyShape.xs)
let bounds = percentiles.map(_ => maxX);
_.zipWith(t.pointSetDist.value.xyShape.xs,t.pointSetDist.value.xyShape.ys, (x,y) => {
total += y
let maxX = _.max(t.pointSetDist.value.xyShape.xs);
let bounds = percentiles.map((_) => maxX);
_.zipWith(
t.pointSetDist.value.xyShape.xs,
t.pointSetDist.value.xyShape.ys,
(x, y) => {
total += y;
percentiles.forEach((v, i) => {
if(total > v && bounds[i] == maxX){
bounds[i] = x
}
})
});
return bounds;
}
else if(t.pointSetDist.tag == "Continuous"){
if (total > v && bounds[i] == maxX) {
bounds[i] = x;
}
});
}
);
return bounds;
} else if (t.pointSetDist.tag == "Continuous") {
let total = 0;
let maxX = _.max(t.pointSetDist.value.xyShape.xs)
let totalY = _.sum(t.pointSetDist.value.xyShape.ys)
let bounds = percentiles.map(_ => maxX);
_.zipWith(t.pointSetDist.value.xyShape.xs,t.pointSetDist.value.xyShape.ys, (x,y) => {
let maxX = _.max(t.pointSetDist.value.xyShape.xs);
let totalY = _.sum(t.pointSetDist.value.xyShape.ys);
let bounds = percentiles.map((_) => maxX);
_.zipWith(
t.pointSetDist.value.xyShape.xs,
t.pointSetDist.value.xyShape.ys,
(x, y) => {
total += y / totalY;
percentiles.forEach((v, i) => {
if(total > v && bounds[i] == maxX){
bounds[i] = x
}
})
});
return bounds;
}
else if(t.pointSetDist.tag == "Mixed"){
if (total > v && bounds[i] == maxX) {
bounds[i] = x;
}
});
}
);
return bounds;
} else if (t.pointSetDist.tag == "Mixed") {
let discreteShape = t.pointSetDist.value.discrete.xyShape;
let totalDiscrete = discreteShape.ys.reduce((a, b) => a + b);
@ -232,80 +251,87 @@ function getPercentiles(percentiles:number[], t : DistPlus) {
let continuousPoints = _.zip(continuousShape.xs, continuousShape.ys);
interface labeledPoint {
x: number,
y: number,
type: "discrete" | "continuous"
};
x: number;
y: number;
type: "discrete" | "continuous";
}
let markedDisPoints : labeledPoint[] = discretePoints.map(([x,y]) => ({x: x, y: y, type: "discrete"}))
let markedConPoints : labeledPoint[] = continuousPoints.map(([x,y]) => ({x: x, y: y, type: "continuous"}))
let markedDisPoints: labeledPoint[] = discretePoints.map(([x, y]) => ({
x: x,
y: y,
type: "discrete",
}));
let markedConPoints: labeledPoint[] = continuousPoints.map(([x, y]) => ({
x: x,
y: y,
type: "continuous",
}));
let sortedPoints = _.sortBy(markedDisPoints.concat(markedConPoints), 'x')
let sortedPoints = _.sortBy(markedDisPoints.concat(markedConPoints), "x");
let totalContinuous = 1 - totalDiscrete;
let totalY = continuousShape.ys.reduce((a:number, b:number) => a + b);
let totalY = continuousShape.ys.reduce((a: number, b: number) => a + b);
let total = 0;
let maxX = _.max(sortedPoints.map(x => x.x));
let bounds = percentiles.map(_ => maxX);
let maxX = _.max(sortedPoints.map((x) => x.x));
let bounds = percentiles.map((_) => maxX);
sortedPoints.map((point: labeledPoint) => {
if(point.type == "discrete") {
if (point.type == "discrete") {
total += point.y;
} else if (point.type == "continuous") {
total += (point.y / totalY) * totalContinuous;
}
else if (point.type == "continuous") {
total += point.y / totalY * totalContinuous;
}
percentiles.forEach((v,i) => {
if(total > v && bounds[i] == maxX){
percentiles.forEach((v, i) => {
if (total > v && bounds[i] == maxX) {
bounds[i] = total;
}
})
});
return total;
});
return bounds;
}
}
function MakeNumberShower(props: {number: number, precision :number}){
function MakeNumberShower(props: { number: number; precision: number }) {
let numberWithPresentation = numberShow(props.number, props.precision);
return (
<span>
{numberWithPresentation.value}
{numberWithPresentation.symbol}
{numberWithPresentation.power ?
<span>
{'\u00b710'}
<span style={{fontSize: "0.6em", verticalAlign: "super"}}>
{numberWithPresentation.power}
{numberWithPresentation.power ? (
<span>
{"\u00b710"}
<span style={{ fontSize: "0.6em", verticalAlign: "super" }}>
{numberWithPresentation.power}
</span>
</span>
</span>
: <></>}
) : (
<></>
)}
</span>
);
);
}
const orderOfMagnitudeNum = (n:number) => {
const orderOfMagnitudeNum = (n: number) => {
return Math.pow(10, n);
};
// 105 -> 3
const orderOfMagnitude = (n:number) => {
const orderOfMagnitude = (n: number) => {
return Math.floor(Math.log(n) / Math.LN10 + 0.000000001);
};
function withXSigFigs(number:number, sigFigs:number) {
function withXSigFigs(number: number, sigFigs: number) {
const withPrecision = number.toPrecision(sigFigs);
const formatted = Number(withPrecision);
return `${formatted}`;
}
class NumberShower {
number: number
precision: number
number: number;
precision: number;
constructor(number:number, precision = 2) {
constructor(number: number, precision = 2) {
this.number = number;
this.precision = precision;
}
@ -314,9 +340,9 @@ class NumberShower {
const number = Math.abs(this.number);
const response = this.evaluate(number);
if (this.number < 0) {
response.value = '-' + response.value;
response.value = "-" + response.value;
}
return response
return response;
}
metricSystem(number: number, order: number) {
@ -327,7 +353,7 @@ class NumberShower {
evaluate(number: number) {
if (number === 0) {
return { value: this.metricSystem(0, 0) }
return { value: this.metricSystem(0, 0) };
}
const order = orderOfMagnitude(number);
@ -336,13 +362,13 @@ class NumberShower {
} else if (order < 4) {
return { value: this.metricSystem(number, 0) };
} else if (order < 6) {
return { value: this.metricSystem(number, 3), symbol: 'K' };
return { value: this.metricSystem(number, 3), symbol: "K" };
} else if (order < 9) {
return { value: this.metricSystem(number, 6), symbol: 'M' };
return { value: this.metricSystem(number, 6), symbol: "M" };
} else if (order < 12) {
return { value: this.metricSystem(number, 9), symbol: 'B' };
return { value: this.metricSystem(number, 9), symbol: "B" };
} else if (order < 15) {
return { value: this.metricSystem(number, 12), symbol: 'T' };
return { value: this.metricSystem(number, 12), symbol: "T" };
} else {
return { value: this.metricSystem(number, order), power: order };
}

View File

@ -0,0 +1,120 @@
import * as React from "react";
import * as ReactDOM from "react-dom";
import { SquiggleChart } from "./SquiggleChart";
import { ReactCodeJar } from "react-codejar";
import type { exportEnv } from "@quri/squiggle-lang";
export interface SquiggleEditorProps {
/** The input string for squiggle */
initialSquiggleString?: string;
/** If the output requires monte carlo sampling, the amount of samples */
sampleCount?: number;
/** The amount of points returned to draw the distribution */
outputXYPoints?: number;
kernelWidth?: number;
pointDistLength?: number;
/** If the result is a function, where the function starts */
diagramStart?: number;
/** If the result is a function, where the function ends */
diagramStop?: number;
/** If the result is a function, how many points along the function it samples */
diagramCount?: number;
/** The environment, other variables that were already declared */
environment?: exportEnv;
/** when the environment changes. Used again for notebook magic*/
onEnvChange?(env: exportEnv): void;
}
const highlight = (editor: HTMLInputElement) => {
let code = editor.textContent;
code = code.replace(/\((\w+?)(\b)/g, '(<font color="#8a2be2">$1</font>$2');
editor.innerHTML = code;
};
interface SquiggleEditorState {
expression: string;
env: exportEnv;
}
export class SquiggleEditor extends React.Component<
SquiggleEditorProps,
SquiggleEditorState
> {
constructor(props: SquiggleEditorProps) {
super(props);
let code = props.initialSquiggleString ? props.initialSquiggleString : "";
this.state = { expression: code, env: props.environment };
}
render() {
let { expression, env } = this.state;
let props = this.props;
return (
<div>
<ReactCodeJar
code={expression}
onUpdate={(e) => {
this.setState({ expression: e });
}}
style={{
borderRadius: "6px",
width: "530px",
border: "1px solid grey",
fontFamily: "'Source Code Pro', monospace",
fontSize: "14px",
fontWeight: "400",
letterSpacing: "normal",
lineHeight: "20px",
padding: "10px",
tabSize: "4",
}}
highlight={highlight}
lineNumbers={false}
/>
<SquiggleChart
squiggleString={expression}
sampleCount={props.sampleCount}
outputXYPoints={props.outputXYPoints}
kernelWidth={props.kernelWidth}
pointDistLength={props.pointDistLength}
diagramStart={props.diagramStart}
diagramStop={props.diagramStop}
diagramCount={props.diagramCount}
environment={env}
onEnvChange={props.onEnvChange}
/>
</div>
);
}
}
export function renderSquiggleEditor(props: SquiggleEditorProps) {
let parent = document.createElement("div");
ReactDOM.render(
<SquiggleEditor
{...props}
onEnvChange={(env) => {
// 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
// be a string
//
// Which are reasonable in most web contexts.
//
// However we're using observable, neither of those things have to be
// true there. div elements can contain the value property, and can have
// the value be any datatype they wish.
//
// This is here to get the 'viewof' part of:
// viewof env = cell('normal(0,1)')
// to work
// @ts-ignore
parent.value = env;
parent.dispatchEvent(new CustomEvent("input"));
if (props.onEnvChange) props.onEnvChange(env);
}}
/>,
parent
);
return parent;
}

View File

@ -1 +1,2 @@
export { SquiggleChart } from './SquiggleChart';
export { SquiggleChart } from "./SquiggleChart";
export { SquiggleEditor, renderSquiggleEditor } from "./SquiggleEditor";

View File

@ -4,14 +4,14 @@
"width": 500,
"height": 200,
"padding": 5,
"data": [{"name": "con"}, {"name": "dis"}],
"data": [{ "name": "con" }, { "name": "dis" }],
"signals": [
{
"name": "mousex",
"description": "x position of mouse",
"update": "0",
"on": [{"events": "mousemove", "update": "1-x()/width"}]
"on": [{ "events": "mousemove", "update": "1-x()/width" }]
},
{
"name": "xscale",
@ -21,7 +21,7 @@
"input": "range",
"min": 0.1,
"max": 1
}
}
},
{
"name": "yscale",
@ -31,90 +31,93 @@
"input": "range",
"min": 0.1,
"max": 1
}
}
}
],
"scales": [{
"name": "xscale",
"type": "pow",
"exponent": {"signal": "xscale"},
"range": "width",
"zero": false,
"nice": false,
"domain": {
"fields": [
{ "data": "con", "field": "x"},
{ "data": "dis", "field": "x"}
"scales": [
{
"name": "xscale",
"type": "pow",
"exponent": { "signal": "xscale" },
"range": "width",
"zero": false,
"nice": false,
"domain": {
"fields": [
{ "data": "con", "field": "x" },
{ "data": "dis", "field": "x" }
]
}
}, {
"name": "yscale",
"type": "pow",
"exponent": {"signal": "yscale"},
"range": "height",
"nice": true,
"zero": true,
"domain": {
"fields": [
{ "data": "con", "field": "y"},
{ "data": "dis", "field": "y"}
},
{
"name": "yscale",
"type": "pow",
"exponent": { "signal": "yscale" },
"range": "height",
"nice": true,
"zero": true,
"domain": {
"fields": [
{ "data": "con", "field": "y" },
{ "data": "dis", "field": "y" }
]
}
}
}
],
"axes": [
{"orient": "bottom", "scale": "xscale", "tickCount": 20},
{"orient": "left", "scale": "yscale"}
{ "orient": "bottom", "scale": "xscale", "tickCount": 20 },
{ "orient": "left", "scale": "yscale" }
],
"marks": [
{
"type": "area",
"from": {"data": "con"},
"from": { "data": "con" },
"encode": {
"enter": {
"tooltip": {"signal": "datum.cdf"}
"tooltip": { "signal": "datum.cdf" }
},
"update": {
"x": {"scale": "xscale", "field": "x"},
"y": {"scale": "yscale", "field": "y"},
"y2": {"scale": "yscale", "value": 0},
"x": { "scale": "xscale", "field": "x" },
"y": { "scale": "yscale", "field": "y" },
"y2": { "scale": "yscale", "value": 0 },
"fill": {
"signal": "{gradient: 'linear', x1: 1, y1: 1, x2: 0, y2: 1, stops: [ {offset: 0.0, color: 'steelblue'}, {offset: clamp(mousex, 0, 1), color: 'steelblue'}, {offset: clamp(mousex, 0, 1), color: 'blue'}, {offset: 1.0, color: 'blue'} ] }"
},
"interpolate": {"value": "monotone"},
"fillOpacity": {"value": 1}
"signal": "{gradient: 'linear', x1: 1, y1: 1, x2: 0, y2: 1, stops: [ {offset: 0.0, color: '#11ac8f'}, {offset: clamp(mousex, 0, 1), color: '#11ac8f'}, {offset: clamp(mousex, 0, 1), color: '#1b6fac'}, {offset: 1.0, color: '#1b6fac'} ] }",
"color": "#000"
},
"interpolate": { "value": "monotone" },
"fillOpacity": { "value": 1 }
}
}
},
{
"type": "rect",
"from": {"data": "dis"},
"from": { "data": "dis" },
"encode": {
"enter": {
"y2": {"scale": "yscale", "value": 0},
"width": {"value": 1}
"y2": { "scale": "yscale", "value": 0 },
"width": { "value": 1 }
},
"update": {
"x": {"scale": "xscale", "field": "x"},
"y": {"scale": "yscale", "field": "y"}
"x": { "scale": "xscale", "field": "x" },
"y": { "scale": "yscale", "field": "y" }
}
}
},
{
"type": "symbol",
"from": {"data": "dis"},
"from": { "data": "dis" },
"encode": {
"enter": {
"shape": {"value": "circle"},
"width": {"value": 5},
"tooltip": {"signal": "datum.y"}
"shape": { "value": "circle" },
"width": { "value": 5 },
"tooltip": { "signal": "datum.y" }
},
"update": {
"x": {"scale": "xscale", "field": "x"},
"y": {"scale": "yscale", "field": "y"}
"x": { "scale": "xscale", "field": "x" },
"y": { "scale": "yscale", "field": "y" }
}
}
}

View File

@ -1,8 +1,8 @@
import { Meta } from '@storybook/addon-docs';
import { Meta } from "@storybook/addon-docs";
<Meta title="Squiggle/Introduction" />
This is the component library for Squiggle. All of these components are react
This is the component library for Squiggle. All of these components are react
components, and can be used in any application that you see fit.
Currently, the only component that is provided is the SquiggleChart component.

View File

@ -1,9 +1,9 @@
import { SquiggleChart } from '../SquiggleChart'
import { Canvas, Meta, Story, Props } from '@storybook/addon-docs';
import { SquiggleChart } from "../SquiggleChart";
import { Canvas, Meta, Story, Props } from "@storybook/addon-docs";
<Meta title="Squiggle/SquiggleChart" component={ SquiggleChart } />
<Meta title="Squiggle/SquiggleChart" component={SquiggleChart} />
export const Template = SquiggleChart
export const Template = SquiggleChart;
# Squiggle Chart
@ -19,53 +19,62 @@ could be continuous, discrete or mixed.
## Distributions
An example of a normal distribution is:
<Canvas>
<Story
name="Normal"
args={{
squiggleString: "normal(5,2)"
}}>
squiggleString: "normal(5,2)",
}}
>
{Template.bind({})}
</Story>
</Canvas>
An example of a Discrete distribution is:
<Canvas>
<Story
name="Discrete"
args={{
squiggleString: "mm(0, 1, [0.5, 0.5])"
}}>
squiggleString: "mm(0, 1, [0.5, 0.5])",
}}
>
{Template.bind({})}
</Story>
</Canvas>
An example of a Mixed distribution is:
<Canvas>
<Story
name="Mixed"
args={{
squiggleString: "mm(0, 5 to 10, [0.5, 0.5])"
}}>
squiggleString: "mm(0, 5 to 10, [0.5, 0.5])",
}}
>
{Template.bind({})}
</Story>
</Canvas>
## Constants
A constant is a simple number as a result. This has special formatting rules
to allow large and small numbers being printed cleanly.
<Canvas>
<Story
name="Constant"
args={{
squiggleString: "500000 * 5000000"
}}>
squiggleString: "500000 * 5000000",
}}
>
{Template.bind({})}
</Story>
</Canvas>
## Functions
Finally, a function can be returned, and this shows how the distribution changes
over the axis between x = 0 and 10.
@ -73,8 +82,9 @@ over the axis between x = 0 and 10.
<Story
name="Function"
args={{
squiggleString: "f(x) = normal(x,x)\nf"
}}>
squiggleString: "f(x) = normal(x,x)\nf",
}}
>
{Template.bind({})}
</Story>
</Canvas>

View File

@ -1,5 +1,8 @@
{
"compilerOptions": {
"paths": {
"@quri/squiggle-lang": ["../squiggle-lang/src/js"]
},
"module": "commonjs",
"jsx": "react",
"resolveJsonModule": true,
@ -7,12 +10,19 @@
"esModuleInterop": true,
"removeComments": true,
"preserveConstEnums": true,
"composite": true,
"outDir": "./dist",
"declarationDir": "./dist",
"declaration": true,
"sourceMap": true
},
"files": ["src/spec-distributions.json","src/spec-percentiles.json"],
"target": "ES6",
"include": ["src/**/*"],
"exclude": ["node_modules", "**/*.spec.ts"]
"include": ["src/**/*", "src/*"],
"exclude": ["node_modules", "**/*.spec.ts"],
"references": [
{
"path": "../squiggle-lang"
}
]
}

View File

@ -1,26 +1,38 @@
const path = require('path');
const path = require("path");
module.exports = {
mode: 'production',
entry: './src/index.ts',
mode: "production",
devtool: "source-map",
entry: "./src/index.ts",
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
loader: "ts-loader",
options: { projectReferences: true },
exclude: /node_modules/,
},
],
},
resolve: {
extensions: ['.js', '.tsx', '.ts'],
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
library: {
name: 'squiggle_components',
type: 'umd',
extensions: [".js", ".tsx", ".ts"],
alias: {
"@quri/squiggle-lang": path.resolve(__dirname, '../squiggle-lang/src/js')
},
},
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "dist"),
library: {
name: "squiggle_components",
type: "umd",
},
},
devServer: {
static: {
directory: path.join(__dirname, "public"),
},
compress: true,
port: 9000,
},
};

View File

@ -1,9 +1,11 @@
# TODO: REVIVE PLAYGROUND.
# Squiggle Playground
This repository contains the squiggle playground, a small web interface
for playing around with squiggle concepts.
It depends on `@squiggle/components` and `@squiggle/lang` so both of them will
It depends on `@quri/squiggle-components` and `@quri/squiggle-lang` so both of them will
need to be packaged for this to work. This can be done from the root directory
with

View File

@ -18,15 +18,15 @@
"antd": "^4.18.5",
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.2",
"binary-search-tree": "0.2.6",
"css-loader": "^6.6.0",
"gh-pages": "2.2.0",
"jstat": "1.9.2",
"lenses-ppx": "5.1.0",
"css-loader": "^6.7.1",
"gh-pages": "3.2.3",
"jstat": "1.9.5",
"lenses-ppx": "6.1.10",
"less": "3.10.3",
"lodash": "4.17.15",
"mathjs": "5.10.3",
"mathjs": "10.4.1",
"moduleserve": "0.9.1",
"moment": "2.24.0",
"moment": "2.29.1",
"pdfast": "^0.2.0",
"rationale": "0.2.0",
"react": "17.0.2",
@ -35,12 +35,12 @@
"react-use": "^17.3.2",
"react-vega": "^7.4.4",
"vega": "*",
"vega-embed": "6.6.0",
"vega-embed": "6.20.8",
"vega-lite": "*"
},
"devDependencies": {
"@emotion/babel-plugin": "^11.7.2",
"@parcel/core": "^2.3.2",
"@parcel/core": "^2.4.0",
"@types/react": "^17.0.39",
"autoprefixer": "^10.4.2",
"docsify": "^4.12.2",

View File

@ -1,5 +0,0 @@
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
name = "squiggle";
buildInputs = with pkgs; [ yarn yarn2nix nodePackages.npm ];
}

View File

@ -1,5 +1,5 @@
import { FC, useState } from "react"
import { SquiggleChart } from "@squiggle/components"
import { SquiggleChart } from "@quri/squiggle-components"
import { CodeEditor } from "./CodeEditor"
import { Form, Input, Card, Row, Col } from "antd"
import { css } from '@emotion/react'

View File

@ -1,9 +1,28 @@
# Squiggle language
# Build for development
We assume that you ran `yarn` at the monorepo level.
``` sh
yarn build
```
`yarn bundle` is needed for a deployment.
Other:
``` sh
yarn start # listens to files and recompiles at every mutation
yarn test
yarn test:watch # keeps an active session and runs all tests at every mutation
```
# TODO: clean up this README.md
# Squiggle Language
Squiggle is a language for representing probability distributions, as well as
functions that return probability distributions. Its original intended use is
for improving epistemics around EA decisions.
This package, @squiggle/lang, contains the core language of squiggle. The main
This package, @quri/squiggle-lang, contains the core language of squiggle. The main
feature revolves around evaluating squiggle expressions. Currently the package
only exports a single function, named "run", which from a squiggle string returns
an object representing the result of the evaluation.
@ -62,3 +81,9 @@ complicated, as it has to return either a number, or a distribution, or even
a representation of a function of distributions. Currently the export is simply
the generated type that rescript creates, and can be quite confusing. We therefore
highly recommend the use of typescript when creating tests or using this package.
## Potential Issues
If you experiment with generating different types of .gen.ts files and similar, note that they won't be caught by git (because they are in .gitignore). Make sure you delete these extra files, once they are unecessary.
```
rm src/rescript/**/*.gen.ts
```

View File

@ -1,19 +1,34 @@
import { run } from '../src/js/index';
describe("A simple result", () => {
let testRun = (x: string) => {
let result = run(x)
if(result.tag == 'Ok'){
return { tag: 'Ok', value: result.value.exports }
}
else {
return result
}
}
describe("Simple calculations and results", () => {
test("mean(normal(5,2))", () => {
expect(run("mean(normal(5,2))")).toEqual({ tag: 'Ok', value: [ { NAME: 'Float', VAL: 5 } ] })
expect(testRun("mean(normal(5,2))")).toEqual({ tag: 'Ok', value: [ { NAME: 'Float', VAL: 5 } ] })
})
test("10+10", () => {
let foo = run("10 + 10")
let foo = testRun("10 + 10")
expect(foo).toEqual({ tag: 'Ok', value: [ { NAME: 'Float', VAL: 20 } ] })
})
})
describe("Log function", () => {
test("log(1) = 0", () => {
let foo = run("log(1)")
let foo = testRun("log(1)")
expect(foo).toEqual({ tag: 'Ok', value: [ { NAME: 'Float', VAL: 0} ]})
})
})
describe("Multimodal too many weights error", () => {
test("mm(0,0,[0,0,0])", () => {
let foo = run("mm(0,0,[0,0,0])")
let foo = testRun("mm(0,0,[0,0,0])")
expect(foo).toEqual({ "tag": "Error", "value": "Function multimodal error: Too many weights provided" })
})
});

View File

@ -1,5 +1,5 @@
{
"name": "@squiggle/lang",
"name": "@quri/squiggle-lang",
"reason": {},
"sources": [
{

View File

@ -1,19 +1,16 @@
{
"name": "@quri/squiggle-lang",
"version": "0.2.2",
"homepage": "https://foretold-app.github.io/estiband/",
"homepage": "https://squiggle-language.com",
"scripts": {
"build": "rescript build -with-deps",
"bundle": "tsc && webpack",
"bundle": "webpack",
"start": "rescript build -w -with-deps",
"clean": "rescript clean",
"test:reducer": "jest --testPathPattern '.*__tests__/Reducer.*'",
"test": "jest",
"test:ci": "yarn jest ./__tests__/Lodash__test.re",
"watch:test": "jest --watchAll",
"watch:s": "yarn jest -- Converter_test --watch",
"package": "tsc",
"ci": "yarn build && yarn package"
"test:watch": "jest --watchAll",
"all": "yarn build && yarn bundle && yarn test"
},
"keywords": [
"Rescript"

View File

@ -1,5 +0,0 @@
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
name = "squiggle";
buildInputs = with pkgs; [ yarn yarn2nix nodePackages.npm ];
}

View File

@ -1,6 +1,6 @@
import {runAll} from '../rescript/ProgramEvaluator.gen';
import type { Inputs_SamplingInputs_t as SamplingInputs } from '../rescript/ProgramEvaluator.gen';
export type { SamplingInputs }
import type { Inputs_SamplingInputs_t as SamplingInputs, exportEnv, exportType, exportDistribution} from '../rescript/ProgramEvaluator.gen';
export type { SamplingInputs, exportEnv, exportDistribution }
export type {t as DistPlus} from '../rescript/pointSetDist/DistPlus.gen';
export let defaultSamplingInputs : SamplingInputs = {
@ -9,7 +9,9 @@ export let defaultSamplingInputs : SamplingInputs = {
pointDistLength : 1000
}
export function run(squiggleString : string, samplingInputs? : SamplingInputs) {
export function run(squiggleString : string, samplingInputs? : SamplingInputs, environment?: exportEnv) : { tag: "Ok"; value: exportType }
| { tag: "Error"; value: string } {
let si : SamplingInputs = samplingInputs ? samplingInputs : defaultSamplingInputs
return runAll(squiggleString, si)
let env : exportEnv = environment ? environment : []
return runAll(squiggleString, si, env)
}

View File

@ -36,12 +36,20 @@ module Inputs = {
}
}
type exportType = [
type exportDistribution = [
| #DistPlus(DistPlus.t)
| #Float(float)
| #Function((float) => Belt.Result.t<DistPlus.t,string>)
]
type exportEnv = array<(string, ASTTypes.node)>
type exportType = {
environment : exportEnv,
exports: array<exportDistribution>
}
module Internals = {
let addVariable = (
{samplingInputs, squiggleString, environment}: Inputs.inputs,
@ -71,135 +79,125 @@ module Internals = {
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<string>, 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<string>, 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.R.fmap(r => (ins.contents.environment, r)))
}
)
|> E.A.O.concatSomes
|> E.A.R.firstErrorOrOpen
|> 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))
let outputToDistPlus = (inputs: Inputs.inputs, pointSetDist: PointSetTypes.pointSetDist) =>
DistPlus.make(~pointSetDist, ~squiggleString=Some(inputs.squiggleString), ())
}
let renderIfNeeded = (inputs: Inputs.inputs, node: ASTTypes.node): result<
ASTTypes.node,
string,
> =>
node |> (
x =>
switch x {
| #Normalize(_) as n
| #SymbolicDist(_) as n =>
#Render(n)
|> Internals.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 rec returnDist = (functionInfo : (array<string>, 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,
node: ASTTypes.node,
): result<exportType, string> =>
node
|> 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(Internals.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<string>, ASTTypes.node),
fnInputs,
) => {
let output = AST.runFunction(
Internals.makeInputs(inputs),
inputs.environment,
fnInputs,
fn,
)
output |> E.R.bind(_, coersionToExportedTypes(inputs, inputs.environment))
}
let rec mapM = (f, xs) =>
switch xs {
| [] => Ok([])
| arr =>
switch f(arr[0]) {
| Error(err) => Error(err)
| Ok(val) =>
switch mapM(f, Belt.Array.sliceToEnd(arr, 1)) {
| Error(err) => Error(err)
| Ok(restList) => Ok(Belt.Array.concat([val], restList))
}
}
}
let evaluateProgram = (inputs: Inputs.inputs) =>
inputs
|> Internals.inputsToLeaf
|> E.R.bind(_, xs => mapM(((a, b)) => coersionToExportedTypes(inputs, a, b), xs))
@genType
let runAll = (squiggleString: string, samplingInputs: Inputs.SamplingInputs.t) => {
let runAll : (string, Inputs.SamplingInputs.t, exportEnv) => result<exportType,string> =
(squiggleString, samplingInputs, environment) => {
let inputs = Inputs.make(
~samplingInputs,
~squiggleString,
~environment=[]->Belt.Map.String.fromArray,
~environment=Belt.Map.String.fromArray(environment),
(),
)
let response1 = evaluateProgram(inputs);
response1
Internals.inputsToLeaf(inputs)
}

View File

@ -1,3 +1,4 @@
@genType
type rec hash = array<(string, node)>
and node = [
| #SymbolicDist(SymbolicDistTypes.symbolicDist)
@ -229,4 +230,4 @@ module SamplingDistribution = {
pointSetDist |> E.R.fmap(r => #Normalize(#RenderedDist(r)))
})
}
}
}

View File

@ -22,7 +22,7 @@ let makeSymbolicFromTwoFloats = (name, fn) =>
~inputTypes=[#Float, #Float],
~run=x =>
switch x {
| [#Float(a), #Float(b)] => Ok(#SymbolicDist(fn(a, b)))
| [#Float(a), #Float(b)] => fn(a, b) |> E.R.fmap(r => (#SymbolicDist(r)))
| e => wrongInputsError(e)
},
(),
@ -35,7 +35,7 @@ let makeSymbolicFromOneFloat = (name, fn) =>
~inputTypes=[#Float],
~run=x =>
switch x {
| [#Float(a)] => Ok(#SymbolicDist(fn(a)))
| [#Float(a)] => fn(a) |> E.R.fmap(r => #SymbolicDist(r))
| e => wrongInputsError(e)
},
(),

View File

@ -2,7 +2,10 @@ open SymbolicDistTypes
module Normal = {
type t = normal
let make = (mean, stdev): symbolicDist => #Normal({mean: mean, stdev: stdev})
let make = (mean: float, stdev: float): result<symbolicDist,string> =>
stdev > 0.0
? Ok(#Normal({mean: mean, stdev: stdev}))
: Error("Standard deviation of normal distribution must be larger than 0")
let pdf = (x, t: t) => Jstat.Normal.pdf(x, t.mean, t.stdev)
let cdf = (x, t: t) => Jstat.Normal.cdf(x, t.mean, t.stdev)
@ -45,10 +48,12 @@ module Normal = {
module Exponential = {
type t = exponential
let make = (rate: float): symbolicDist =>
#Exponential({
rate: rate,
})
let make = (rate: float): result<symbolicDist,string> =>
rate > 0.0
? Ok(#Exponential({
rate: rate,
}))
: Error("Exponential distributions mean must be larger than 0")
let pdf = (x, t: t) => Jstat.Exponential.pdf(x, t.rate)
let cdf = (x, t: t) => Jstat.Exponential.cdf(x, t.rate)
let inv = (p, t: t) => Jstat.Exponential.inv(p, t.rate)
@ -84,7 +89,10 @@ module Triangular = {
module Beta = {
type t = beta
let make = (alpha, beta) => #Beta({alpha: alpha, beta: beta})
let make = (alpha, beta) =>
alpha > 0.0 && beta > 0.0
? Ok(#Beta({alpha: alpha, beta: beta}))
: Error("Beta distribution parameters must be positive")
let pdf = (x, t: t) => Jstat.Beta.pdf(x, t.alpha, t.beta)
let cdf = (x, t: t) => Jstat.Beta.cdf(x, t.alpha, t.beta)
let inv = (p, t: t) => Jstat.Beta.inv(p, t.alpha, t.beta)
@ -95,7 +103,10 @@ module Beta = {
module Lognormal = {
type t = lognormal
let make = (mu, sigma) => #Lognormal({mu: mu, sigma: sigma})
let make = (mu, sigma) =>
sigma > 0.0
? Ok(#Lognormal({mu: mu, sigma: sigma}))
: Error("Lognormal standard deviation must be larger than 0")
let pdf = (x, t: t) => Jstat.Lognormal.pdf(x, t.mu, t.sigma)
let cdf = (x, t: t) => Jstat.Lognormal.cdf(x, t.mu, t.sigma)
let inv = (p, t: t) => Jstat.Lognormal.inv(p, t.mu, t.sigma)
@ -110,11 +121,16 @@ module Lognormal = {
#Lognormal({mu: mu, sigma: sigma})
}
let fromMeanAndStdev = (mean, stdev) => {
let variance = Js.Math.pow_float(~base=stdev, ~exp=2.0)
let meanSquared = Js.Math.pow_float(~base=mean, ~exp=2.0)
let mu = Js.Math.log(mean) -. 0.5 *. Js.Math.log(variance /. meanSquared +. 1.0)
let sigma = Js.Math.pow_float(~base=Js.Math.log(variance /. meanSquared +. 1.0), ~exp=0.5)
#Lognormal({mu: mu, sigma: sigma})
if stdev > 0.0 {
let variance = Js.Math.pow_float(~base=stdev, ~exp=2.0)
let meanSquared = Js.Math.pow_float(~base=mean, ~exp=2.0)
let mu = Js.Math.log(mean) -. 0.5 *. Js.Math.log(variance /. meanSquared +. 1.0)
let sigma = Js.Math.pow_float(~base=Js.Math.log(variance /. meanSquared +. 1.0), ~exp=0.5)
Ok(#Lognormal({mu: mu, sigma: sigma}))
}
else {
Error("Lognormal standard deviation must be larger than 0")
}
}
let multiply = (l1, l2) => {
@ -137,7 +153,11 @@ module Lognormal = {
module Uniform = {
type t = uniform
let make = (low, high) => #Uniform({low: low, high: high})
let make = (low, high) =>
high > low
? Ok(#Uniform({low: low, high: high}))
: Error("High must be larger than low")
let pdf = (x, t: t) => Jstat.Uniform.pdf(x, t.low, t.high)
let cdf = (x, t: t) => Jstat.Uniform.cdf(x, t.low, t.high)
let inv = (p, t: t) => Jstat.Uniform.inv(p, t.low, t.high)

View File

@ -31,6 +31,7 @@ type triangular = {
high: float,
}
@genType
type symbolicDist = [
| #Normal(normal)
| #Beta(beta)

View File

@ -25,61 +25,61 @@ module Uniform = {
type beta
module Beta = {
@module("jstat") @scope("uniform") external pdf: (float, float, float) => float = "pdf"
@module("jstat") @scope("uniform") external cdf: (float, float, float) => float = "cdf"
@module("jstat") @scope("uniform") external inv: (float, float, float) => float = "inv"
@module("jstat") @scope("uniform") external sample: (float, float) => float = "sample"
@module("jstat") @scope("uniform") external mean: (float, float) => float = "mean"
@module("jstat") @scope("beta") external pdf: (float, float, float) => float = "pdf"
@module("jstat") @scope("beta") external cdf: (float, float, float) => float = "cdf"
@module("jstat") @scope("beta") external inv: (float, float, float) => float = "inv"
@module("jstat") @scope("beta") external sample: (float, float) => float = "sample"
@module("jstat") @scope("beta") external mean: (float, float) => float = "mean"
}
module Exponential = {
@module("jstat") @scope("uniform") external pdf: (float, float) => float = "pdf"
@module("jstat") @scope("uniform") external cdf: (float, float) => float = "cdf"
@module("jstat") @scope("uniform") external inv: (float, float) => float = "inv"
@module("jstat") @scope("uniform") external sample: (float) => float = "sample"
@module("jstat") @scope("uniform") external mean: (float) => float = "mean"
@module("jstat") @scope("exponential") external pdf: (float, float) => float = "pdf"
@module("jstat") @scope("exponential") external cdf: (float, float) => float = "cdf"
@module("jstat") @scope("exponential") external inv: (float, float) => float = "inv"
@module("jstat") @scope("exponential") external sample: (float) => float = "sample"
@module("jstat") @scope("exponential") external mean: (float) => float = "mean"
}
module Cauchy = {
@module("jstat") @scope("uniform") external pdf: (float, float, float) => float = "pdf"
@module("jstat") @scope("uniform") external cdf: (float, float, float) => float = "cdf"
@module("jstat") @scope("uniform") external inv: (float, float, float) => float = "inv"
@module("jstat") @scope("uniform") external sample: (float, float) => float = "sample"
@module("jstat") @scope("uniform") external mean: (float, float) => float = "mean"
@module("jstat") @scope("cauchy") external pdf: (float, float, float) => float = "pdf"
@module("jstat") @scope("cauchy") external cdf: (float, float, float) => float = "cdf"
@module("jstat") @scope("cauchy") external inv: (float, float, float) => float = "inv"
@module("jstat") @scope("cauchy") external sample: (float, float) => float = "sample"
@module("jstat") @scope("cauchy") external mean: (float, float) => float = "mean"
}
module Triangular = {
@module("jstat") @scope("uniform") external pdf: (float, float, float, float) => float = "pdf"
@module("jstat") @scope("uniform") external cdf: (float, float, float, float) => float = "cdf"
@module("jstat") @scope("uniform") external inv: (float, float, float, float) => float = "inv"
@module("jstat") @scope("uniform") external sample: (float, float, float) => float = "sample"
@module("jstat") @scope("uniform") external mean: (float, float, float) => float = "mean"
@module("jstat") @scope("triangular") external pdf: (float, float, float, float) => float = "pdf"
@module("jstat") @scope("triangular") external cdf: (float, float, float, float) => float = "cdf"
@module("jstat") @scope("triangular") external inv: (float, float, float, float) => float = "inv"
@module("jstat") @scope("triangular") external sample: (float, float, float) => float = "sample"
@module("jstat") @scope("triangular") external mean: (float, float, float) => float = "mean"
}
module Pareto = {
@module("jstat") @scope("uniform") external pdf: (float, float, float) => float = "pdf"
@module("jstat") @scope("uniform") external cdf: (float, float, float) => float = "cdf"
@module("jstat") @scope("uniform") external inv: (float, float, float) => float = "inv"
@module("jstat") @scope("pareto") external pdf: (float, float, float) => float = "pdf"
@module("jstat") @scope("pareto") external cdf: (float, float, float) => float = "cdf"
@module("jstat") @scope("pareto") external inv: (float, float, float) => float = "inv"
}
module Poisson = {
@module("jstat") @scope("uniform") external pdf: (float, float) => float = "pdf"
@module("jstat") @scope("uniform") external cdf: (float, float) => float = "cdf"
@module("jstat") @scope("uniform") external sample: (float) => float = "sample"
@module("jstat") @scope("uniform") external mean: (float) => float = "mean"
@module("jstat") @scope("poisson") external pdf: (float, float) => float = "pdf"
@module("jstat") @scope("poisson") external cdf: (float, float) => float = "cdf"
@module("jstat") @scope("poisson") external sample: (float) => float = "sample"
@module("jstat") @scope("poisson") external mean: (float) => float = "mean"
}
module Weibull = {
@module("jstat") @scope("uniform") external pdf: (float, float, float) => float = "pdf"
@module("jstat") @scope("uniform") external cdf: (float, float,float ) => float = "cdf"
@module("jstat") @scope("uniform") external sample: (float,float) => float = "sample"
@module("jstat") @scope("uniform") external mean: (float,float) => float = "mean"
@module("jstat") @scope("weibull") external pdf: (float, float, float) => float = "pdf"
@module("jstat") @scope("weibull") external cdf: (float, float,float ) => float = "cdf"
@module("jstat") @scope("weibull") external sample: (float,float) => float = "sample"
@module("jstat") @scope("weibull") external mean: (float,float) => float = "mean"
}
module Binomial = {
@module("jstat") @scope("uniform") external pdf: (float, float, float) => float = "pdf"
@module("jstat") @scope("uniform") external cdf: (float, float,float ) => float = "cdf"
@module("jstat") @scope("binomial") external pdf: (float, float, float) => float = "pdf"
@module("jstat") @scope("binomial") external cdf: (float, float,float ) => float = "cdf"
}
@module("jstat") external sum: array<float> => float = "sum"

View File

@ -1,5 +1,6 @@
// This file has no dependencies. It's used outside of the interpreter, but the interpreter depends on it.
@genType
type algebraicOperation = [
| #Add
| #Multiply
@ -7,6 +8,7 @@ type algebraicOperation = [
| #Divide
| #Exponentiate
]
@genType
type pointwiseOperation = [#Add | #Multiply | #Exponentiate]
type scaleOperation = [#Multiply | #Exponentiate | #Log]
type distToFloatOperation = [
@ -109,4 +111,4 @@ module Truncate = {
let right = right |> E.O.dimap(Js.Float.toString, () => "inf")
j`truncate($nodeToString, $left, $right)`
}
}
}

View File

@ -9,7 +9,8 @@
"sourceMap": true,
"outDir": "./dist",
"declarationDir": "./dist",
"declaration": true
"declaration": true,
"composite": true
},
"target": "ES6",
"include": ["src/**/*"],

View File

@ -4,38 +4,32 @@ This website is built using [Docusaurus 2](https://docusaurus.io/), a modern sta
### Installation
```
$ yarn
``` sh
yarn
```
### Local Development
```
$ yarn start
``` sh
yarn start
```
This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
### Build
```
$ yarn build
``` sh
yarn build
```
This command generates static content into the `build` directory and can be served using any static contents hosting service.
### Deployment
### Clean
Using SSH:
```
$ USE_SSH=true yarn deploy
Clean up the build artefacts.
``` sh
yarn clean
```
Not using SSH:
```
$ GIT_USER=<Your GitHub username> yarn deploy
```
If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.
# TODO: unify formatting across `packages/*/README.md`
# TODO: build docs in `ci.yaml`.

View File

@ -1,3 +1,6 @@
module.exports = {
presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
presets: [
require.resolve('@docusaurus/core/lib/babel/preset'),
["@babel/preset-react", { "runtime": "automatic" }]
],
};

View File

@ -0,0 +1,114 @@
---
sidebar_position: 7
---
import { SquiggleEditor } from '../src/components/SquiggleEditor'
# Squiggle Functions Reference
## Distributions
### Normal distribution
The `normal(mean, sd)` function creates a normal distribution with the given mean
and standard deviation.
<SquiggleEditor initialSquiggleString="normal(5, 1)" />
### Uniform distribution
The `uniform(low, high)` function creates a uniform distribution between the
two given numbers.
<SquiggleEditor initialSquiggleString="uniform(3, 7)" />
### 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.
<SquiggleEditor initialSquiggleString="lognormal(0, 0.7)" />
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.
<SquiggleEditor initialSquiggleString="2 to 10" />
Furthermore, it's also possible to create a lognormal from it's actual mean
and standard deviation, using `lognormalFromMeanAndStdDev`.
<SquiggleEditor initialSquiggleString="lognormalFromMeanAndStdDev(20, 10)" />
### Beta distribution
The `beta(a, b)` function creates a beta distribution with parameters a and b:
<SquiggleEditor initialSquiggleString="beta(20, 20)" />
### Exponential distribution
The `exponential(mean)` function creates an exponential distribution with the given
mean.
<SquiggleEditor initialSquiggleString="exponential(1)" />
### The Triangular distribution
The `triangular(a,b,c)` function creates a triangular distribution with lower
bound a, mode b and upper bound c.
<SquiggleEditor initialSquiggleString="triangular(1, 2, 4)" />
### Multimodal distriutions
The multimodal 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.
<SquiggleEditor initialSquiggleString="mm(uniform(0,1), normal(1,1), [0.5, 0.5])" />
It's possible to create discrete distributions using this method.
<SquiggleEditor initialSquiggleString="mm(0, 1, [0.2,0.8])" />
As well as mixed distributions:
<SquiggleEditor initialSquiggleString="mm(3, 8, 1 to 10, [0.2, 0.3, 0.5])" />
## Other Functions
### PDF of a distribution
The `pdf(distribution, x)` function returns the density of a distribution at the
given point x.
<SquiggleEditor initialSquiggleString="pdf(normal(0,1),0)" />
### Inverse of a distribution
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`.
<SquiggleEditor initialSquiggleString="inv(normal(0,1),0.5)" />
### CDF of a distribution
The `cdf(distribution,x)` gives the cumulative probability of the distribution
or all values lower than x. It is the inverse of `inv`.
<SquiggleEditor initialSquiggleString="cdf(normal(0,1),0)" />
### Mean of a distribution
The `mean(distribution)` function gives the mean (expected value) of a distribution.
<SquiggleEditor initialSquiggleString="mean(normal(5, 10))" />
### Sampling a distribution
The `sample(distribution)` samples a given distribution.
<SquiggleEditor initialSquiggleString="sample(normal(0, 10))" />

View File

@ -1,5 +1,5 @@
---
sidebar_position: 3
sidebar_position: 4
---
# Future Features
@ -77,5 +77,34 @@ Right now, Monte Carlo simulations are totally random. It would be nicer to be a
- Possibly a decent web GUI (a much more advanced playground).
- A VS Code extention and similar.
## Fixes
## Bugs
- Discrete distributions are particularly buggy. Try ``mm(1,2,3,4,5,6,7,8,9,10) .* (5 to 8)``
## New Functions
### Distributions
```js
cauchy()
pareto()
metalog()
```
Possibly change mm to mix, or mx(). Also, change input format, maybe to mx([a,b,c], [a,b,c]).
### Functions
```js
samples(distribution, n)
toPdf(distribution)
toCdf(distribution)
toHash(distribution)
trunctate(distribution, leftValue, rightValue)
leftTrunctate(distribution, leftValue)
rightTrunctate(distribution, rightValue)
distributionFromSamples(array, params)
distributionFromPoints()
distributionFromHash()
```

View File

@ -0,0 +1,10 @@
---
sidebar_position: 1
---
# Squiggle
Squiggle is a language for writing calculations under uncertainty. It has use
cases in forecasting and writing better evaluations.
The best way to get started with Squiggle is to [try it out yourself](https://playground.squiggle-language.com/).

View File

@ -1,16 +1,36 @@
---
sidebar_position: 2
sidebar_position: 3
---
# Javascript Library
# Javascript Libraries
There's a very simple javscript library for Squiggle here: https://www.npmjs.com/package/squiggle-experimental.
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)
You can see it live on this Observable page: [https://observablehq.com/d/a99e822870c4ca5f](https://observablehq.com/d/a99e822870c4ca5f).
Types are available for both packages.
## Squiggle Language
## Simple Example
```
let squiggle = require("squiggle-experimental@0.1.9/dist/index.js")
squiggle.runMePlease("3 + normal(50,1))
```
The `@quri/squiggle-lang` package exports a single function, `run`, which given
a string of Squiggle code, will execute the code and return any exports and the
environment created from the squiggle code.
`run` has two optional arguments. The first optional argument allows you to set
sampling settings for Squiggle when representing distributions. The second optional
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
this library to help navigate the return type.
## Squiggle Components
The `@quri/squiggle-components` package offers several components and utilities
for people who want to embed Squiggle components into websites. This documentation
relies on `@quri/squiggle-components` frequently.
We host [a storybook](https://components.squiggle-language.com/) with details
and usage of each of the components made available.

View File

@ -1,67 +0,0 @@
---
sidebar_position: 1
---
# Squiggle Language
## Distributions
```js
normal(a,b)
uniform(a,b)
lognormal(a,b)
lognormalFromMeanAndStdDev(mean, stdev)
beta(a,b)
exponential(a)
triangular(a,b,c)
mm(a,b,c, [1,2,3]) //todo: change to mix, or mx(). Also, change input format, maybe to mx([a,b,c], [a,b,c]).
cauchy() //todo
pareto() //todo
metalog() //todo
```
## Functions
```js
pdf(distribution, float)
inv(distribution, float)
cdf(distribution, float)
mean(distribution)
sample(distribution)
scaleExp(distribution, float)
scaleMultiply(distribution, float)
scaleLog(distribution, float)
samples(distribution, n) //todo
toPdf(distribution) //todo
toCdf(distribution) //todo
toHash(distribution) //todo. Make hash of content, like, {xs:[], ys:[]}
trunctate(distribution, leftValue, rightValue) //todo
leftTrunctate(distribution, leftValue) //todo
rightTrunctate(distribution, rightValue) //todo
distributionFromSamples(array, params) //todo
distributionFromPoints() //todo
distributionFromHash() //todo
log() //todo
```
## Example Functions
```js
ozzie_estimate(t) = lognormal({mean: 3 + (t+.1)^2.5, stdev: 8})
nuño_estimate(t) = lognormal({mean: 3 + (t+.1)^2, stdev: 10})
combined(t) = mm(ozzie_estimate(t) .+ nuño_estimate(t))
combined
```
```js
us_economy_2018 = (10.5 to 10.9)T
growth_rate = 1.08 to 1.2
us_economy(t) = us_economy_2018 * (growth_rate^t)
us_population_2019 = 320M to 330M
us_population_growth_rate = 1.01 to 1.1
us_population(t) = us_population_2019 * (us_population_growth_rate^t)
gdp_per_person(t) = us_economy(t)/us_population(t)
gdp_per_person
gdp_per_person
```

View File

@ -0,0 +1,36 @@
---
sidebar_position: 2
---
import { SquiggleEditor } from '../src/components/SquiggleEditor'
# Squiggle Language
The squiggle language has a very simple syntax. The best way to get to understand
it is by simply looking at examples.
## Basic Language
As an example:
<SquiggleEditor initialSquiggleString={`value_of_work = 10 to 70
value_of_work`} />
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!
An export is rendered to the output view so you can see your result.
the exports can be expressions, such as:
<SquiggleEditor initialSquiggleString="normal(0,1)" />
## Functions
Squiggle supports functions, including the rendering of functions:
<SquiggleEditor initialSquiggleString={`ozzie_estimate(t) = lognormal({mean: 3 + (t+.1)^2.5, stdev: 8})
ozzie_estimate
`} />

View File

@ -1,5 +1,5 @@
---
sidebar_position: 4
sidebar_position: 5
---
# Three Formats of Distributions

View File

@ -3,18 +3,35 @@
const lightCodeTheme = require('prism-react-renderer/themes/github');
const darkCodeTheme = require('prism-react-renderer/themes/dracula');
const path = require('path');
/** @type {import('@docusaurus/types').Config} */
const config = {
title: 'Squiggle (alpha)',
tagline: "Scorable programming, for use by forecasters",
url: 'https://squiggle-documentation.netlify.app',
tagline: "Estimation language for forecasters",
url: 'https://squiggle-language.com',
baseUrl: '/',
onBrokenLinks: 'throw',
onBrokenMarkdownLinks: 'warn',
favicon: 'img/favicon.ico',
organizationName: 'QURI', // Usually your GitHub org/user name.
projectName: 'Squiggle', // Usually your repo name.
organizationName: 'QURIResearch', // Usually your GitHub org/user name.
projectName: 'squiggle', // Usually your repo name.
plugins: [
() => ({
configureWebpack(config, isServer, utils, content) {
return {
resolve: {
alias : {
"@quri/squiggle-components": path.resolve(__dirname, "../components/src"),
"@quri/squiggle-lang": path.resolve(__dirname, "../squiggle-lang/src/js")
}
}
};
}
})
],
presets: [
[
@ -51,7 +68,7 @@ const config = {
items: [
{
type: 'doc',
docId: 'Language',
docId: 'Introduction',
position: 'left',
label: 'Documentation',
},

File diff suppressed because it is too large Load Diff

View File

@ -3,19 +3,14 @@
"version": "0.0.0",
"private": true,
"scripts": {
"docusaurus": "docusaurus",
"start": "docusaurus start",
"build": "docusaurus build",
"swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy",
"clear": "docusaurus clear",
"serve": "docusaurus serve",
"write-translations": "docusaurus write-translations",
"write-heading-ids": "docusaurus write-heading-ids"
"clean": "docusaurus clear",
"all": "yarn build"
},
"dependencies": {
"@docusaurus/core": "2.0.0-beta.15",
"@docusaurus/preset-classic": "2.0.0-beta.15",
"@docusaurus/core": "2.0.0-beta.17",
"@docusaurus/preset-classic": "2.0.0-beta.17",
"@mdx-js/react": "^1.6.21",
"clsx": "^1.1.1",
"prism-react-renderer": "^1.2.1",

View File

@ -0,0 +1,13 @@
import BrowserOnly from '@docusaurus/BrowserOnly';
export function SquiggleEditor(props) {
return (
<BrowserOnly fallback={<div>Loading...</div>}>
{() => {
const LibComponent =
require('@quri/squiggle-components').SquiggleEditor;
return <LibComponent {...props} />;
}}
</BrowserOnly>
);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +0,0 @@
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
name = "squiggle-root";
buildInputs = with pkgs; [ nodePackages.yarn ];
}