From 017cbe7274d6888f931b82805946b843abd1af5f Mon Sep 17 00:00:00 2001 From: Quinn Date: Wed, 20 Apr 2022 11:22:10 -0400 Subject: [PATCH 01/15] hotfix: `cauchy` in binary dist constructors --- .../ReducerInterface/ReducerInterface_GenericDistribution.res | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res index f7381f07..b20eb168 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res @@ -182,7 +182,7 @@ let dispatchToGenericOutput = (call: ExpressionValue.functionCall): option< ->E.R.bind(r => r(f1)) ->SymbolicConstructors.symbolicResultToOutput | ( - ("normal" | "uniform" | "beta" | "lognormal" | "to") as fnName, + ("normal" | "uniform" | "beta" | "lognormal" | "cauchy" | "to") as fnName, [EvNumber(f1), EvNumber(f2)], ) => SymbolicConstructors.twoFloat(fnName) From 0540fef63a38db2549e63a579557dcd5b3838422 Mon Sep 17 00:00:00 2001 From: Quinn Date: Wed, 20 Apr 2022 11:39:00 -0400 Subject: [PATCH 02/15] Update ReducerInterface_GenericDistribution.res --- .../ReducerInterface/ReducerInterface_GenericDistribution.res | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res index b20eb168..a15c4082 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res @@ -149,6 +149,7 @@ module SymbolicConstructors = { | "uniform" => Ok(SymbolicDist.Uniform.make) | "beta" => Ok(SymbolicDist.Beta.make) | "lognormal" => Ok(SymbolicDist.Lognormal.make) + | "cauchy" => Ok(SymbolicDist.Cauchy.make) | "to" => Ok(SymbolicDist.From90thPercentile.make) | _ => Error("Unreachable state") } From 5875880c06c1341d077b121f5c988d4c3344d2b2 Mon Sep 17 00:00:00 2001 From: Quinn Date: Wed, 20 Apr 2022 11:44:33 -0400 Subject: [PATCH 03/15] nested `Ok` because `cauchy` requires no input validation --- .../ReducerInterface/ReducerInterface_GenericDistribution.res | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res index a15c4082..8a379882 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res @@ -149,7 +149,7 @@ module SymbolicConstructors = { | "uniform" => Ok(SymbolicDist.Uniform.make) | "beta" => Ok(SymbolicDist.Beta.make) | "lognormal" => Ok(SymbolicDist.Lognormal.make) - | "cauchy" => Ok(SymbolicDist.Cauchy.make) + | "cauchy" => Ok(Ok(SymbolicDist.Cauchy.make)) | "to" => Ok(SymbolicDist.From90thPercentile.make) | _ => Error("Unreachable state") } From 4631c183d9bd8ef0aba1ceb78eab16abcf9f0c3d Mon Sep 17 00:00:00 2001 From: Quinn Date: Wed, 20 Apr 2022 11:49:41 -0400 Subject: [PATCH 04/15] wrap `#Cauchy` in `Ok` --- .../src/rescript/Distributions/SymbolicDist/SymbolicDist.res | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res b/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res index da722036..c67c2bba 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res +++ b/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res @@ -86,7 +86,7 @@ module Exponential = { module Cauchy = { type t = cauchy - let make = (local, scale): symbolicDist => #Cauchy({local: local, scale: scale}) + let make = (local, scale): symbolicDist => Ok(#Cauchy({local: local, scale: scale})) let pdf = (x, t: t) => Jstat.Cauchy.pdf(x, t.local, t.scale) let cdf = (x, t: t) => Jstat.Cauchy.cdf(x, t.local, t.scale) let inv = (p, t: t) => Jstat.Cauchy.inv(p, t.local, t.scale) From cbd93b7e79ea3d51f9b26cc5121b750b7d293f9b Mon Sep 17 00:00:00 2001 From: Quinn Date: Wed, 20 Apr 2022 11:50:08 -0400 Subject: [PATCH 05/15] back out of double `Ok` --- .../ReducerInterface/ReducerInterface_GenericDistribution.res | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res index 8a379882..a15c4082 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res @@ -149,7 +149,7 @@ module SymbolicConstructors = { | "uniform" => Ok(SymbolicDist.Uniform.make) | "beta" => Ok(SymbolicDist.Beta.make) | "lognormal" => Ok(SymbolicDist.Lognormal.make) - | "cauchy" => Ok(Ok(SymbolicDist.Cauchy.make)) + | "cauchy" => Ok(SymbolicDist.Cauchy.make) | "to" => Ok(SymbolicDist.From90thPercentile.make) | _ => Error("Unreachable state") } From c101cdac1817801853b0deb22aa1c32bad8acc44 Mon Sep 17 00:00:00 2001 From: Quinn Date: Wed, 20 Apr 2022 11:54:17 -0400 Subject: [PATCH 06/15] update signature for `result` --- .../src/rescript/Distributions/SymbolicDist/SymbolicDist.res | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res b/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res index c67c2bba..dcc9bd5e 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res +++ b/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res @@ -86,7 +86,8 @@ module Exponential = { module Cauchy = { type t = cauchy - let make = (local, scale): symbolicDist => Ok(#Cauchy({local: local, scale: scale})) + let make = (local, scale): result => + Ok(#Cauchy({local: local, scale: scale})) let pdf = (x, t: t) => Jstat.Cauchy.pdf(x, t.local, t.scale) let cdf = (x, t: t) => Jstat.Cauchy.cdf(x, t.local, t.scale) let inv = (p, t: t) => Jstat.Cauchy.inv(p, t.local, t.scale) From 76a0f254ea16c9b519c29ca96fda1c0a562555b4 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Wed, 20 Apr 2022 11:55:56 -0400 Subject: [PATCH 07/15] removed pull request trigger from codeql analysis --- .github/workflows/codeql-analysis.yml | 7 - .parcelrc | 6 - packages/website/docs/Features/Functions.mdx | 169 +++++++++++++++---- packages/website/docs/Internal/Invariants.md | 20 ++- 4 files changed, 158 insertions(+), 44 deletions(-) delete mode 100644 .parcelrc diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index c7750177..2aef3ae3 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -18,13 +18,6 @@ on: - production - staging - develop - pull_request: - # The branches below must be a subset of the branches above - branches: - - master - - production - - staging - - develop schedule: - cron: "42 19 * * 0" diff --git a/.parcelrc b/.parcelrc deleted file mode 100644 index f5a5c2d5..00000000 --- a/.parcelrc +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": "@parcel/config-default", - "transformers": { - "*.res": ["@parcel/transformer-raw"] - } -} diff --git a/packages/website/docs/Features/Functions.mdx b/packages/website/docs/Features/Functions.mdx index 7e0d1fc6..5e09004c 100644 --- a/packages/website/docs/Features/Functions.mdx +++ b/packages/website/docs/Features/Functions.mdx @@ -1,69 +1,101 @@ --- +title: Functions reference sidebar_position: 7 --- import { SquiggleEditor } from "../../src/components/SquiggleEditor"; -# Squiggle Functions Reference +_The source of truth for this document is [this file of code](https://github.com/quantified-uncertainty/squiggle/blob/develop/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res)_ -## Distributions +# Inventory distributions -### Normal distribution +We provide starter distributions, computed symbolically. + +## Normal distribution The `normal(mean, sd)` function creates a normal distribution with the given mean and standard deviation. -### Uniform distribution +### Validity +- `sd > 0` + +## Uniform distribution The `uniform(low, high)` function creates a uniform distribution between the two given numbers. -### Lognormal distribution +### Validity +- `low < high` + +## Lognormal distribution The `lognormal(mu, sigma)` returns the log of a normal distribution with parameters -mu and sigma. The log of lognormal(mu, sigma) is a normal distribution with parameters -mean mu and standard deviation sigma. +`mu` and `sigma`. The log of `lognormal(mu, sigma)` is a normal distribution with mean `mu` and standard deviation `sigma`. -An alternative format is also available. The "to" notation creates a lognormal +An alternative format is also available. The `to` notation creates a lognormal distribution with a 90% confidence interval between the two numbers. We add this convinience as lognormal distributions are commonly used in practice. +### Future feature: Furthermore, it's also possible to create a lognormal from it's actual mean and standard deviation, using `lognormalFromMeanAndStdDev`. -### Beta distribution +### Validity +- `sigma > 0` +- In `x to y` notation, `x < y` -The `beta(a, b)` function creates a beta distribution with parameters a and b: +## Beta distribution + +The `beta(a, b)` function creates a beta distribution with parameters `a` and `b`: -### Exponential distribution +### Validity +- `a > 0` +- `b > 0` +- Empirically, we have noticed that numerical instability arises when `a < 1` or `b < 1` -The `exponential(mean)` function creates an exponential distribution with the given -mean. +## Exponential distribution + +The `exponential(rate)` function creates an exponential distribution with the given +rate. -### The Triangular distribution +### Validity +- `rate > 0` + +## Triangular distribution The `triangular(a,b,c)` function creates a triangular distribution with lower -bound a, mode b and upper bound c. +bound `a`, mode `b` and upper bound `c`. + +### Validity +- `a < b < c` -### Multimodal distriutions +## Scalar (constant dist) -The multimodal function combines 2 or more other distributions to create a weighted +Squiggle, when the context is right, automatically casts a float to a constant distribution. + +# Operating on distributions + +Here are the ways we combine distributions. + +## Mixture of distributions + +The `mx` 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. @@ -78,37 +110,114 @@ As well as mixed distributions: -## Other Functions +An alias of `mx` is `mixture` -### PDF of a distribution +### Validity +Using javascript's variable arguments notation, consider `mx(...dists, weights)`: +- `dists.length == weights.length` -The `pdf(distribution, x)` function returns the density of a distribution at the +## Addition (horizontal right shift) + + + +## Subtraction (horizontal left shift) + + + +## Multiplication (??) + + + +## Division (???) + + + +## Taking the base `e` exponential + + + +## Taking the base `e` and base `10` logarithm + + + + + +### Validity +- See [the current discourse](https://github.com/quantified-uncertainty/squiggle/issues/304) + +# Standard functions on distributions + +## Probability density function + +The `pdf(dist, x)` function returns the density of a distribution at the given point x. -### Inverse of a distribution +### Validity +- `x` must be a scalar +- `dist` must be 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`. +## Cumulative density function - - -### CDF of a distribution - -The `cdf(distribution,x)` gives the cumulative probability of the distribution +The `cdf(dist, x)` gives the cumulative probability of the distribution or all values lower than x. It is the inverse of `inv`. -### Mean of a distribution +### Validity +- `x` must be a scalar +- `dist` must be a distribution + +## Inverse CDF + +The `inv(dist, prob)` gives the value x or which the probability for all values +lower than x is equal to prob. It is the inverse of `cdf`. + + + +### Validity +- `prob` must be a scalar (please only put it in `(0,1)`) +- `dist` must be a distribution + +## Mean The `mean(distribution)` function gives the mean (expected value) of a distribution. -### Sampling a distribution +## Sampling a distribution The `sample(distribution)` samples a given distribution. + +# Normalization + +Some distribution operations (like horizontal shift) return an unnormalized distriibution. + +We provide a `normalize` function + + +### Valdity +- Input to `normalize` must be a dist + +We provide a predicate `isNormalized`, for when we have simple control flow + + + +### Validity +- Input to `isNormalized` must be a dist + +# Convert any distribution to a sample set distribution + +`toSampleSet` has two signatures + +It is unary when you use an internal hardcoded number of samples + + + +And binary when you provide a number of samples (truncated) + + + diff --git a/packages/website/docs/Internal/Invariants.md b/packages/website/docs/Internal/Invariants.md index c1c0fd79..633c904b 100644 --- a/packages/website/docs/Internal/Invariants.md +++ b/packages/website/docs/Internal/Invariants.md @@ -1,5 +1,5 @@ --- -title: Statistical properties of algebraic combinations of distributions for property testing. +title: Invariants urlcolor: blue author: - Nuño Sempere @@ -7,8 +7,12 @@ author: abstract: This document outlines some properties about algebraic combinations of distributions. It is meant to facilitate property tests for [Squiggle](https://squiggle-language.com/), an estimation language for forecasters. So far, we are focusing on the means, the standard deviation and the shape of the pdfs. --- +Invariants to check with property tests. + _This document right now is normative and aspirational, not a description of the testing that's currently done_. +# Algebraic combinations + The academic keyword to search for in relation to this document is "[algebra of random variables](https://wikiless.org/wiki/Algebra_of_random_variables?lang=en)". Squiggle doesn't yet support getting the standard deviation, denoted by $\sigma$, but such support could yet be added. ## Means and standard deviations @@ -118,6 +122,20 @@ TODO TODO +# `pdf`, `cdf`, and `inv` + +With $\forall dist, pdf := x \mapsto \texttt{pdf}(dist, x) \land cdf := x \mapsto \texttt{cdf}(dist, x) \land inv := p \mapsto \texttt{inv}(dist, p)$, + +## `cdf` and `inv` are inverses +$$ +\forall x \in (0,1), cdf(inv(x)) = x \land \forall x \in \texttt{dom}(cdf), x = inv(cdf(x)) +$$ + +## The codomain of `cdf` equals the open interval `(0,1)` equals the codomain of `pdf` +$$ +\texttt{cod}(cdf) = (0,1) = \texttt{cod}(pdf) +$$ + # To do: - Provide sources or derivations, useful as this document becomes more complicated From 9cdffc309beeecc4fa3b516200d3a71581c1617b Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Wed, 20 Apr 2022 12:09:57 -0400 Subject: [PATCH 08/15] functions reference is almost done --- packages/website/.prettierignore | 2 + packages/website/docs/Features/Functions.mdx | 62 +++++++++++++++----- packages/website/docs/Internal/Invariants.md | 8 ++- 3 files changed, 55 insertions(+), 17 deletions(-) create mode 100644 packages/website/.prettierignore diff --git a/packages/website/.prettierignore b/packages/website/.prettierignore new file mode 100644 index 00000000..d858cd65 --- /dev/null +++ b/packages/website/.prettierignore @@ -0,0 +1,2 @@ +.docusaurus +build diff --git a/packages/website/docs/Features/Functions.mdx b/packages/website/docs/Features/Functions.mdx index 5e09004c..5cba7c11 100644 --- a/packages/website/docs/Features/Functions.mdx +++ b/packages/website/docs/Features/Functions.mdx @@ -9,7 +9,7 @@ _The source of truth for this document is [this file of code](https://github.com # Inventory distributions -We provide starter distributions, computed symbolically. +We provide starter distributions, computed symbolically. ## Normal distribution @@ -19,6 +19,7 @@ and standard deviation. ### Validity + - `sd > 0` ## Uniform distribution @@ -29,6 +30,7 @@ two given numbers. ### Validity + - `low < high` ## Lognormal distribution @@ -44,13 +46,15 @@ this convinience as lognormal distributions are commonly used in practice. -### Future feature: +### Future feature: + Furthermore, it's also possible to create a lognormal from it's actual mean and standard deviation, using `lognormalFromMeanAndStdDev`. ### Validity + - `sigma > 0` - In `x to y` notation, `x < y` @@ -61,6 +65,7 @@ The `beta(a, b)` function creates a beta distribution with parameters `a` and `b ### Validity + - `a > 0` - `b > 0` - Empirically, we have noticed that numerical instability arises when `a < 1` or `b < 1` @@ -73,6 +78,7 @@ rate. ### Validity + - `rate > 0` ## Triangular distribution @@ -81,17 +87,18 @@ The `triangular(a,b,c)` function creates a triangular distribution with lower bound `a`, mode `b` and upper bound `c`. ### Validity + - `a < b < c` ## Scalar (constant dist) -Squiggle, when the context is right, automatically casts a float to a constant distribution. +Squiggle, when the context is right, automatically casts a float to a constant distribution. # Operating on distributions -Here are the ways we combine distributions. +Here are the ways we combine distributions. ## Mixture of distributions @@ -110,10 +117,12 @@ As well as mixed distributions: -An alias of `mx` is `mixture` +An alias of `mx` is `mixture` ### Validity -Using javascript's variable arguments notation, consider `mx(...dists, weights)`: + +Using javascript's variable arguments notation, consider `mx(...dists, weights)`: + - `dists.length == weights.length` ## Addition (horizontal right shift) @@ -124,7 +133,7 @@ Using javascript's variable arguments notation, consider `mx(...dists, weights)` -## Multiplication (??) +## Multiplication (??) @@ -136,16 +145,17 @@ Using javascript's variable arguments notation, consider `mx(...dists, weights)` -## Taking the base `e` and base `10` logarithm +## Taking the base `e` and base `10` logarithm ### Validity + - See [the current discourse](https://github.com/quantified-uncertainty/squiggle/issues/304) -# Standard functions on distributions +# Standard functions on distributions ## Probability density function @@ -155,6 +165,7 @@ given point x. ### Validity + - `x` must be a scalar - `dist` must be a distribution @@ -166,6 +177,7 @@ or all values lower than x. It is the inverse of `inv`. ### Validity + - `x` must be a scalar - `dist` must be a distribution @@ -177,6 +189,7 @@ lower than x is equal to prob. It is the inverse of `cdf`. ### Validity + - `prob` must be a scalar (please only put it in `(0,1)`) - `dist` must be a distribution @@ -194,19 +207,19 @@ The `sample(distribution)` samples a given distribution. # Normalization -Some distribution operations (like horizontal shift) return an unnormalized distriibution. +Some distribution operations (like horizontal shift) return an unnormalized distriibution. We provide a `normalize` function -### Valdity -- Input to `normalize` must be a dist +### Valdity - Input to `normalize` must be a dist -We provide a predicate `isNormalized`, for when we have simple control flow +We provide a predicate `isNormalized`, for when we have simple control flow -### Validity +### Validity + - Input to `isNormalized` must be a dist # Convert any distribution to a sample set distribution @@ -221,3 +234,24 @@ And binary when you provide a number of samples (truncated) +# `inspect` + +You may like to debug by right clicking your browser and using the _inspect_ functionality on the webpage, and viewing the _console_ tab. Then, wrap your squiggle output with `inspect` to log an internal representation. + + + +Save for a logging side effect, `inspect` does nothing to input and returns it. + +# Truncate + +You can cut off from the left + + + +You can cut off from the right + + + +You can cut off from both sides + + diff --git a/packages/website/docs/Internal/Invariants.md b/packages/website/docs/Internal/Invariants.md index 633c904b..85fd56be 100644 --- a/packages/website/docs/Internal/Invariants.md +++ b/packages/website/docs/Internal/Invariants.md @@ -7,7 +7,7 @@ author: abstract: This document outlines some properties about algebraic combinations of distributions. It is meant to facilitate property tests for [Squiggle](https://squiggle-language.com/), an estimation language for forecasters. So far, we are focusing on the means, the standard deviation and the shape of the pdfs. --- -Invariants to check with property tests. +Invariants to check with property tests. _This document right now is normative and aspirational, not a description of the testing that's currently done_. @@ -122,16 +122,18 @@ TODO TODO -# `pdf`, `cdf`, and `inv` +# `pdf`, `cdf`, and `inv` -With $\forall dist, pdf := x \mapsto \texttt{pdf}(dist, x) \land cdf := x \mapsto \texttt{cdf}(dist, x) \land inv := p \mapsto \texttt{inv}(dist, p)$, +With $\forall dist, pdf := x \mapsto \texttt{pdf}(dist, x) \land cdf := x \mapsto \texttt{cdf}(dist, x) \land inv := p \mapsto \texttt{inv}(dist, p)$, ## `cdf` and `inv` are inverses + $$ \forall x \in (0,1), cdf(inv(x)) = x \land \forall x \in \texttt{dom}(cdf), x = inv(cdf(x)) $$ ## The codomain of `cdf` equals the open interval `(0,1)` equals the codomain of `pdf` + $$ \texttt{cod}(cdf) = (0,1) = \texttt{cod}(pdf) $$ From 806ba864465f4760dca5e0801f16f0680111467f Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Wed, 20 Apr 2022 12:13:31 -0400 Subject: [PATCH 09/15] fixed lint --- packages/squiggle-lang/.prettierignore | 2 ++ .../src/rescript/Distributions/SymbolicDist/SymbolicDist.res | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/squiggle-lang/.prettierignore b/packages/squiggle-lang/.prettierignore index 30674e4d..bc218d9b 100644 --- a/packages/squiggle-lang/.prettierignore +++ b/packages/squiggle-lang/.prettierignore @@ -2,3 +2,5 @@ dist lib *.bs.js *.gen.tsx +.nyc_output/ +coverage/ diff --git a/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res b/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res index dcc9bd5e..a6eda12b 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res +++ b/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res @@ -86,8 +86,9 @@ module Exponential = { module Cauchy = { type t = cauchy - let make = (local, scale): result => - Ok(#Cauchy({local: local, scale: scale})) + let make = (local, scale): result => Ok( + #Cauchy({local: local, scale: scale}), + ) let pdf = (x, t: t) => Jstat.Cauchy.pdf(x, t.local, t.scale) let cdf = (x, t: t) => Jstat.Cauchy.cdf(x, t.local, t.scale) let inv = (p, t: t) => Jstat.Cauchy.inv(p, t.local, t.scale) From b9ad87fda7555d81efa9b387ac37a39110d38f90 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Wed, 20 Apr 2022 12:15:07 -0400 Subject: [PATCH 10/15] squiggle syntax in `initialSquiggleString` for `trunctate` --- packages/website/docs/Features/Functions.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/website/docs/Features/Functions.mdx b/packages/website/docs/Features/Functions.mdx index 5cba7c11..2dacd2fe 100644 --- a/packages/website/docs/Features/Functions.mdx +++ b/packages/website/docs/Features/Functions.mdx @@ -254,4 +254,4 @@ You can cut off from the right You can cut off from both sides - + From 15ccf876a64af389d5930afe4793abbf2ed64f75 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Wed, 20 Apr 2022 12:26:03 -0400 Subject: [PATCH 11/15] fixed `/>` bug --- packages/website/docs/Features/Functions.mdx | 22 ++++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/website/docs/Features/Functions.mdx b/packages/website/docs/Features/Functions.mdx index 2dacd2fe..f4fd3aab 100644 --- a/packages/website/docs/Features/Functions.mdx +++ b/packages/website/docs/Features/Functions.mdx @@ -1,5 +1,5 @@ --- -title: Functions reference +title: "Functions reference" sidebar_position: 7 --- @@ -62,7 +62,7 @@ and standard deviation, using `lognormalFromMeanAndStdDev`. The `beta(a, b)` function creates a beta distribution with parameters `a` and `b`: - + ### Validity @@ -75,7 +75,7 @@ The `beta(a, b)` function creates a beta distribution with parameters `a` and `b The `exponential(rate)` function creates an exponential distribution with the given rate. - + ### Validity @@ -127,29 +127,29 @@ Using javascript's variable arguments notation, consider `mx(...dists, weights)` ## Addition (horizontal right shift) - + ## Subtraction (horizontal left shift) - + ## Multiplication (??) - + ## Division (???) - + ## Taking the base `e` exponential - + ## Taking the base `e` and base `10` logarithm - + - + ### Validity @@ -230,7 +230,7 @@ It is unary when you use an internal hardcoded number of samples -And binary when you provide a number of samples (truncated) +And binary when you provide a number of samples (floored) From 9f35039b60d8dda949f0247f240865083f26ee71 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Wed, 20 Apr 2022 13:41:22 -0400 Subject: [PATCH 12/15] second pass; one CR comment --- packages/website/docs/Features/Functions.mdx | 136 ++++++++++++------- packages/website/docs/Internal/Invariants.md | 28 ++-- 2 files changed, 104 insertions(+), 60 deletions(-) diff --git a/packages/website/docs/Features/Functions.mdx b/packages/website/docs/Features/Functions.mdx index f4fd3aab..cfc308ee 100644 --- a/packages/website/docs/Features/Functions.mdx +++ b/packages/website/docs/Features/Functions.mdx @@ -1,5 +1,5 @@ --- -title: "Functions reference" +title: "Functions Reference" sidebar_position: 7 --- @@ -7,33 +7,33 @@ import { SquiggleEditor } from "../../src/components/SquiggleEditor"; _The source of truth for this document is [this file of code](https://github.com/quantified-uncertainty/squiggle/blob/develop/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res)_ -# Inventory distributions +## Inventory distributions We provide starter distributions, computed symbolically. -## Normal distribution +### Normal distribution The `normal(mean, sd)` function creates a normal distribution with the given mean and standard deviation. -### Validity +#### Validity - `sd > 0` -## Uniform distribution +### Uniform distribution The `uniform(low, high)` function creates a uniform distribution between the two given numbers. -### Validity +#### Validity - `low < high` -## Lognormal distribution +### 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 mean `mu` and standard deviation `sigma`. @@ -46,183 +46,227 @@ this convinience as lognormal distributions are commonly used in practice. -### Future feature: +#### Future feature: Furthermore, it's also possible to create a lognormal from it's actual mean and standard deviation, using `lognormalFromMeanAndStdDev`. -### Validity +#### Validity - `sigma > 0` - In `x to y` notation, `x < y` -## Beta distribution +### Beta distribution The `beta(a, b)` function creates a beta distribution with parameters `a` and `b`: -### Validity +#### Validity - `a > 0` - `b > 0` - Empirically, we have noticed that numerical instability arises when `a < 1` or `b < 1` -## Exponential distribution +### Exponential distribution The `exponential(rate)` function creates an exponential distribution with the given rate. -### Validity +#### Validity - `rate > 0` -## Triangular distribution +### Triangular distribution The `triangular(a,b,c)` function creates a triangular distribution with lower bound `a`, mode `b` and upper bound `c`. -### Validity +#### Validity - `a < b < c` -## Scalar (constant dist) +### Scalar (constant dist) Squiggle, when the context is right, automatically casts a float to a constant distribution. -# Operating on distributions +## Operating on distributions Here are the ways we combine distributions. -## Mixture of distributions +### Mixture of distributions -The `mx` function combines 2 or more other distributions to create a weighted +The `mixture` function combines 2 or more other distributions to create a weighted combination of the two. The first positional arguments represent the distributions to be combined, and the last argument is how much to weigh every distribution in the combination. - + It's possible to create discrete distributions using this method. - + As well as mixed distributions: - + -An alias of `mx` is `mixture` +An alias of `mixture` is `mx` -### Validity +#### Validity Using javascript's variable arguments notation, consider `mx(...dists, weights)`: - `dists.length == weights.length` -## Addition (horizontal right shift) +### Addition (horizontal right shift) -## Subtraction (horizontal left shift) +### Subtraction (horizontal left shift) -## Multiplication (??) +### Multiplication (??) -## Division (???) +We also provide concatenation of two distributions as a syntax sugar for `*` + + + +### Division (???) -## Taking the base `e` exponential +### Exponentiation (???) + + + +### Taking the base `e` exponential -## Taking the base `e` and base `10` logarithm +### Taking logarithms -### Validity +Base `x` + +#### Validity + +- `x` must be a scalar - See [the current discourse](https://github.com/quantified-uncertainty/squiggle/issues/304) -# Standard functions on distributions +### Pointwise addition -## Probability density function +**Pointwise operations are done with `PointSetDist` internals rather than `SampleSetDist` internals**. + +TODO: this isn't in the new interpreter/parser yet. + + + +### Pointwise subtraction + +TODO: this isn't in the new interpreter/parser yet. + + + +### Pointwise multiplication + + + +### Pointwise division + + + +### Pointwise exponentiation + + + +### Pointwise logarithm + +TODO: write about the semantics and the case handling re scalar vs. dist and log base. + + + +## Standard functions on distributions + +### Probability density function The `pdf(dist, x)` function returns the density of a distribution at the given point x. -### Validity +#### Validity - `x` must be a scalar - `dist` must be a distribution -## Cumulative density function +### Cumulative density function The `cdf(dist, x)` gives the cumulative probability of the distribution or all values lower than x. It is the inverse of `inv`. -### Validity +#### Validity - `x` must be a scalar - `dist` must be a distribution -## Inverse CDF +### Inverse CDF The `inv(dist, prob)` gives the value x or which the probability for all values lower than x is equal to prob. It is the inverse of `cdf`. -### Validity +#### Validity - `prob` must be a scalar (please only put it in `(0,1)`) - `dist` must be a distribution -## Mean +### Mean The `mean(distribution)` function gives the mean (expected value) of a distribution. -## Sampling a distribution +### Sampling a distribution The `sample(distribution)` samples a given distribution. -# Normalization +## Normalization Some distribution operations (like horizontal shift) return an unnormalized distriibution. We provide a `normalize` function -### Valdity - Input to `normalize` must be a dist +#### Validity - Input to `normalize` must be a dist We provide a predicate `isNormalized`, for when we have simple control flow -### Validity +#### Validity - Input to `isNormalized` must be a dist -# Convert any distribution to a sample set distribution +## Convert any distribution to a sample set distribution `toSampleSet` has two signatures @@ -234,7 +278,7 @@ And binary when you provide a number of samples (floored) -# `inspect` +## `inspect` You may like to debug by right clicking your browser and using the _inspect_ functionality on the webpage, and viewing the _console_ tab. Then, wrap your squiggle output with `inspect` to log an internal representation. @@ -242,7 +286,7 @@ You may like to debug by right clicking your browser and using the _inspect_ fun Save for a logging side effect, `inspect` does nothing to input and returns it. -# Truncate +## Truncate You can cut off from the left diff --git a/packages/website/docs/Internal/Invariants.md b/packages/website/docs/Internal/Invariants.md index 85fd56be..8a89466a 100644 --- a/packages/website/docs/Internal/Invariants.md +++ b/packages/website/docs/Internal/Invariants.md @@ -11,13 +11,13 @@ Invariants to check with property tests. _This document right now is normative and aspirational, not a description of the testing that's currently done_. -# Algebraic combinations +## Algebraic combinations The academic keyword to search for in relation to this document is "[algebra of random variables](https://wikiless.org/wiki/Algebra_of_random_variables?lang=en)". Squiggle doesn't yet support getting the standard deviation, denoted by $\sigma$, but such support could yet be added. -## Means and standard deviations +### Means and standard deviations -### Sums +#### Sums $$ mean(f+g) = mean(f) + mean(g) @@ -33,7 +33,7 @@ $$ mean(normal(a,b) + normal(c,d)) = mean(normal(a+c, \sqrt{b^2 + d^2})) $$ -### Subtractions +#### Subtractions $$ mean(f-g) = mean(f) - mean(g) @@ -43,7 +43,7 @@ $$ \sigma(f-g) = \sqrt{\sigma(f)^2 + \sigma(g)^2} $$ -### Multiplications +#### Multiplications $$ mean(f \cdot g) = mean(f) \cdot mean(g) @@ -53,15 +53,15 @@ $$ \sigma(f \cdot g) = \sqrt{ (\sigma(f)^2 + mean(f)) \cdot (\sigma(g)^2 + mean(g)) - (mean(f) \cdot mean(g))^2} $$ -### Divisions +#### Divisions Divisions are tricky, and in general we don't have good expressions to characterize properties of ratios. In particular, the ratio of two normals is a Cauchy distribution, which doesn't have to have a mean. -## Probability density functions (pdfs) +### Probability density functions (pdfs) Specifying the pdf of the sum/multiplication/... of distributions as a function of the pdfs of the individual arguments can still be done. But it requires integration. My sense is that this is still doable, and I (Nuño) provide some _pseudocode_ to do this. -### Sums +#### Sums Let $f, g$ be two independently distributed functions. Then, the pdf of their sum, evaluated at a point $z$, expressed as $(f + g)(z)$, is given by: @@ -114,31 +114,31 @@ let pdfOfSum = (pdf1, pdf2, cdf1, cdf2, z) => { }; ``` -## Cumulative density functions +### Cumulative density functions TODO -## Inverse cumulative density functions +### Inverse cumulative density functions TODO -# `pdf`, `cdf`, and `inv` +## `pdf`, `cdf`, and `inv` With $\forall dist, pdf := x \mapsto \texttt{pdf}(dist, x) \land cdf := x \mapsto \texttt{cdf}(dist, x) \land inv := p \mapsto \texttt{inv}(dist, p)$, -## `cdf` and `inv` are inverses +### `cdf` and `inv` are inverses $$ \forall x \in (0,1), cdf(inv(x)) = x \land \forall x \in \texttt{dom}(cdf), x = inv(cdf(x)) $$ -## The codomain of `cdf` equals the open interval `(0,1)` equals the codomain of `pdf` +### The codomain of `cdf` equals the open interval `(0,1)` equals the codomain of `pdf` $$ \texttt{cod}(cdf) = (0,1) = \texttt{cod}(pdf) $$ -# To do: +## To do: - Provide sources or derivations, useful as this document becomes more complicated - Provide definitions for the probability density function, exponential, inverse, log, etc. From c2b93831e3e9c1395b2c1910f44101b724256fe2 Mon Sep 17 00:00:00 2001 From: Sam Nolan Date: Wed, 20 Apr 2022 13:51:27 -0400 Subject: [PATCH 13/15] Fix rescript warnings --- README.md | 1 + .../Reducer/Reducer_Expression/Reducer_Expression.res | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ee4d00c8..2f1de7f7 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ The playground depends on the components library which then depends on the langu # Develop For any project in the repo, begin by running `yarn` in the top level + ```sh yarn ``` diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res index 90e79e8e..b1befa5a 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res @@ -44,7 +44,7 @@ let defaultBindings: T.bindings = Belt.Map.String.empty /* Recursively evaluate/reduce the expression (Lisp AST) */ -let rec reduceExpression = (expression: t, bindings: T.bindings): result => { +let reduceExpression = (expression: t, bindings: T.bindings): result => { /* After reducing each level of expression(Lisp AST), we have a value list to evaluate */ @@ -135,6 +135,7 @@ let rec reduceExpression = (expression: t, bindings: T.bindings): resultResult.flatMap(acc => acc->doMacroCall(bindings)) } + | T.EBindings(bindings) => T.EBindings(bindings)->Ok } let rec reduceExpandedExpression = (expression: t): result => @@ -155,6 +156,7 @@ let rec reduceExpression = (expression: t, bindings: T.bindings): resultResult.flatMap(acc => acc->reduceValueList) } + | T.EBindings(bindings) => RETodo("Cannot return bindings")->Error } let rExpandedExpression: result = expression->seekMacros(bindings) From 8b83c9ec844f8a14435b3c076e6dd9fbd5bab533 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Wed, 20 Apr 2022 14:55:14 -0400 Subject: [PATCH 14/15] CR pass thru; cleanup; no more scientific notation --- packages/website/docs/Features/Functions.mdx | 52 +++++++++++++------- packages/website/docs/Internal/Invariants.md | 2 +- 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/packages/website/docs/Features/Functions.mdx b/packages/website/docs/Features/Functions.mdx index cfc308ee..e1564fc6 100644 --- a/packages/website/docs/Features/Functions.mdx +++ b/packages/website/docs/Features/Functions.mdx @@ -42,7 +42,7 @@ The `lognormal(mu, sigma)` returns the log of a normal distribution with paramet An alternative format is also available. The `to` notation creates a lognormal distribution with a 90% confidence interval between the two numbers. We add -this convinience as lognormal distributions are commonly used in practice. +this convenience as lognormal distributions are commonly used in practice. @@ -51,6 +51,8 @@ this convinience as lognormal distributions are commonly used in practice. Furthermore, it's also possible to create a lognormal from it's actual mean and standard deviation, using `lognormalFromMeanAndStdDev`. +TODO: interpreter/parser doesn't provide this in current `develop` branch + #### Validity @@ -62,7 +64,7 @@ and standard deviation, using `lognormalFromMeanAndStdDev`. The `beta(a, b)` function creates a beta distribution with parameters `a` and `b`: - + #### Validity @@ -75,7 +77,7 @@ The `beta(a, b)` function creates a beta distribution with parameters `a` and `b The `exponential(rate)` function creates an exponential distribution with the given rate. - + #### Validity @@ -125,29 +127,39 @@ Using javascript's variable arguments notation, consider `mx(...dists, weights)` - `dists.length == weights.length` -### Addition (horizontal right shift) +### Addition + +A horizontal right shift -### Subtraction (horizontal left shift) +### Subtraction + +A horizontal left shift -### Multiplication (??) +### Multiplication + +TODO: provide intuition pump for the semantics We also provide concatenation of two distributions as a syntax sugar for `*` - + -### Division (???) +### Division + +TODO: provide intuition pump for the semantics -### Exponentiation (???) +### Exponentiation - +TODO: provide intuition pump for the semantics + + ### Taking the base `e` exponential @@ -161,7 +173,8 @@ We also provide concatenation of two distributions as a syntax sugar for `*` Base `x` - + + #### Validity - `x` must be a scalar @@ -255,12 +268,13 @@ Some distribution operations (like horizontal shift) return an unnormalized dist We provide a `normalize` function - + + #### Validity - Input to `normalize` must be a dist We provide a predicate `isNormalized`, for when we have simple control flow - + #### Validity @@ -272,17 +286,17 @@ We provide a predicate `isNormalized`, for when we have simple control flow It is unary when you use an internal hardcoded number of samples - + And binary when you provide a number of samples (floored) - + ## `inspect` You may like to debug by right clicking your browser and using the _inspect_ functionality on the webpage, and viewing the _console_ tab. Then, wrap your squiggle output with `inspect` to log an internal representation. - + Save for a logging side effect, `inspect` does nothing to input and returns it. @@ -290,12 +304,12 @@ Save for a logging side effect, `inspect` does nothing to input and returns it. You can cut off from the left - + You can cut off from the right - + You can cut off from both sides - + diff --git a/packages/website/docs/Internal/Invariants.md b/packages/website/docs/Internal/Invariants.md index 8a89466a..91adf1a1 100644 --- a/packages/website/docs/Internal/Invariants.md +++ b/packages/website/docs/Internal/Invariants.md @@ -1,5 +1,5 @@ --- -title: Invariants +title: Invariants of Probability Distributions urlcolor: blue author: - Nuño Sempere From be6beac944ec10913d3d91acb63447c0fa320124 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Wed, 20 Apr 2022 16:50:43 -0400 Subject: [PATCH 15/15] multiline `initialSquiggleString`s; favicon! --- packages/website/docs/Features/Functions.mdx | 83 +++++++++++++++---- packages/website/docusaurus.config.js | 10 +-- packages/website/src/pages/index.js | 5 +- packages/website/static/img/favicon.ico | Bin 3626 -> 6611 bytes packages/website/static/img/quri-logo.png | Bin 0 -> 20214 bytes 5 files changed, 74 insertions(+), 24 deletions(-) create mode 100644 packages/website/static/img/quri-logo.png diff --git a/packages/website/docs/Features/Functions.mdx b/packages/website/docs/Features/Functions.mdx index e1564fc6..c85a3438 100644 --- a/packages/website/docs/Features/Functions.mdx +++ b/packages/website/docs/Features/Functions.mdx @@ -131,19 +131,31 @@ Using javascript's variable arguments notation, consider `mx(...dists, weights)` A horizontal right shift - + ### Subtraction A horizontal left shift - + ### Multiplication TODO: provide intuition pump for the semantics - + We also provide concatenation of two distributions as a syntax sugar for `*` @@ -153,27 +165,44 @@ We also provide concatenation of two distributions as a syntax sugar for `*` TODO: provide intuition pump for the semantics - + ### Exponentiation TODO: provide intuition pump for the semantics - + ### Taking the base `e` exponential - + ### Taking logarithms - + - + Base `x` - + #### Validity @@ -186,31 +215,55 @@ Base `x` TODO: this isn't in the new interpreter/parser yet. - + ### Pointwise subtraction TODO: this isn't in the new interpreter/parser yet. - + ### Pointwise multiplication - + ### Pointwise division - + ### Pointwise exponentiation - + ### Pointwise logarithm TODO: write about the semantics and the case handling re scalar vs. dist and log base. - + ## Standard functions on distributions diff --git a/packages/website/docusaurus.config.js b/packages/website/docusaurus.config.js index c5d03327..4971b1b0 100644 --- a/packages/website/docusaurus.config.js +++ b/packages/website/docusaurus.config.js @@ -49,7 +49,7 @@ const config = { sidebarPath: require.resolve("./sidebars.js"), // Please change this to your repo. editUrl: - "https://github.com/quantified-uncertainty/squiggle/tree/master/packages/website/", + "https://github.com/quantified-uncertainty/squiggle/tree/develop/packages/website/", remarkPlugins: [math], rehypePlugins: [katex], }, @@ -57,7 +57,7 @@ const config = { showReadingTime: true, // Please change this to your repo. editUrl: - "https://github.com/quantified-uncertainty/squiggle/tree/master/packages/website/", + "https://github.com/quantified-uncertainty/squiggle/tree/develop/packages/website/", }, theme: { customCss: require.resolve("./src/css/custom.css"), @@ -73,7 +73,7 @@ const config = { title: "Squiggle", logo: { alt: "Squiggle Logo", - src: "img/logo.svg", + src: "img/quri-logo.png", }, items: [ { @@ -85,7 +85,7 @@ const config = { { to: "/blog", label: "Blog", position: "left" }, { to: "/playground", label: "Playground", position: "left" }, { - href: "https://github.com/QURIresearch/squiggle", + href: "https://github.com/quantified-uncertainty/squiggle", label: "GitHub", position: "right", }, @@ -103,7 +103,7 @@ const config = { }, { label: "GitHub", - href: "https://github.com/QURIresearch/squiggle", + href: "https://github.com/quantified-uncertainty/squiggle", }, ], }, diff --git a/packages/website/src/pages/index.js b/packages/website/src/pages/index.js index 452c7ef0..4cbddcac 100644 --- a/packages/website/src/pages/index.js +++ b/packages/website/src/pages/index.js @@ -22,10 +22,7 @@ function HomepageHeader() { export default function Home() { const { siteConfig } = useDocusaurusContext(); return ( - +
diff --git a/packages/website/static/img/favicon.ico b/packages/website/static/img/favicon.ico index c01d54bcd39a5f853428f3cd5aa0f383d963c484..d318e2344334b98493b932a60f95c1d389357b68 100644 GIT binary patch literal 6611 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
Jvzps}Nnrwa6}}3DUefLf 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 literal 0 HcmV?d00001