Merge branch 'develop' into reducer-dev

This commit is contained in:
Umur Ozkul 2022-04-14 03:03:08 +02:00
commit de379b6c04
55 changed files with 1276 additions and 404 deletions

View File

@ -1,13 +0,0 @@
---
name: Developer friction when contributing to Squiggle
about: Have a testing-related task? Did your yarn scripts fail? Did the CI diverge from a README? Etc.
labels: "ops & testing"
---
# Description:
# The OS and version, yarn version, etc. in which this came up
_delete this section if testing task_
# Desired behavior

12
.github/ISSUE_TEMPLATE/ops-testing.md vendored Normal file
View File

@ -0,0 +1,12 @@
---
name: Operations and testing
about: Have a testing-related task? Developer friction when contributing to squiggle? Etc.
labels: "ops & testing"
---
# Description:
<!-- delete this section if testing task or otherwise not applicable -->
# The OS and version, yarn version, etc. in which this came up
# Desired behavior

View File

@ -1,6 +1,6 @@
---
name: Regarding the programming language
about: Interpreter, parser, syntax, semantics, and including distributions
name: Regarding the programming language (the `squiggle-lang` package)
about: Anything concerning distributions/numerics, as well as the interpreter, parser, syntax, semantics
labels: "programming language"
---

View File

@ -62,7 +62,10 @@ jobs:
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
- name: Install dependencies
run: yarn
- name: Build rescript
run: cd packages/squiggle-lang && yarn build
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl

View File

@ -60,7 +60,8 @@ Please work against `develop` branch. **Do not** work against `master`.
Autopings are set up: if you are not autopinged, you are welcome to comment, but please do not use the formal review feature, send approvals, rejections, or merges.
# Code Quality
- Aim for at least 8/10* quality in ``/packages/squiggle-lang``, and 7/10 quality in ``/packages/components``.
- Aim for at least 8/10\* quality in `/packages/squiggle-lang`, and 7/10 quality in `/packages/components`.
- If you submit a PR that is under a 7, for some reason, describe the reasoning for this in the PR.
* This quality score is subjective.
@ -74,6 +75,7 @@ Note: Our codebase used to use `|>`, so there's a lot of that in the system. We'
**Don't use anonymous functions with over three lines**
Bad:
```rescript
foo
-> E.O.fmap(r => {
@ -83,7 +85,9 @@ Bad:
r + a + b + c
}
```
Good:
```rescript
let addingFn = (r => {
let a = 34;
@ -101,6 +105,7 @@ We'll try this for one month (ending May 5, 2022), then revisit.
Rescript is clever about function inputs. There's custom syntax for default and optional arguments. In the cases where this applies, use it.
From https://rescript-lang.org/docs/manual/latest/function:
```rescript
// radius can be omitted
let drawCircle = (~color, ~radius=?, ()) => {
@ -114,11 +119,12 @@ let drawCircle = (~color, ~radius=?, ()) => {
**Use named arguments**
If a function is called externally (in a different file), and has either:
1. Two arguments of the same type
2. Three paramaters or more.
**Module naming: Use x_y as module names**
For example: ``Myname_Myproject_Add.res``. Rescript/Ocaml both require files to have unique names, so long names are needed to keep different parts separate from each other.
For example: `Myname_Myproject_Add.res`. Rescript/Ocaml both require files to have unique names, so long names are needed to keep different parts separate from each other.
See [this page](https://dev.to/yawaramin/a-modular-ocaml-project-structure-1ikd) for more information. (Though note that they use two underscores, and we do one. We might refactor that later.
@ -128,8 +134,8 @@ We have some of this in the Reducer code, but generally discourage it.
**Use interface files (.resi) for files with very public interfaces**
### Recommended Rescript resources
- https://dev.to/yawaramin/a-modular-ocaml-project-structure-1ikd
- https://github.com/avohq/reasonml-code-style-guide
- https://cs.brown.edu/courses/cs017/content/docs/reasonml-style.pdf
- https://github.com/ostera/reason-design-patterns/

View File

@ -2,7 +2,9 @@
"private": true,
"name": "squiggle",
"scripts": {
"nodeclean": "rm -r node_modules && rm -r packages/*/node_modules"
"nodeclean": "rm -r node_modules && rm -r packages/*/node_modules",
"format:all": "prettier --write . && cd packages/squiggle-lang && yarn format",
"lint:all": "prettier --check . && cd packages/squiggle-lang && yarn lint:rescript"
},
"devDependencies": {
"prettier": "^2.6.2"

View File

@ -9,7 +9,7 @@
"@types/jest": "^27.4.0",
"@types/lodash": "^4.14.181",
"@types/node": "^17.0.23",
"@types/react": "^18.0.1",
"@types/react": "^18.0.3",
"@types/react-dom": "^18.0.0",
"antd": "^4.19.3",
"cross-env": "^7.0.3",
@ -17,7 +17,7 @@
"react": "^18.0.0",
"react-ace": "9.5.0",
"react-dom": "^18.0.0",
"react-scripts": "5.0.0",
"react-scripts": "5.0.1",
"react-vega": "^7.5.0",
"styled-components": "^5.3.5",
"tsconfig-paths-webpack-plugin": "^3.5.2",

View File

@ -37,7 +37,7 @@ could be continuous, discrete or mixed.
<Story
name="Discrete"
args={{
squiggleString: "mm(0, 1, 3, 5, 8, 10, [0.1, 0.8, 0.5, 0.3, 0.2, 0.1])",
squiggleString: "mx(0, 1, 3, 5, 8, 10, [0.1, 0.8, 0.5, 0.3, 0.2, 0.1])",
}}
>
{Template.bind({})}
@ -51,7 +51,7 @@ could be continuous, discrete or mixed.
name="Mixed"
args={{
squiggleString:
"mm(0, 1, 3, 5, 8, normal(8, 1), [0.1, 0.3, 0.4, 0.35, 0.2, 0.8])",
"mx(0, 1, 3, 5, 8, normal(8, 1), [0.1, 0.3, 0.4, 0.35, 0.2, 0.8])",
}}
>
{Template.bind({})}

View File

@ -130,10 +130,6 @@
},
"encode": {
"enter": {
"y2": {
"scale": "yscale",
"value": 0
},
"width": {
"value": 1
}
@ -146,6 +142,10 @@
"y": {
"scale": "yscale",
"field": "y"
},
"y2": {
"scale": "yscale",
"value": 0
}
}
}
@ -160,7 +160,7 @@
"shape": {
"value": "circle"
},
"size": [{ "value": 30 }],
"size": [{ "value": 100 }],
"tooltip": {
"signal": "datum.y"
}

View File

@ -0,0 +1,4 @@
dist
lib
*.bs.js
*.gen.tsx

View File

@ -4,10 +4,10 @@ open Expect
describe("Bandwidth", () => {
test("nrd0()", () => {
let data = [1., 4., 3., 2.]
expect(SampleSetDist_Bandwidth.nrd0(data)) -> toEqual(0.7625801874014622)
expect(SampleSetDist_Bandwidth.nrd0(data))->toEqual(0.7625801874014622)
})
test("nrd()", () => {
let data = [1., 4., 3., 2.]
expect(SampleSetDist_Bandwidth.nrd(data)) -> toEqual(0.8981499984950554)
expect(SampleSetDist_Bandwidth.nrd(data))->toEqual(0.8981499984950554)
})
})

View File

@ -18,11 +18,9 @@ let {
triangularDist,
exponentialDist,
} = module(GenericDist_Fixtures)
let mkNormal = (mean, stdev) => GenericDist_Types.Symbolic(#Normal({mean: mean, stdev: stdev}))
let {toFloat, toDist, toString, toError} = module(DistributionOperation.Output)
let {toFloat, toDist, toString, toError, fmap} = module(DistributionOperation.Output)
let {run} = module(DistributionOperation)
let {fmap} = module(DistributionOperation.Output)
let run = run(~env)
let outputMap = fmap(~env)
let toExt: option<'a> => 'a = E.O.toExt(

View File

@ -6,6 +6,9 @@ let normalDist: GenericDist_Types.genericDist = normalDist5
let betaDist: GenericDist_Types.genericDist = Symbolic(#Beta({alpha: 2.0, beta: 5.0}))
let lognormalDist: GenericDist_Types.genericDist = Symbolic(#Lognormal({mu: 0.0, sigma: 1.0}))
let cauchyDist: GenericDist_Types.genericDist = Symbolic(#Cauchy({local: 1.0, scale: 1.0}))
let triangularDist: GenericDist_Types.genericDist = Symbolic(#Triangular({low: 1.0, medium: 2.0, high: 3.0}))
let triangularDist: GenericDist_Types.genericDist = Symbolic(
#Triangular({low: 1.0, medium: 2.0, high: 3.0}),
)
let exponentialDist: GenericDist_Types.genericDist = Symbolic(#Exponential({rate: 2.0}))
let uniformDist: GenericDist_Types.genericDist = Symbolic(#Uniform({low: 9.0, high: 10.0}))
let floatDist: GenericDist_Types.genericDist = Symbolic(#Float(1e1))

View File

@ -0,0 +1,368 @@
/*
This file is like a half measure between one-off unit tests and proper invariant validation.
As such, I'm not that excited about it, though it does provide some structure and will alarm us
when things substantially change.
Also, there are some open comments in https://github.com/quantified-uncertainty/squiggle/pull/232 that haven't been addressed.
*/
open Jest
open Expect
open TestHelpers
let {
normalDist5, // mean=5, stdev=2
normalDist10, // mean=10, stdev=2
normalDist20, // mean=20, stdev=2
normalDist, // mean=5; stdev=2
uniformDist, // low=9; high=10
betaDist, // alpha=2; beta=5
lognormalDist, // mu=0; sigma=1
cauchyDist, // local=1; scale=1
triangularDist, // low=1; medium=2; high=3;
exponentialDist, // rate=2
} = module(GenericDist_Fixtures)
let {
algebraicAdd,
algebraicMultiply,
algebraicDivide,
algebraicSubtract,
algebraicLogarithm,
algebraicPower,
} = module(DistributionOperation.Constructors)
let algebraicAdd = algebraicAdd(~env)
let algebraicMultiply = algebraicMultiply(~env)
let algebraicDivide = algebraicDivide(~env)
let algebraicSubtract = algebraicSubtract(~env)
let algebraicLogarithm = algebraicLogarithm(~env)
let algebraicPower = algebraicPower(~env)
describe("(Algebraic) addition of distributions", () => {
describe("mean", () => {
test("normal(mean=5) + normal(mean=20)", () => {
normalDist5
->algebraicAdd(normalDist20)
->E.R2.fmap(GenericDist_Types.Constructors.UsingDists.mean)
->E.R2.fmap(run)
->E.R2.fmap(toFloat)
->E.R.toExn
->expect
->toBe(Some(2.5e1))
})
test("uniform(low=9, high=10) + beta(alpha=2, beta=5)", () => {
// let uniformMean = (9.0 +. 10.0) /. 2.0
// let betaMean = 1.0 /. (1.0 +. 5.0 /. 2.0)
let received =
uniformDist
->algebraicAdd(betaDist)
->E.R2.fmap(GenericDist_Types.Constructors.UsingDists.mean)
->E.R2.fmap(run)
->E.R2.fmap(toFloat)
->E.R.toExn
switch received {
| None => "algebraicAdd has"->expect->toBe("failed")
// This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad.
// sometimes it works with ~digits=2.
| Some(x) => x->expect->toBeSoCloseTo(0.01927225696028752, ~digits=1) // (uniformMean +. betaMean)
}
})
test("beta(alpha=2, beta=5) + uniform(low=9, high=10)", () => {
// let uniformMean = (9.0 +. 10.0) /. 2.0
// let betaMean = 1.0 /. (1.0 +. 5.0 /. 2.0)
let received =
betaDist
->algebraicAdd(uniformDist)
->E.R2.fmap(GenericDist_Types.Constructors.UsingDists.mean)
->E.R2.fmap(run)
->E.R2.fmap(toFloat)
->E.R.toExn
switch received {
| None => "algebraicAdd has"->expect->toBe("failed")
// This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad.
// sometimes it works with ~digits=2.
| Some(x) => x->expect->toBeSoCloseTo(0.019275414920485248, ~digits=1) // (uniformMean +. betaMean)
}
})
})
describe("pdf", () => {
// TEST IS WRONG. SEE STDEV ADDITION EXPRESSION.
testAll(
"(normal(mean=5) + normal(mean=5)).pdf (imprecise)",
list{8e0, 1e1, 1.2e1, 1.4e1},
x => {
let received =
normalDist10 // this should be normal(10, sqrt(8))
->Ok
->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.pdf(d, x))
->E.R2.fmap(run)
->E.R2.fmap(toFloat)
->E.R.toOption
->E.O.flatten
let calculated =
normalDist5
->algebraicAdd(normalDist5)
->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.pdf(d, x))
->E.R2.fmap(run)
->E.R2.fmap(toFloat)
->E.R.toOption
->E.O.flatten
switch received {
| None =>
"this branch occurs when the dispatch to Jstat on trusted input fails."
->expect
->toBe("never")
| Some(x) =>
switch calculated {
| None => "algebraicAdd has"->expect->toBe("failed")
| Some(y) => x->expect->toBeSoCloseTo(y, ~digits=0)
}
}
},
)
test("(normal(mean=10) + normal(mean=10)).pdf(1.9e1)", () => {
let received =
normalDist20
->Ok
->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.pdf(d, 1.9e1))
->E.R2.fmap(run)
->E.R2.fmap(toFloat)
->E.R.toOption
->E.O.flatten
let calculated =
normalDist10
->algebraicAdd(normalDist10)
->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.pdf(d, 1.9e1))
->E.R2.fmap(run)
->E.R2.fmap(toFloat)
->E.R.toOption
->E.O.flatten
switch received {
| None =>
"this branch occurs when the dispatch to Jstat on trusted input fails."
->expect
->toBe("never")
| Some(x) =>
switch calculated {
| None => "algebraicAdd has"->expect->toBe("failed")
| Some(y) => x->expect->toBeSoCloseTo(y, ~digits=1)
}
}
})
test("(uniform(low=9, high=10) + beta(alpha=2, beta=5)).pdf(10)", () => {
let received =
uniformDist
->algebraicAdd(betaDist)
->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.pdf(d, 1e1))
->E.R2.fmap(run)
->E.R2.fmap(toFloat)
->E.R.toExn
switch received {
| None => "algebraicAdd has"->expect->toBe("failed")
// This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad.
// sometimes it works with ~digits=4.
| Some(x) => x->expect->toBeSoCloseTo(0.001978994877226945, ~digits=3)
}
})
test("(beta(alpha=2, beta=5) + uniform(low=9, high=10)).pdf(10)", () => {
let received =
betaDist
->algebraicAdd(uniformDist)
->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.pdf(d, 1e1))
->E.R2.fmap(run)
->E.R2.fmap(toFloat)
->E.R.toExn
switch received {
| None => "algebraicAdd has"->expect->toBe("failed")
// This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad.
// sometimes it works with ~digits=4.
| Some(x) => x->expect->toBeSoCloseTo(0.001978994877226945, ~digits=3)
}
})
})
describe("cdf", () => {
testAll("(normal(mean=5) + normal(mean=5)).cdf (imprecise)", list{6e0, 8e0, 1e1, 1.2e1}, x => {
let received =
normalDist10
->Ok
->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.cdf(d, x))
->E.R2.fmap(run)
->E.R2.fmap(toFloat)
->E.R.toOption
->E.O.flatten
let calculated =
normalDist5
->algebraicAdd(normalDist5)
->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.cdf(d, x))
->E.R2.fmap(run)
->E.R2.fmap(toFloat)
->E.R.toOption
->E.O.flatten
switch received {
| None =>
"this branch occurs when the dispatch to Jstat on trusted input fails."
->expect
->toBe("never")
| Some(x) =>
switch calculated {
| None => "algebraicAdd has"->expect->toBe("failed")
| Some(y) => x->expect->toBeSoCloseTo(y, ~digits=0)
}
}
})
test("(normal(mean=10) + normal(mean=10)).cdf(1.25e1)", () => {
let received =
normalDist20
->Ok
->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.cdf(d, 1.25e1))
->E.R2.fmap(run)
->E.R2.fmap(toFloat)
->E.R.toOption
->E.O.flatten
let calculated =
normalDist10
->algebraicAdd(normalDist10)
->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.cdf(d, 1.25e1))
->E.R2.fmap(run)
->E.R2.fmap(toFloat)
->E.R.toOption
->E.O.flatten
switch received {
| None =>
"this branch occurs when the dispatch to Jstat on trusted input fails."
->expect
->toBe("never")
| Some(x) =>
switch calculated {
| None => "algebraicAdd has"->expect->toBe("failed")
| Some(y) => x->expect->toBeSoCloseTo(y, ~digits=2)
}
}
})
test("(uniform(low=9, high=10) + beta(alpha=2, beta=5)).cdf(10)", () => {
let received =
uniformDist
->algebraicAdd(betaDist)
->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.cdf(d, 1e1))
->E.R2.fmap(run)
->E.R2.fmap(toFloat)
->E.R.toExn
switch received {
| None => "algebraicAdd has"->expect->toBe("failed")
// This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad.
// sometimes it works with ~digits=4.
| Some(x) => x->expect->toBeSoCloseTo(0.0013961779932477507, ~digits=3)
}
})
test("(beta(alpha=2, beta=5) + uniform(low=9, high=10)).cdf(10)", () => {
let received =
betaDist
->algebraicAdd(uniformDist)
->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.cdf(d, 1e1))
->E.R2.fmap(run)
->E.R2.fmap(toFloat)
->E.R.toExn
switch received {
| None => "algebraicAdd has"->expect->toBe("failed")
// This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad.
// sometimes it works with ~digits=4.
| Some(x) => x->expect->toBeSoCloseTo(0.001388898111625753, ~digits=3)
}
})
})
describe("inv", () => {
testAll("(normal(mean=5) + normal(mean=5)).inv (imprecise)", list{5e-2, 4.2e-3, 9e-3}, x => {
let received =
normalDist10
->Ok
->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.inv(d, x))
->E.R2.fmap(run)
->E.R2.fmap(toFloat)
->E.R.toOption
->E.O.flatten
let calculated =
normalDist5
->algebraicAdd(normalDist5)
->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.inv(d, x))
->E.R2.fmap(run)
->E.R2.fmap(toFloat)
->E.R.toOption
->E.O.flatten
switch received {
| None =>
"this branch occurs when the dispatch to Jstat on trusted input fails."
->expect
->toBe("never")
| Some(x) =>
switch calculated {
| None => "algebraicAdd has"->expect->toBe("failed")
| Some(y) => x->expect->toBeSoCloseTo(y, ~digits=-1)
}
}
})
test("(normal(mean=10) + normal(mean=10)).inv(1e-1)", () => {
let received =
normalDist20
->Ok
->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.inv(d, 1e-1))
->E.R2.fmap(run)
->E.R2.fmap(toFloat)
->E.R.toOption
->E.O.flatten
let calculated =
normalDist10
->algebraicAdd(normalDist10)
->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.inv(d, 1e-1))
->E.R2.fmap(run)
->E.R2.fmap(toFloat)
->E.R.toOption
->E.O.flatten
switch received {
| None =>
"this branch occurs when the dispatch to Jstat on trusted input fails."
->expect
->toBe("never")
| Some(x) =>
switch calculated {
| None => "algebraicAdd has"->expect->toBe("failed")
| Some(y) => x->expect->toBeSoCloseTo(y, ~digits=-1)
}
}
})
test("(uniform(low=9, high=10) + beta(alpha=2, beta=5)).inv(2e-2)", () => {
let received =
uniformDist
->algebraicAdd(betaDist)
->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.inv(d, 2e-2))
->E.R2.fmap(run)
->E.R2.fmap(toFloat)
->E.R.toExn
switch received {
| None => "algebraicAdd has"->expect->toBe("failed")
// This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad.
// sometimes it works with ~digits=2.
| Some(x) => x->expect->toBeSoCloseTo(10.927078217530806, ~digits=0)
}
})
test("(beta(alpha=2, beta=5) + uniform(low=9, high=10)).inv(2e-2)", () => {
let received =
betaDist
->algebraicAdd(uniformDist)
->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.inv(d, 2e-2))
->E.R2.fmap(run)
->E.R2.fmap(toFloat)
->E.R.toExn
switch received {
| None => "algebraicAdd has"->expect->toBe("failed")
// This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad.
// sometimes it works with ~digits=2.
| Some(x) => x->expect->toBeSoCloseTo(10.915396627014363, ~digits=0)
}
})
})
})

View File

@ -0,0 +1,140 @@
/*
This is the most basic file in our invariants family of tests.
See document in https://github.com/quantified-uncertainty/squiggle/pull/238 for details
Note: digits parameter should be higher than -4.
*/
open Jest
open Expect
open TestHelpers
let {
algebraicAdd,
algebraicMultiply,
algebraicDivide,
algebraicSubtract,
algebraicLogarithm,
algebraicPower,
} = module(DistributionOperation.Constructors)
let algebraicAdd = algebraicAdd(~env)
let algebraicMultiply = algebraicMultiply(~env)
let algebraicDivide = algebraicDivide(~env)
let algebraicSubtract = algebraicSubtract(~env)
let algebraicLogarithm = algebraicLogarithm(~env)
let algebraicPower = algebraicPower(~env)
describe("Mean", () => {
let digits = -4
let mean = GenericDist_Types.Constructors.UsingDists.mean
let runMean: result<DistributionTypes.genericDist, DistributionTypes.error> => float = distR => {
distR
->E.R2.fmap(mean)
->E.R2.fmap(run)
->E.R2.fmap(toFloat)
->E.R.toExn
->E.O2.toExn("Shouldn't see this because we trust testcase input")
}
let impossiblePath: string => assertion = algebraicOp =>
`${algebraicOp} has`->expect->toEqual("failed")
let distributions = list{
normalMake(0.0, 1e0),
betaMake(2e0, 4e0),
exponentialMake(1.234e0),
uniformMake(7e0, 1e1),
// cauchyMake(1e0, 1e0),
lognormalMake(1e0, 1e0),
triangularMake(1e0, 1e1, 5e1),
Ok(floatMake(1e1)),
}
let combinations = E.L.combinations2(distributions)
let zipDistsDists = E.L.zip(distributions, distributions)
let testOperationMean = (
distOp: (DistributionTypes.genericDist, DistributionTypes.genericDist) => result<DistributionTypes.genericDist, DistributionTypes.error>,
description: string,
floatOp: (float, float) => float,
dist1': result<SymbolicDistTypes.symbolicDist, string>,
dist2': result<SymbolicDistTypes.symbolicDist, string>
) => {
let dist1 = dist1'->E.R2.fmap(x=>DistributionTypes.Symbolic(x))->E.R2.fmap2(s=>DistributionTypes.Other(s))
let dist2 = dist2'->E.R2.fmap(x=>DistributionTypes.Symbolic(x))->E.R2.fmap2(s=>DistributionTypes.Other(s))
let received =
E.R.liftJoin2(distOp, dist1, dist2)
->E.R2.fmap(mean)
->E.R2.fmap(run)
->E.R2.fmap(toFloat)
let expected = floatOp(runMean(dist1), runMean(dist2))
switch received {
| Error(err) => impossiblePath(description)
| Ok(x) =>
switch x {
| None => impossiblePath(description)
| Some(x) => x->expect->toBeSoCloseTo(expected, ~digits)
}
}
}
describe("addition", () => {
let testAdditionMean = testOperationMean(algebraicAdd, "algebraicAdd", \"+.")
testAll("homogeneous addition", zipDistsDists, dists => {
let (dist1, dist2) = dists
testAdditionMean(dist1, dist2)
})
testAll("heterogeneous addition (1)", combinations, dists => {
let (dist1, dist2) = dists
testAdditionMean(dist1, dist2)
})
testAll("heterogeneous addition (commuted of 1 (or; 2))", combinations, dists => {
let (dist1, dist2) = dists
testAdditionMean(dist2, dist1)
})
})
describe("subtraction", () => {
let testSubtractionMean = testOperationMean(algebraicSubtract, "algebraicSubtract", \"-.")
testAll("homogeneous subtraction", zipDistsDists, dists => {
let (dist1, dist2) = dists
testSubtractionMean(dist1, dist2)
})
testAll("heterogeneous subtraction (1)", combinations, dists => {
let (dist1, dist2) = dists
testSubtractionMean(dist1, dist2)
})
testAll("heterogeneous subtraction (commuted of 1 (or; 2))", combinations, dists => {
let (dist1, dist2) = dists
testSubtractionMean(dist2, dist1)
})
})
describe("multiplication", () => {
let testMultiplicationMean = testOperationMean(algebraicMultiply, "algebraicMultiply", \"*.")
testAll("homogeneous subtraction", zipDistsDists, dists => {
let (dist1, dist2) = dists
testMultiplicationMean(dist1, dist2)
})
testAll("heterogeneoous subtraction (1)", combinations, dists => {
let (dist1, dist2) = dists
testMultiplicationMean(dist1, dist2)
})
testAll("heterogeneoous subtraction (commuted of 1 (or; 2))", combinations, dists => {
let (dist1, dist2) = dists
testMultiplicationMean(dist2, dist1)
})
})
})

View File

@ -2,69 +2,64 @@ open Jest
open Expect
open TestHelpers
// TODO: use Normal.make (etc.), but preferably after the new validation dispatch is in.
let mkNormal = (mean, stdev) => GenericDist_Types.Symbolic(#Normal({mean: mean, stdev: stdev}))
let mkBeta = (alpha, beta) => GenericDist_Types.Symbolic(#Beta({alpha: alpha, beta: beta}))
let mkExponential = rate => GenericDist_Types.Symbolic(#Exponential({rate: rate}))
let mkUniform = (low, high) => GenericDist_Types.Symbolic(#Uniform({low: low, high: high}))
let mkCauchy = (local, scale) => GenericDist_Types.Symbolic(#Cauchy({local: local, scale: scale}))
let mkLognormal = (mu, sigma) => GenericDist_Types.Symbolic(#Lognormal({mu: mu, sigma: sigma}))
describe("mixture", () => {
testAll("fair mean of two normal distributions", list{(0.0, 1e2), (-1e1, -1e-4), (-1e1, 1e2), (-1e1, 1e1)}, tup => { // should be property
let (mean1, mean2) = tup
let meanValue = {
run(Mixture([(mkNormal(mean1, 9e-1), 0.5), (mkNormal(mean2, 9e-1), 0.5)]))
-> outputMap(FromDist(ToFloat(#Mean)))
}
meanValue -> unpackFloat -> expect -> toBeSoCloseTo((mean1 +. mean2) /. 2.0, ~digits=-1)
})
testAll(
"weighted mean of a beta and an exponential",
// This would not survive property testing, it was easy for me to find cases that NaN'd out.
list{((128.0, 1.0), 2.0), ((2e-1, 64.0), 16.0), ((1e0, 1e0), 64.0)},
tup => {
let ((alpha, beta), rate) = tup
let betaWeight = 0.25
let exponentialWeight = 0.75
let meanValue = {
run(Mixture(
[
(mkBeta(alpha, beta), betaWeight),
(mkExponential(rate), exponentialWeight)
]
)) -> outputMap(FromDist(ToFloat(#Mean)))
}
let betaMean = 1.0 /. (1.0 +. beta /. alpha)
let exponentialMean = 1.0 /. rate
meanValue
-> unpackFloat
-> expect
-> toBeSoCloseTo(
betaWeight *. betaMean +. exponentialWeight *. exponentialMean,
~digits=-1
"fair mean of two normal distributions",
list{(0.0, 1e2), (-1e1, -1e-4), (-1e1, 1e2), (-1e1, 1e1)},
tup => {
// should be property
let (mean1, mean2) = tup
let meanValue = {
run(Mixture([(mkNormal(mean1, 9e-1), 0.5), (mkNormal(mean2, 9e-1), 0.5)]))->outputMap(
FromDist(ToFloat(#Mean)),
)
}
meanValue->unpackFloat->expect->toBeSoCloseTo((mean1 +. mean2) /. 2.0, ~digits=-1)
},
)
testAll(
"weighted mean of lognormal and uniform",
// Would not survive property tests: very easy to find cases that NaN out.
list{((-1e2,1e1), (2e0,1e0)), ((-1e-16,1e-16), (1e-8,1e0)), ((0.0,1e0), (1e0,1e-2))},
tup => {
let ((low, high), (mu, sigma)) = tup
let uniformWeight = 0.6
let lognormalWeight = 0.4
let meanValue = {
run(Mixture([(mkUniform(low, high), uniformWeight), (mkLognormal(mu, sigma), lognormalWeight)]))
-> outputMap(FromDist(ToFloat(#Mean)))
}
let uniformMean = (low +. high) /. 2.0
let lognormalMean = mu +. sigma ** 2.0 /. 2.0
meanValue
-> unpackFloat
-> expect
-> toBeSoCloseTo(uniformWeight *. uniformMean +. lognormalWeight *. lognormalMean, ~digits=-1)
"weighted mean of a beta and an exponential",
// This would not survive property testing, it was easy for me to find cases that NaN'd out.
list{((128.0, 1.0), 2.0), ((2e-1, 64.0), 16.0), ((1e0, 1e0), 64.0)},
tup => {
let ((alpha, beta), rate) = tup
let betaWeight = 0.25
let exponentialWeight = 0.75
let meanValue = {
run(
Mixture([(mkBeta(alpha, beta), betaWeight), (mkExponential(rate), exponentialWeight)]),
)->outputMap(FromDist(ToFloat(#Mean)))
}
let betaMean = 1.0 /. (1.0 +. beta /. alpha)
let exponentialMean = 1.0 /. rate
meanValue
->unpackFloat
->expect
->toBeSoCloseTo(betaWeight *. betaMean +. exponentialWeight *. exponentialMean, ~digits=-1)
},
)
testAll(
"weighted mean of lognormal and uniform",
// Would not survive property tests: very easy to find cases that NaN out.
list{((-1e2, 1e1), (2e0, 1e0)), ((-1e-16, 1e-16), (1e-8, 1e0)), ((0.0, 1e0), (1e0, 1e-2))},
tup => {
let ((low, high), (mu, sigma)) = tup
let uniformWeight = 0.6
let lognormalWeight = 0.4
let meanValue = {
run(
Mixture([
(mkUniform(low, high), uniformWeight),
(mkLognormal(mu, sigma), lognormalWeight),
]),
)->outputMap(FromDist(ToFloat(#Mean)))
}
let uniformMean = (low +. high) /. 2.0
let lognormalMean = mu +. sigma ** 2.0 /. 2.0
meanValue
->unpackFloat
->expect
->toBeSoCloseTo(uniformWeight *. uniformMean +. lognormalWeight *. lognormalMean, ~digits=-1)
},
)
})

View File

@ -38,4 +38,3 @@ describe("Continuous and discrete splits", () => {
let toArr2 = discrete2 |> E.FloatFloatMap.toArray
makeTest("splitMedium at count=500", toArr2 |> Belt.Array.length, 500)
})

View File

@ -3,131 +3,115 @@ open Expect
open TestHelpers
// TODO: use Normal.make (but preferably after teh new validation dispatch is in)
let mkNormal = (mean, stdev) => GenericDist_Types.Symbolic(#Normal({mean: mean, stdev: stdev}))
let mkNormal = (mean, stdev) => DistributionTypes.Symbolic(#Normal({mean: mean, stdev: stdev}))
describe("(Symbolic) normalize", () => {
testAll("has no impact on normal distributions", list{-1e8, -1e-2, 0.0, 1e-4, 1e16}, mean => {
let normalValue = mkNormal(mean, 2.0)
let normalizedValue = run(FromDist(ToDist(Normalize), normalValue))
normalizedValue
-> unpackDist
-> expect
-> toEqual(normalValue)
normalizedValue->unpackDist->expect->toEqual(normalValue)
})
})
describe("(Symbolic) mean", () => {
testAll("of normal distributions", list{-1e8, -16.0, -1e-2, 0.0, 1e-4, 32.0, 1e16}, mean => {
run(FromDist(ToFloat(#Mean), mkNormal(mean, 4.0)))
-> unpackFloat
-> expect
-> toBeCloseTo(mean)
run(FromDist(ToFloat(#Mean), mkNormal(mean, 4.0)))->unpackFloat->expect->toBeCloseTo(mean)
})
Skip.test("of normal(0, -1) (it NaNs out)", () => {
run(FromDist(ToFloat(#Mean), mkNormal(1e1, -1e0)))
-> unpackFloat
-> expect
-> ExpectJs.toBeFalsy
run(FromDist(ToFloat(#Mean), mkNormal(1e1, -1e0)))->unpackFloat->expect->ExpectJs.toBeFalsy
})
test("of normal(0, 1e-8) (it doesn't freak out at tiny stdev)", () => {
run(FromDist(ToFloat(#Mean), mkNormal(0.0, 1e-8)))
-> unpackFloat
-> expect
-> toBeCloseTo(0.0)
run(FromDist(ToFloat(#Mean), mkNormal(0.0, 1e-8)))->unpackFloat->expect->toBeCloseTo(0.0)
})
testAll("of exponential distributions", list{1e-7, 2.0, 10.0, 100.0}, rate => {
let meanValue = run(FromDist(ToFloat(#Mean), GenericDist_Types.Symbolic(#Exponential({rate: rate}))))
meanValue -> unpackFloat -> expect -> toBeCloseTo(1.0 /. rate) // https://en.wikipedia.org/wiki/Exponential_distribution#Mean,_variance,_moments,_and_median
let meanValue = run(
FromDist(ToFloat(#Mean), DistributionTypes.Symbolic(#Exponential({rate: rate}))),
)
meanValue->unpackFloat->expect->toBeCloseTo(1.0 /. rate) // https://en.wikipedia.org/wiki/Exponential_distribution#Mean,_variance,_moments,_and_median
})
test("of a cauchy distribution", () => {
let meanValue = run(FromDist(ToFloat(#Mean), GenericDist_Types.Symbolic(#Cauchy({local: 1.0, scale: 1.0}))))
meanValue
-> unpackFloat
-> expect
-> toBeCloseTo(2.01868297874546)
let meanValue = run(
FromDist(ToFloat(#Mean), DistributionTypes.Symbolic(#Cauchy({local: 1.0, scale: 1.0}))),
)
meanValue->unpackFloat->expect->toBeSoCloseTo(1.0098094001641797, ~digits=5)
//-> toBe(GenDistError(Other("Cauchy distributions may have no mean value.")))
})
testAll("of triangular distributions", list{(1.0,2.0,3.0), (-1e7,-1e-7,1e-7), (-1e-7,1e0,1e7), (-1e-16,0.0,1e-16)}, tup => {
let (low, medium, high) = tup
let meanValue = run(FromDist(
ToFloat(#Mean),
GenericDist_Types.Symbolic(#Triangular({low: low, medium: medium, high: high}))
))
meanValue
-> unpackFloat
-> expect
-> toBeCloseTo((low +. medium +. high) /. 3.0) // https://www.statology.org/triangular-distribution/
})
testAll(
"of triangular distributions",
list{(1.0, 2.0, 3.0), (-1e7, -1e-7, 1e-7), (-1e-7, 1e0, 1e7), (-1e-16, 0.0, 1e-16)},
tup => {
let (low, medium, high) = tup
let meanValue = run(
FromDist(
ToFloat(#Mean),
DistributionTypes.Symbolic(#Triangular({low: low, medium: medium, high: high})),
),
)
meanValue->unpackFloat->expect->toBeCloseTo((low +. medium +. high) /. 3.0) // https://www.statology.org/triangular-distribution/
},
)
// TODO: nonpositive inputs are SUPPOSED to crash.
testAll("of beta distributions", list{(1e-4, 6.4e1), (1.28e2, 1e0), (1e-16, 1e-16), (1e16, 1e16), (-1e4, 1e1), (1e1, -1e4)}, tup => {
let (alpha, beta) = tup
let meanValue = run(FromDist(
ToFloat(#Mean),
GenericDist_Types.Symbolic(#Beta({alpha: alpha, beta: beta}))
))
meanValue
-> unpackFloat
-> expect
-> toBeCloseTo(1.0 /. (1.0 +. (beta /. alpha))) // https://en.wikipedia.org/wiki/Beta_distribution#Mean
})
testAll(
"of beta distributions",
list{(1e-4, 6.4e1), (1.28e2, 1e0), (1e-16, 1e-16), (1e16, 1e16), (-1e4, 1e1), (1e1, -1e4)},
tup => {
let (alpha, beta) = tup
let meanValue = run(
FromDist(ToFloat(#Mean), DistributionTypes.Symbolic(#Beta({alpha: alpha, beta: beta}))),
)
meanValue->unpackFloat->expect->toBeCloseTo(1.0 /. (1.0 +. beta /. alpha)) // https://en.wikipedia.org/wiki/Beta_distribution#Mean
},
)
// TODO: When we have our theory of validators we won't want this to be NaN but to be an error.
test("of beta(0, 0)", () => {
let meanValue = run(FromDist(
ToFloat(#Mean),
GenericDist_Types.Symbolic(#Beta({alpha: 0.0, beta: 0.0}))
))
meanValue
-> unpackFloat
-> expect
-> ExpectJs.toBeFalsy
let meanValue = run(
FromDist(ToFloat(#Mean), DistributionTypes.Symbolic(#Beta({alpha: 0.0, beta: 0.0}))),
)
meanValue->unpackFloat->expect->ExpectJs.toBeFalsy
})
testAll("of lognormal distributions", list{(2.0, 4.0), (1e-7, 1e-2), (-1e6, 10.0), (1e3, -1e2), (-1e8, -1e4), (1e2, 1e-5)}, tup => {
let (mu, sigma) = tup
let meanValue = run(FromDist(
ToFloat(#Mean),
GenericDist_Types.Symbolic(#Lognormal({mu: mu, sigma: sigma}))
))
meanValue
-> unpackFloat
-> expect
-> toBeCloseTo(Js.Math.exp(mu +. sigma ** 2.0 /. 2.0 )) // https://brilliant.org/wiki/log-normal-distribution/
})
testAll(
"of lognormal distributions",
list{(2.0, 4.0), (1e-7, 1e-2), (-1e6, 10.0), (1e3, -1e2), (-1e8, -1e4), (1e2, 1e-5)},
tup => {
let (mu, sigma) = tup
let meanValue = run(
FromDist(ToFloat(#Mean), DistributionTypes.Symbolic(#Lognormal({mu: mu, sigma: sigma}))),
)
meanValue->unpackFloat->expect->toBeCloseTo(Js.Math.exp(mu +. sigma ** 2.0 /. 2.0)) // https://brilliant.org/wiki/log-normal-distribution/
},
)
testAll("of uniform distributions", list{(1e-5, 12.345), (-1e4, 1e4), (-1e16, -1e2), (5.3e3, 9e9)}, tup => {
let (low, high) = tup
let meanValue = run(FromDist(
ToFloat(#Mean),
GenericDist_Types.Symbolic(#Uniform({low: low, high: high}))
))
meanValue
-> unpackFloat
-> expect
-> toBeCloseTo((low +. high) /. 2.0) // https://en.wikipedia.org/wiki/Continuous_uniform_distribution#Moments
})
testAll(
"of uniform distributions",
list{(1e-5, 12.345), (-1e4, 1e4), (-1e16, -1e2), (5.3e3, 9e9)},
tup => {
let (low, high) = tup
let meanValue = run(
FromDist(ToFloat(#Mean), DistributionTypes.Symbolic(#Uniform({low: low, high: high}))),
)
meanValue->unpackFloat->expect->toBeCloseTo((low +. high) /. 2.0) // https://en.wikipedia.org/wiki/Continuous_uniform_distribution#Moments
},
)
test("of a float", () => {
let meanValue = run(FromDist(
ToFloat(#Mean),
GenericDist_Types.Symbolic(#Float(7.7))
))
meanValue -> unpackFloat -> expect -> toBeCloseTo(7.7)
let meanValue = run(FromDist(ToFloat(#Mean), DistributionTypes.Symbolic(#Float(7.7))))
meanValue->unpackFloat->expect->toBeCloseTo(7.7)
})
})
describe("Normal distribution with sparklines", () => {
let parameterWiseAdditionPdf = (n1: SymbolicDistTypes.normal, n2: SymbolicDistTypes.normal) => {
let normalDistAtSumMeanConstr = SymbolicDist.Normal.add(n1, n2)
let normalDistAtSumMean: SymbolicDistTypes.normal = switch normalDistAtSumMeanConstr {
| #Normal(params) => params
| #Normal(params) => params
}
x => SymbolicDist.Normal.pdf(x, normalDistAtSumMean)
}
@ -140,22 +124,23 @@ describe("Normal distribution with sparklines", () => {
let pdfNormalDistAtMean5 = x => SymbolicDist.Normal.pdf(x, normalDistAtMean5)
let sparklineMean5 = fnImage(pdfNormalDistAtMean5, range20Float)
Sparklines.create(sparklineMean5, ())
-> expect
-> toEqual(`▁▂▃▆██▇▅▂▁▁▁▁▁▁▁▁▁▁▁`)
->expect
->toEqual(`▁▂▃▆██▇▅▂▁▁▁▁▁▁▁▁▁▁▁`)
})
test("parameter-wise addition of two normal distributions", () => {
let sparklineMean15 = normalDistAtMean5 -> parameterWiseAdditionPdf(normalDistAtMean10) -> fnImage(range20Float)
let sparklineMean15 =
normalDistAtMean5->parameterWiseAdditionPdf(normalDistAtMean10)->fnImage(range20Float)
Sparklines.create(sparklineMean15, ())
-> expect
-> toEqual(`▁▁▁▁▁▁▁▁▁▂▃▄▆███▇▅▄▂`)
->expect
->toEqual(`▁▁▁▁▁▁▁▁▁▂▃▄▆███▇▅▄▂`)
})
test("mean=10 cdf", () => {
let cdfNormalDistAtMean10 = x => SymbolicDist.Normal.cdf(x, normalDistAtMean10)
let sparklineMean10 = fnImage(cdfNormalDistAtMean10, range20Float)
Sparklines.create(sparklineMean10, ())
-> expect
-> toEqual(`▁▁▁▁▁▁▁▁▂▄▅▇████████`)
->expect
->toEqual(`▁▁▁▁▁▁▁▁▂▄▅▇████████`)
})
})

View File

@ -3,8 +3,8 @@ open Expect
let makeTest = (~only=false, str, item1, item2) =>
only
? Only.test(str, () => expect(item1) -> toEqual(item2))
: test(str, () => expect(item1) -> toEqual(item2))
? Only.test(str, () => expect(item1)->toEqual(item2))
: test(str, () => expect(item1)->toEqual(item2))
describe("Lodash", () =>
describe("Lodash", () => {

View File

@ -6,8 +6,7 @@ open Expect
let expectEvalToBe = (expr: string, answer: string) =>
Reducer.evaluate(expr)->ExpressionValue.toStringResult->expect->toBe(answer)
let testEval = (expr, answer) =>
test(expr, () => expectEvalToBe(expr, answer))
let testEval = (expr, answer) => test(expr, () => expectEvalToBe(expr, answer))
describe("builtin", () => {
// All MathJs operators and functions are available for string, number and boolean

View File

@ -14,7 +14,8 @@ let testDescriptionParse = (desc, expr, answer) => test(desc, () => expectParseT
module MySkip = {
let testParse = (expr, answer) => Skip.test(expr, () => expectParseToBe(expr, answer))
let testDescriptionParse = (desc, expr, answer) => Skip.test(desc, () => expectParseToBe(expr, answer))
let testDescriptionParse = (desc, expr, answer) =>
Skip.test(desc, () => expectParseToBe(expr, answer))
}
describe("MathJs parse", () => {
@ -60,7 +61,8 @@ describe("MathJs parse", () => {
MySkip.testDescriptionParse("define", "# This is a comment", "???")
})
describe("if statement", () => { // TODO Tertiary operator instead
describe("if statement", () => {
// TODO Tertiary operator instead
MySkip.testDescriptionParse("define", "if (true) { 1 } else { 0 }", "???")
})
})

View File

@ -3,7 +3,8 @@ open Reducer_TestHelpers
let testParseToBe = (expr, answer) => test(expr, () => expectParseToBe(expr, answer))
let testDescriptionParseToBe = (desc, expr, answer) => test(desc, () => expectParseToBe(expr, answer))
let testDescriptionParseToBe = (desc, expr, answer) =>
test(desc, () => expectParseToBe(expr, answer))
let testEvalToBe = (expr, answer) => test(expr, () => expectEvalToBe(expr, answer))
@ -44,13 +45,21 @@ describe("reducer using mathjs parse", () => {
})
describe("multi-line", () => {
testParseToBe("1; 2", "Ok((:$$bindExpression (:$$bindStatement (:$$bindings) 1) 2))")
testParseToBe("1+1; 2+1", "Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:add 1 1)) (:add 2 1)))")
testParseToBe(
"1+1; 2+1",
"Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:add 1 1)) (:add 2 1)))",
)
})
describe("assignment", () => {
testParseToBe("x=1; x", "Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :x 1)) :x))")
testParseToBe("x=1+1; x+1", "Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :x (:add 1 1))) (:add :x 1)))")
testParseToBe(
"x=1; x",
"Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :x 1)) :x))",
)
testParseToBe(
"x=1+1; x+1",
"Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :x (:add 1 1))) (:add :x 1)))",
)
})
})
describe("eval", () => {
@ -101,5 +110,9 @@ describe("test exceptions", () => {
"javascriptraise('div by 0')",
"Error(JS Exception: Error: 'div by 0')",
)
testDescriptionEvalToBe("rescript exception", "rescriptraise()", "Error(TODO: unhandled rescript exception)")
testDescriptionEvalToBe(
"rescript exception",
"rescriptraise()",
"Error(TODO: unhandled rescript exception)",
)
})

View File

@ -90,16 +90,8 @@ describe("eval on distribution functions", () => {
})
describe("mixture", () => {
testEval(
~skip=true,
"mx(normal(5,2), normal(10,1), normal(15, 1))",
"Ok(Point Set Distribution)",
)
testEval(
~skip=true,
"mixture(normal(5,2), normal(10,1), [.2,, .4])",
"Ok(Point Set Distribution)",
)
testEval("mx(normal(5,2), normal(10,1), normal(15, 1))", "Ok(Point Set Distribution)")
testEval("mixture(normal(5,2), normal(10,1), [0.2, 0.4])", "Ok(Point Set Distribution)")
})
})
@ -111,7 +103,11 @@ describe("parse on distribution functions", () => {
})
describe("pointwise arithmetic expressions", () => {
testParse(~skip=true, "normal(5,2) .+ normal(5,1)", "Ok((:dotAdd (:normal 5 2) (:normal 5 1)))")
testParse(~skip=true, "normal(5,2) .- normal(5,1)", "Ok((:dotSubtract (:normal 5 2) (:normal 5 1)))")
testParse(
~skip=true,
"normal(5,2) .- normal(5,1)",
"Ok((:dotSubtract (:normal 5 2) (:normal 5 1)))",
)
testParse("normal(5,2) .* normal(5,1)", "Ok((:dotMultiply (:normal 5 2) (:normal 5 1)))")
testParse("normal(5,2) ./ normal(5,1)", "Ok((:dotDivide (:normal 5 2) (:normal 5 1)))")
testParse("normal(5,2) .^ normal(5,1)", "Ok((:dotPow (:normal 5 2) (:normal 5 1)))")

View File

@ -3,24 +3,41 @@ open Expect
let makeTest = (~only=false, str, item1, item2) =>
only
? Only.test(str, () => expect(item1) -> toEqual(item2))
: test(str, () => expect(item1) -> toEqual(item2))
? Only.test(str, () => expect(item1)->toEqual(item2))
: test(str, () => expect(item1)->toEqual(item2))
let {toFloat, toDist, toString, toError, fmap} = module(DistributionOperation.Output)
let fnImage = (theFn, inps) => Js.Array.map(theFn, inps)
let env: DistributionOperation.env = {
sampleCount: 100,
xyPointLength: 100,
sampleCount: 10000,
xyPointLength: 1000,
}
let run = DistributionOperation.run(~env)
let outputMap = fmap(~env)
let unreachableInTestFileMessage = "Should be impossible to reach (This error is in test file)"
let toExtFloat: option<float> => float = E.O.toExt(unreachableInTestFileMessage)
let toExtDist: option<GenericDist_Types.genericDist> => GenericDist_Types.genericDist = E.O.toExt(unreachableInTestFileMessage)
let toExtDist: option<DistributionTypes.genericDist> => DistributionTypes.genericDist = E.O.toExt(
unreachableInTestFileMessage,
)
// let toExt: option<'a> => 'a = E.O.toExt(unreachableInTestFileMessage)
let unpackFloat = x => x -> toFloat -> toExtFloat
let unpackDist = y => y -> toDist -> toExtDist
let unpackFloat = x => x->toFloat->toExtFloat
let unpackDist = y => y->toDist->toExtDist
let mkNormal = (mean, stdev) => DistributionTypes.Symbolic(#Normal({mean: mean, stdev: stdev}))
let mkBeta = (alpha, beta) => DistributionTypes.Symbolic(#Beta({alpha: alpha, beta: beta}))
let mkExponential = rate => DistributionTypes.Symbolic(#Exponential({rate: rate}))
let mkUniform = (low, high) => DistributionTypes.Symbolic(#Uniform({low: low, high: high}))
let mkCauchy = (local, scale) => DistributionTypes.Symbolic(#Cauchy({local: local, scale: scale}))
let mkLognormal = (mu, sigma) => DistributionTypes.Symbolic(#Lognormal({mu: mu, sigma: sigma}))
let normalMake = SymbolicDist.Normal.make
let betaMake = SymbolicDist.Beta.make
let exponentialMake = SymbolicDist.Exponential.make
let uniformMake = SymbolicDist.Uniform.make
let cauchyMake = SymbolicDist.Cauchy.make
let lognormalMake = SymbolicDist.Lognormal.make
let triangularMake = SymbolicDist.Triangular.make
let floatMake = SymbolicDist.Float.make

View File

@ -0,0 +1,10 @@
open Jest
open Expect
describe("E.L.combinations2", () => {
test("size three", () => {
E.L.combinations2(list{"alice", "bob", "eve"})
->expect
->toEqual(list{("alice", "bob"), ("alice", "eve"), ("bob", "eve")})
})
})

View File

@ -3,8 +3,8 @@ open Expect
let makeTest = (~only=false, str, item1, item2) =>
only
? Only.test(str, () => expect(item1) -> toEqual(item2))
: test(str, () => expect(item1) -> toEqual(item2))
? Only.test(str, () => expect(item1)->toEqual(item2))
: test(str, () => expect(item1)->toEqual(item2))
let pointSetDist1: PointSetTypes.xyShape = {xs: [1., 4., 8.], ys: [0.2, 0.4, 0.8]}
@ -21,7 +21,11 @@ let pointSetDist3: PointSetTypes.xyShape = {
describe("XYShapes", () => {
describe("logScorePoint", () => {
makeTest("When identical", XYShape.logScorePoint(30, pointSetDist1, pointSetDist1), Some(0.0))
makeTest("When similar", XYShape.logScorePoint(30, pointSetDist1, pointSetDist2), Some(1.658971191043856))
makeTest(
"When similar",
XYShape.logScorePoint(30, pointSetDist1, pointSetDist2),
Some(1.658971191043856),
)
makeTest(
"When very different",
XYShape.logScorePoint(30, pointSetDist1, pointSetDist3),

View File

@ -10,6 +10,7 @@
"test:reducer": "jest --testPathPattern '.*__tests__/Reducer.*'",
"test": "jest",
"test:watch": "jest --watchAll",
"test:quick": "jest --modulePathIgnorePatterns=__tests__/Distributions/Invariants/*",
"coverage": "rm -f *.coverage; yarn clean; BISECT_ENABLE=yes yarn build; yarn test; bisect-ppx-report html",
"coverage:ci": "yarn clean; BISECT_ENABLE=yes yarn build; yarn test; bisect-ppx-report send-to Codecov",
"lint:rescript": "./lint.sh",

View File

@ -1,6 +1,6 @@
type functionCallInfo = GenericDist_Types.Operation.genericFunctionCallInfo
type genericDist = GenericDist_Types.genericDist
type error = GenericDist_Types.error
type genericDist = DistributionTypes.genericDist
type error = DistributionTypes.error
// TODO: It could be great to use a cache for some calculations (basically, do memoization). Also, better analytics/tracking could go a long way.

View File

@ -39,57 +39,52 @@ module Output: {
}
module Constructors: {
@genType
let mean: (~env: env, genericDist) => result<float, error>
@genType
let sample: (~env: env, genericDist) => result<float, error>
@genType
let cdf: (~env: env, genericDist, float) => result<float, error>
@genType
let inv: (~env: env, genericDist, float) => result<float, error>
@genType
let pdf: (~env: env, genericDist, float) => result<float, error>
@genType
let normalize: (~env: env, genericDist) => result<genericDist, error>
@genType
let toPointSet: (~env: env, genericDist) => result<genericDist, error>
@genType
let toSampleSet: (~env: env, genericDist, int) => result<genericDist, error>
@genType
let truncate: (
~env: env,
genericDist,
option<float>,
option<float>,
) => result<genericDist, error>
@genType
let inspect: (~env: env, genericDist) => result<genericDist, error>
@genType
let toString: (~env: env, genericDist) => result<string, error>
@genType
let toSparkline: (~env: env, genericDist, int) => result<string, error>
@genType
let algebraicAdd: (~env: env, genericDist, genericDist) => result<genericDist, error>
@genType
let algebraicMultiply: (~env: env, genericDist, genericDist) => result<genericDist, error>
@genType
let algebraicDivide: (~env: env, genericDist, genericDist) => result<genericDist, error>
@genType
let algebraicSubtract: (~env: env, genericDist, genericDist) => result<genericDist, error>
@genType
let algebraicLogarithm: (~env: env, genericDist, genericDist) => result<genericDist, error>
@genType
let algebraicPower: (~env: env, genericDist, genericDist) => result<genericDist, error>
@genType
let pointwiseAdd: (~env: env, genericDist, genericDist) => result<genericDist, error>
@genType
let pointwiseMultiply: (~env: env, genericDist, genericDist) => result<genericDist, error>
@genType
let pointwiseDivide: (~env: env, genericDist, genericDist) => result<genericDist, error>
@genType
let pointwiseSubtract: (~env: env, genericDist, genericDist) => result<genericDist, error>
@genType
let pointwiseLogarithm: (~env: env, genericDist, genericDist) => result<genericDist, error>
@genType
let pointwisePower: (~env: env, genericDist, genericDist) => result<genericDist, error>
@genType
let mean: (~env: env, genericDist) => result<float, error>
@genType
let sample: (~env: env, genericDist) => result<float, error>
@genType
let cdf: (~env: env, genericDist, float) => result<float, error>
@genType
let inv: (~env: env, genericDist, float) => result<float, error>
@genType
let pdf: (~env: env, genericDist, float) => result<float, error>
@genType
let normalize: (~env: env, genericDist) => result<genericDist, error>
@genType
let toPointSet: (~env: env, genericDist) => result<genericDist, error>
@genType
let toSampleSet: (~env: env, genericDist, int) => result<genericDist, error>
@genType
let truncate: (~env: env, genericDist, option<float>, option<float>) => result<genericDist, error>
@genType
let inspect: (~env: env, genericDist) => result<genericDist, error>
@genType
let toString: (~env: env, genericDist) => result<string, error>
@genType
let toSparkline: (~env: env, genericDist, int) => result<string, error>
@genType
let algebraicAdd: (~env: env, genericDist, genericDist) => result<genericDist, error>
@genType
let algebraicMultiply: (~env: env, genericDist, genericDist) => result<genericDist, error>
@genType
let algebraicDivide: (~env: env, genericDist, genericDist) => result<genericDist, error>
@genType
let algebraicSubtract: (~env: env, genericDist, genericDist) => result<genericDist, error>
@genType
let algebraicLogarithm: (~env: env, genericDist, genericDist) => result<genericDist, error>
@genType
let algebraicPower: (~env: env, genericDist, genericDist) => result<genericDist, error>
@genType
let pointwiseAdd: (~env: env, genericDist, genericDist) => result<genericDist, error>
@genType
let pointwiseMultiply: (~env: env, genericDist, genericDist) => result<genericDist, error>
@genType
let pointwiseDivide: (~env: env, genericDist, genericDist) => result<genericDist, error>
@genType
let pointwiseSubtract: (~env: env, genericDist, genericDist) => result<genericDist, error>
@genType
let pointwiseLogarithm: (~env: env, genericDist, genericDist) => result<genericDist, error>
@genType
let pointwisePower: (~env: env, genericDist, genericDist) => result<genericDist, error>
}

View File

@ -1,12 +1,15 @@
@genType
type genericDist =
| PointSet(PointSetTypes.pointSetDist)
| SampleSet(array<float>)
| SampleSet(SampleSetDist.t)
| Symbolic(SymbolicDistTypes.symbolicDist)
@genType
type error =
| NotYetImplemented
| Unreachable
| DistributionVerticalShiftIsInvalid
| ArgumentError(string)
| Other(string)
module Operation = {
@ -55,7 +58,11 @@ module DistributionOperation = {
type fromDist =
| ToFloat(Operation.toFloat)
| ToDist(toDist)
| ToDistCombination(Operation.direction, Operation.arithmeticOperation, [#Dist(genericDist) | #Float(float)])
| ToDistCombination(
Operation.direction,
Operation.arithmeticOperation,
[#Dist(genericDist) | #Float(float)],
)
| ToString
type singleParamaterFunction =

View File

@ -1,6 +1,6 @@
//TODO: multimodal, add interface, test somehow, track performance, refactor sampleSet, refactor ASTEvaluator.res.
type t = GenericDist_Types.genericDist
type error = GenericDist_Types.error
type t = DistributionTypes.genericDist
type error = DistributionTypes.error
type toPointSetFn = t => result<PointSetTypes.pointSetDist, error>
type toSampleSetFn = t => result<SampleSetDist.t, error>
type scaleMultiplyFn = (t, float) => result<t, error>
@ -115,7 +115,7 @@ module Truncate = {
| Some(r) => Ok(r)
| None =>
toPointSetFn(t)->E.R2.fmap(t => {
GenericDist_Types.PointSet(PointSetDist.T.truncate(leftCutoff, rightCutoff, t))
DistributionTypes.PointSet(PointSetDist.T.truncate(leftCutoff, rightCutoff, t))
})
}
}
@ -168,7 +168,7 @@ module AlgebraicCombination = {
->E.R.bind(((t1, t2)) => {
SampleSetDist.map2(~fn, ~t1, ~t2)->GenericDist_Types.Error.resultStringToResultError
})
->E.R2.fmap(r => GenericDist_Types.SampleSet(r))
->E.R2.fmap(r => DistributionTypes.SampleSet(r))
}
//I'm (Ozzie) really just guessing here, very little idea what's best
@ -206,7 +206,7 @@ module AlgebraicCombination = {
arithmeticOperation,
t1,
t2,
)->E.R2.fmap(r => GenericDist_Types.PointSet(r))
)->E.R2.fmap(r => DistributionTypes.PointSet(r))
}
}
}
@ -229,7 +229,7 @@ let pointwiseCombination = (
t2,
)
)
->E.R2.fmap(r => GenericDist_Types.PointSet(r))
->E.R2.fmap(r => DistributionTypes.PointSet(r))
}
let pointwiseCombinationFloat = (
@ -239,7 +239,7 @@ let pointwiseCombinationFloat = (
~float: float,
): result<t, error> => {
let m = switch arithmeticOperation {
| #Add | #Subtract => Error(GenericDist_Types.DistributionVerticalShiftIsInvalid)
| #Add | #Subtract => Error(DistributionTypes.DistributionVerticalShiftIsInvalid)
| (#Multiply | #Divide | #Power | #Logarithm) as arithmeticOperation =>
toPointSetFn(t)->E.R2.fmap(t => {
//TODO: Move to PointSet codebase
@ -254,7 +254,7 @@ let pointwiseCombinationFloat = (
)
})
}
m->E.R2.fmap(r => GenericDist_Types.PointSet(r))
m->E.R2.fmap(r => DistributionTypes.PointSet(r))
}
//Note: The result should always cumulatively sum to 1. This would be good to test.
@ -265,7 +265,7 @@ let mixture = (
~pointwiseAddFn: pointwiseAddFn,
) => {
if E.A.length(values) == 0 {
Error(GenericDist_Types.Other("Mixture error: mixture must have at least 1 element"))
Error(DistributionTypes.Other("Mixture error: mixture must have at least 1 element"))
} else {
let totalWeight = values->E.A2.fmap(E.Tuple2.second)->E.A.Floats.sum
let properlyWeightedValues =

View File

@ -1,14 +1,6 @@
type genericDist =
| PointSet(PointSetTypes.pointSetDist)
| SampleSet(SampleSetDist.t)
| Symbolic(SymbolicDistTypes.symbolicDist)
type genericDist = DistributionTypes.genericDist
@genType
type error =
| NotYetImplemented
| Unreachable
| DistributionVerticalShiftIsInvalid
| Other(string)
type error = DistributionTypes.error
@genType
module Error = {
@ -22,6 +14,7 @@ module Error = {
| NotYetImplemented => "Not Yet Implemented"
| Unreachable => "Unreachable"
| DistributionVerticalShiftIsInvalid => "Distribution Vertical Shift Is Invalid"
| ArgumentError(x) => `Argument Error: ${x}`
| Other(s) => s
}
}

View File

@ -100,7 +100,6 @@ let combineShapesContinuousContinuous = (
s1: PointSetTypes.xyShape,
s2: PointSetTypes.xyShape,
): PointSetTypes.xyShape => {
// if we add the two distributions, we should probably use normal filters.
// if we multiply the two distributions, we should probably use lognormal filters.
let t1m = toDiscretePointMassesFromTriangulars(s1)

View File

@ -235,18 +235,10 @@ module T = Dist({
let indefiniteIntegralStepwise = (p, h1) => h1 *. p ** 2.0 /. 2.0
let indefiniteIntegralLinear = (p, a, b) => a *. p ** 2.0 /. 2.0 +. b *. p ** 3.0 /. 3.0
Analysis.integrate(
~indefiniteIntegralStepwise,
~indefiniteIntegralLinear,
t,
)
Analysis.integrate(~indefiniteIntegralStepwise, ~indefiniteIntegralLinear, t)
}
let variance = (t: t): float =>
XYShape.Analysis.getVarianceDangerously(
t,
mean,
Analysis.getMeanOfSquares,
)
XYShape.Analysis.getVarianceDangerously(t, mean, Analysis.getMeanOfSquares)
})
let downsampleEquallyOverX = (length, t): t =>

View File

@ -212,8 +212,7 @@ module T = Dist({
let totalIntegralSum = discreteIntegralSum +. continuousIntegralSum
let getMeanOfSquares = ({discrete, continuous}: t) => {
let discreteMean =
discrete |> Discrete.shapeMap(XYShape.T.square) |> Discrete.T.mean
let discreteMean = discrete |> Discrete.shapeMap(XYShape.T.square) |> Discrete.T.mean
let continuousMean = continuous |> Continuous.Analysis.getMeanOfSquares
(discreteMean *. discreteIntegralSum +. continuousMean *. continuousIntegralSum) /.
totalIntegralSum

View File

@ -14,10 +14,10 @@ type distributionType = [
| #CDF
]
type xyShape = XYShape.xyShape;
type interpolationStrategy = XYShape.interpolationStrategy;
type extrapolationStrategy = XYShape.extrapolationStrategy;
type interpolator = XYShape.extrapolationStrategy;
type xyShape = XYShape.xyShape
type interpolationStrategy = XYShape.interpolationStrategy
type extrapolationStrategy = XYShape.extrapolationStrategy
type interpolator = XYShape.extrapolationStrategy
@genType
type rec continuousShape = {

View File

@ -141,6 +141,8 @@ module Lognormal = {
}
let divide = (l1, l2) => {
let mu = l1.mu -. l2.mu
// We believe the ratiands will have covariance zero.
// See here https://stats.stackexchange.com/questions/21735/what-are-the-mean-and-variance-of-the-ratio-of-two-lognormal-variables for details
let sigma = l1.sigma +. l2.sigma
#Lognormal({mu: mu, sigma: sigma})
}
@ -346,7 +348,11 @@ module T = {
| _ => #NoSolution
}
let toPointSetDist = (~xSelection=#ByWeight, sampleCount, d: symbolicDist): PointSetTypes.pointSetDist =>
let toPointSetDist = (
~xSelection=#ByWeight,
sampleCount,
d: symbolicDist,
): PointSetTypes.pointSetDist =>
switch d {
| #Float(v) => Discrete(Discrete.make(~integralSumCache=Some(1.0), {xs: [v], ys: [1.0]}))
| _ =>

View File

@ -22,7 +22,7 @@ let makeSymbolicFromTwoFloats = (name, fn) =>
~inputTypes=[#Float, #Float],
~run=x =>
switch x {
| [#Float(a), #Float(b)] => fn(a, b) |> E.R.fmap(r => (#SymbolicDist(r)))
| [#Float(a), #Float(b)] => fn(a, b) |> E.R.fmap(r => #SymbolicDist(r))
| e => wrongInputsError(e)
},
(),
@ -90,7 +90,8 @@ let floatFromDist = (
switch t {
| #SymbolicDist(s) =>
SymbolicDist.T.operate(distToFloatOp, s) |> E.R.bind(_, v => Ok(#SymbolicDist(#Float(v))))
| #RenderedDist(rs) => PointSetDist.operate(distToFloatOp, rs) |> (v => Ok(#SymbolicDist(#Float(v))))
| #RenderedDist(rs) =>
PointSetDist.operate(distToFloatOp, rs) |> (v => Ok(#SymbolicDist(#Float(v))))
}
let verticalScaling = (scaleOp, rs, scaleBy) => {
@ -125,10 +126,15 @@ module Multimodal = {
->E.R.bind(TypeSystem.TypedValue.toArray)
->E.R.bind(r => r |> E.A.fmap(TypeSystem.TypedValue.toFloat) |> E.A.R.firstErrorOrOpen)
E.R.merge(dists, weights) -> E.R.bind(((a, b)) =>
E.A.length(b) > E.A.length(a) ?
Error("Too many weights provided") :
Ok(E.A.zipMaxLength(a, b) |> E.A.fmap(((a, b)) => (a |> E.O.toExn(""), b |> E.O.default(1.0))))
E.R.merge(dists, weights)->E.R.bind(((a, b)) =>
E.A.length(b) > E.A.length(a)
? Error("Too many weights provided")
: Ok(
E.A.zipMaxLength(a, b) |> E.A.fmap(((a, b)) => (
a |> E.O.toExn(""),
b |> E.O.default(1.0),
)),
)
)
| _ => Error("Needs items")
}

View File

@ -86,11 +86,7 @@ module TypedValue = {
|> E.R.fmap(r => #Array(r))
| (#Hash(named), #Hash(r)) =>
let keyValues =
named |> E.A.fmap(((name, intendedType)) => (
name,
intendedType,
Hash.getByName(r, name),
))
named |> E.A.fmap(((name, intendedType)) => (name, intendedType, Hash.getByName(r, name)))
let typedHash =
keyValues
|> E.A.fmap(((name, intendedType, optionNode)) =>
@ -180,11 +176,7 @@ module Function = {
_coerceInputNodes(evaluationParams, t.inputTypes, t.shouldCoerceTypes),
)
let run = (
evaluationParams: ASTTypes.evaluationParams,
inputNodes: inputNodes,
t: t,
) =>
let run = (evaluationParams: ASTTypes.evaluationParams, inputNodes: inputNodes, t: t) =>
inputsToTypedValues(evaluationParams, inputNodes, t)->E.R.bind(t.run)
|> (
x =>

View File

@ -6,7 +6,7 @@ module Js = Reducer_Js
module MathJs = Reducer_MathJs
@genType
type expressionValue = Reducer_Expression.expressionValue
type expressionValue = ReducerInterface_ExpressionValue.expressionValue
@genType
let evaluate: string => result<expressionValue, Reducer_ErrorValue.errorValue>

View File

@ -104,7 +104,7 @@ let rec reduceExpression = (expression: t, bindings: T.bindings): result<express
}
switch list {
| list{T.EValue(EvCall("$$bindings"))} => bindings->EBindings->Ok
| list{T.EValue(EvCall("$$bindings"))} => bindings->T.EBindings->Ok
| list{T.EValue(EvCall("$$bindStatement")), T.EBindings(bindings), statement} =>
doBindStatement(statement, bindings)

View File

@ -66,6 +66,64 @@ module Helpers = {
dist1,
)->runGenericOperation
}
let parseNumber = (args: expressionValue): Belt.Result.t<float, string> =>
switch args {
| EvNumber(x) => Ok(x)
| _ => Error("Not a number")
}
let parseNumberArray = (ags: array<expressionValue>): Belt.Result.t<array<float>, string> =>
E.A.fmap(parseNumber, ags) |> E.A.R.firstErrorOrOpen
let parseDist = (args: expressionValue): Belt.Result.t<GenericDist_Types.genericDist, string> =>
switch args {
| EvDistribution(x) => Ok(x)
| EvNumber(x) => Ok(GenericDist.fromFloat(x))
| _ => Error("Not a distribution")
}
let parseDistributionArray = (ags: array<expressionValue>): Belt.Result.t<
array<GenericDist_Types.genericDist>,
string,
> => E.A.fmap(parseDist, ags) |> E.A.R.firstErrorOrOpen
let mixtureWithGivenWeights = (
distributions: array<GenericDist_Types.genericDist>,
weights: array<float>,
): DistributionOperation.outputType =>
E.A.length(distributions) == E.A.length(weights)
? Mixture(Belt.Array.zip(distributions, weights))->runGenericOperation
: GenDistError(
ArgumentError("Error, mixture call has different number of distributions and weights"),
)
let mixtureWithDefaultWeights = (
distributions: array<GenericDist_Types.genericDist>,
): DistributionOperation.outputType => {
let length = E.A.length(distributions)
let weights = Belt.Array.make(length, 1.0 /. Belt.Int.toFloat(length))
mixtureWithGivenWeights(distributions, weights)
}
let mixture = (args: array<expressionValue>): DistributionOperation.outputType => {
switch E.A.last(args) {
| Some(EvArray(b)) => {
let weights = parseNumberArray(b)
let distributions = parseDistributionArray(
Belt.Array.slice(args, ~offset=0, ~len=E.A.length(args) - 1),
)
switch E.R.merge(distributions, weights) {
| Ok(d, w) => mixtureWithGivenWeights(d, w)
| Error(err) => GenDistError(ArgumentError(err))
}
}
| Some(EvDistribution(b)) => switch parseDistributionArray(args) {
| Ok(distributions) => mixtureWithDefaultWeights(distributions)
| Error(err) => GenDistError(ArgumentError(err))
}
| _ => GenDistError(ArgumentError("Last argument of mx must be array or distribution"))
}
}
}
module SymbolicConstructors = {
@ -146,6 +204,7 @@ let dispatchToGenericOutput = (call: ExpressionValue.functionCall): option<
Helpers.toDistFn(Truncate(None, Some(float)), dist)
| ("truncate", [EvDistribution(dist), EvNumber(float1), EvNumber(float2)]) =>
Helpers.toDistFn(Truncate(Some(float1), Some(float2)), dist)
| ("mx" | "mixture", args) => Helpers.mixture(args)->Some
| ("log", [EvDistribution(a)]) =>
Helpers.twoDiststoDistFn(Algebraic, "log", a, GenericDist.fromFloat(Math.e))->Some
| ("log10", [EvDistribution(a)]) =>
@ -187,7 +246,8 @@ let genericOutputToReducerValue = (o: DistributionOperation.outputType): result<
| GenDistError(NotYetImplemented) => Error(RETodo("Function not yet implemented"))
| GenDistError(Unreachable) => Error(RETodo("Unreachable"))
| GenDistError(DistributionVerticalShiftIsInvalid) =>
Error(RETodo("Distribution Vertical Shift is Invalid"))
Error(RETodo("Distribution Vertical Shift Is Invalid"))
| GenDistError(ArgumentError(err)) => Error(RETodo("Argument Error: " ++ err))
| GenDistError(Other(s)) => Error(RETodo(s))
}

View File

@ -11,10 +11,10 @@ The below few seem to work fine. In the future there's definitely more work to d
type samplingParams = DistributionOperation.env
@genType
type genericDist = GenericDist_Types.genericDist
type genericDist = DistributionTypes.genericDist
@genType
type distributionError = GenericDist_Types.error
type distributionError = DistributionTypes.error
@genType
type resultDist = result<genericDist, distributionError>
@ -32,7 +32,7 @@ let makeSampleSetDist = SampleSetDist.make
let evaluate = Reducer.evaluate
@genType
type expressionValue = Reducer_Expression.expressionValue
type expressionValue = ReducerInterface_ExpressionValue.expressionValue
@genType
type errorValue = Reducer_ErrorValue.errorValue

View File

@ -59,8 +59,9 @@ module O = {
let toExn = Rationale.Option.toExn
let some = Rationale.Option.some
let firstSome = Rationale.Option.firstSome
let toExt = Rationale.Option.toExn
let toExt = Rationale.Option.toExn // wanna flag this-- looks like a typo but `Rationale.OptiontoExt` doesn't exist.
let flatApply = (fn, b) => Rationale.Option.apply(fn, Some(b)) |> Rationale.Option.flatten
let flatten = Rationale.Option.flatten
let toBool = opt =>
switch opt {
@ -103,6 +104,7 @@ module O2 = {
let toExn = (a, b) => O.toExn(b, a)
let fmap = (a, b) => O.fmap(b, a)
let toResult = (a, b) => O.toResult(b, a)
let bind = (a, b) => O.bind(b, a)
}
/* Functions */
@ -176,17 +178,49 @@ module R = {
let errorIfCondition = (errorCondition, errorMessage, r) =>
errorCondition(r) ? Error(errorMessage) : Ok(r)
let ap = Rationale.Result.ap
let ap' = (r, a) =>
switch r {
| Ok(f) => fmap(f, a)
| Error(err) => Error(err)
}
// (a1 -> a2 -> r) -> m a1 -> m a2 -> m r // not in Rationale
let liftM2: (('a, 'b) => 'c, result<'a, 'd>, result<'b, 'd>) => result<'c, 'd> = (op, xR, yR) => {
ap'(fmap(op, xR), yR)
}
let liftJoin2: (('a, 'b) => result<'c, 'd>, result<'a, 'd>, result<'b, 'd>) => result<'c, 'd> = (
op,
xR,
yR,
) => {
bind(liftM2(op, xR, yR), x => x)
}
let fmap2 = (f, r) =>
switch r {
| Ok(r) => r->Ok
| Error(x) => x->f->Error
}
}
module R2 = {
let fmap = (a,b) => R.fmap(b,a)
let fmap = (a, b) => R.fmap(b, a)
let bind = (a, b) => R.bind(b, a)
//Converts result type to change error type only
let errMap = (a, map) => switch(a){
let errMap = (a, map) =>
switch a {
| Ok(r) => Ok(r)
| Error(e) => map(e)
}
}
let fmap2 = (xR, f) =>
switch xR {
| Ok(x) => x->Ok
| Error(x) => x->f->Error
}
}
let safe_fn_of_string = (fn, s: string): option<'a> =>
@ -257,6 +291,29 @@ module L = {
let update = Rationale.RList.update
let iter = List.iter
let findIndex = Rationale.RList.findIndex
let headSafe = Belt.List.head
let tailSafe = Belt.List.tail
let headExn = Belt.List.headExn
let tailExn = Belt.List.tailExn
let zip = Belt.List.zip
let combinations2: list<'a> => list<('a, 'a)> = xs => {
let rec loop: ('a, list<'a>) => list<('a, 'a)> = (x', xs') => {
let n = length(xs')
if n == 0 {
list{}
} else {
let combs = fmap(y => (x', y), xs')
let hd = headExn(xs')
let tl = tailExn(xs')
concat(list{combs, loop(hd, tl)})
}
}
switch (headSafe(xs), tailSafe(xs)) {
| (Some(x'), Some(xs')) => loop(x', xs')
| (_, _) => list{}
}
}
}
/* A for Array */
@ -300,7 +357,6 @@ module A = {
|> Rationale.Result.return
}
// This zips while taking the longest elements of each array.
let zipMaxLength = (array1, array2) => {
let maxLength = Int.max(length(array1), length(array2))
@ -456,7 +512,6 @@ module A = {
let diff = (arr: array<float>): array<float> =>
Belt.Array.zipBy(arr, Belt.Array.sliceToEnd(arr, 1), (left, right) => right -. left)
exception RangeError(string)
let range = (min: float, max: float, n: int): array<float> =>
switch n {
@ -474,7 +529,7 @@ module A = {
}
module A2 = {
let fmap = (a,b) => A.fmap(b,a)
let fmap = (a, b) => A.fmap(b, a)
let joinWith = (a, b) => A.joinWith(b, a)
}

View File

@ -36,8 +36,8 @@ module Exponential = {
@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("jstat") @scope("exponential") external sample: float => float = "sample"
@module("jstat") @scope("exponential") external mean: float => float = "mean"
}
module Cauchy = {
@ -56,7 +56,6 @@ module Triangular = {
@module("jstat") @scope("triangular") external mean: (float, float, float) => float = "mean"
}
module Pareto = {
@module("jstat") @scope("pareto") external pdf: (float, float, float) => float = "pdf"
@module("jstat") @scope("pareto") external cdf: (float, float, float) => float = "cdf"
@ -66,20 +65,20 @@ module Pareto = {
module Poisson = {
@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("jstat") @scope("poisson") external sample: float => float = "sample"
@module("jstat") @scope("poisson") external mean: float => float = "mean"
}
module Weibull = {
@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("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("binomial") external pdf: (float, float, float) => float = "pdf"
@module("jstat") @scope("binomial") external cdf: (float, float,float ) => float = "cdf"
@module("jstat") @scope("binomial") external cdf: (float, float, float) => float = "cdf"
}
@module("jstat") external sum: array<float> => float = "sum"

View File

@ -2,21 +2,24 @@
This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator.
## Build for development and production
# Build for development
This one actually works without running `yarn` at the monorepo level, but it doesn't hurt. You must at least run it at this package level
We assume you ran `yarn` at monorepo level.
The website depends on `squiggle-lang`, which you have to build manually.
```sh
yarn
cd ../squiggle-lang
yarn build
```
This command generates static content into the `build` directory and can be served using any static contents hosting service.
Generate static content, to the `build` directory.
```sh
yarn build
```
Your local dev server is here, opening up a browser window.
Open a local dev server
```sh
yarn start

View File

@ -68,15 +68,15 @@ combination of the two. The first positional arguments represent the distributio
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])" />
<SquiggleEditor initialSquiggleString="mx(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])" />
<SquiggleEditor initialSquiggleString="mx(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])" />
<SquiggleEditor initialSquiggleString="mx(3, 8, 1 to 10, [0.2, 0.3, 0.5])" />
## Other Functions

View File

@ -0,0 +1,126 @@
---
title: Statistical properties of algebraic combinations of distributions for property testing.
urlcolor: blue
author:
- Nuño Sempere
- Quinn Dougherty
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.
---
_This document right now is normative and aspirational, not a description of the testing that's currently done_.
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
### Sums
$$
mean(f+g) = mean(f) + mean(g)
$$
$$
\sigma(f+g) = \sqrt{\sigma(f)^2 + \sigma(g)^2}
$$
In the case of normal distributions,
$$
mean(normal(a,b) + normal(c,d)) = mean(normal(a+c, \sqrt{b^2 + d^2}))
$$
### Subtractions
$$
mean(f-g) = mean(f) - mean(g)
$$
$$
\sigma(f-g) = \sqrt{\sigma(f)^2 + \sigma(g)^2}
$$
### Multiplications
$$
mean(f \cdot g) = mean(f) \cdot mean(g)
$$
$$
\sigma(f \cdot g) = \sqrt{ (\sigma(f)^2 + mean(f)) \cdot (\sigma(g)^2 + mean(g)) - (mean(f) \cdot mean(g))^2}
$$
### 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)
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
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:
$$
(f + g)(z)= \int_{-\infty}^{\infty} f(x)\cdot g(z-x) \,dx
$$
See a proof sketch [here](https://www.milefoot.com/math/stat/rv-sums.htm)
Here is some pseudocode to approximate this:
```js
// pdf1 and pdf2 are pdfs,
// and cdf1 and cdf2 are their corresponding cdfs
let epsilonForBounds = 2 ** -16;
let getBounds = (cdf) => {
let cdf_min = -1;
let cdf_max = 1;
let n = 0;
while (
(cdf(cdf_min) > epsilonForBounds || 1 - cdf(cdf_max) > epsilonForBounds) &&
n < 10
) {
if (cdf(cdf_min) > epsilonForBounds) {
cdf_min = cdf_min * 2;
}
if (1 - cdf(cdf_max) > epsilonForBounds) {
cdf_max = cdf_max * 2;
}
}
return [cdf_min, cdf_max];
};
let epsilonForIntegrals = 2 ** -16;
let pdfOfSum = (pdf1, pdf2, cdf1, cdf2, z) => {
let bounds1 = getBounds(cdf1);
let bounds2 = getBounds(cdf2);
let bounds = [
Math.min(bounds1[0], bounds2[0]),
Math.max(bounds1[1], bounds2[1]),
];
let result = 0;
for (let x = bounds[0]; (x = x + epsilonForIntegrals); x < bounds[1]) {
let delta = pdf1(x) * pdf2(z - x);
result = result + delta * epsilonForIntegrals;
}
return result;
};
```
## Cumulative density functions
TODO
## Inverse cumulative density functions
TODO
# To do:
- Provide sources or derivations, useful as this document becomes more complicated
- Provide definitions for the probability density function, exponential, inverse, log, etc.
- Provide at least some tests for division
- See if playing around with characteristic functions turns out anything useful

View File

@ -1,5 +1,7 @@
// @ts-check
// Note: type annotations allow type checking and IDEs autocompletion
const math = require("remark-math");
const katex = require("rehype-katex");
const lightCodeTheme = require("prism-react-renderer/themes/github");
const darkCodeTheme = require("prism-react-renderer/themes/dracula");
@ -14,7 +16,7 @@ const config = {
onBrokenLinks: "throw",
onBrokenMarkdownLinks: "warn",
favicon: "img/favicon.ico",
organizationName: "QURIResearch", // Usually your GitHub org/user name.
organizationName: "quantified-uncertainty", // Usually your GitHub org/user name.
projectName: "squiggle", // Usually your repo name.
plugins: [
@ -47,13 +49,15 @@ const config = {
sidebarPath: require.resolve("./sidebars.js"),
// Please change this to your repo.
editUrl:
"https://github.com/foretold-app/squiggle/tree/master/packages/website/",
"https://github.com/quantified-uncertainty/squiggle/tree/master/packages/website/",
remarkPlugins: [math],
rehypePlugins: [katex],
},
blog: {
showReadingTime: true,
// Please change this to your repo.
editUrl:
"https://github.com/foretold-app/squiggle/tree/master/packages/website/",
"https://github.com/quantified-uncertainty/squiggle/tree/master/packages/website/",
},
theme: {
customCss: require.resolve("./src/css/custom.css"),
@ -111,6 +115,15 @@ const config = {
darkTheme: darkCodeTheme,
},
}),
stylesheets: [
{
href: "https://cdn.jsdelivr.net/npm/katex@0.13.24/dist/katex.min.css",
type: "text/css",
integrity:
"sha384-odtC+0UGzzFL/6PNoE8rX/SPcQDXBJ+uRepguP4QkPCm2LBxH3FA3y+fKSiJ+AmM",
crossorigin: "anonymous",
},
],
};
module.exports = config;

View File

@ -17,7 +17,10 @@
"clsx": "^1.1.1",
"prism-react-renderer": "^1.2.1",
"react": "^18.0.0",
"react-dom": "^18.0.0"
"react-dom": "^18.0.0",
"remark-math": "^3",
"rehype-katex": "^5",
"hast-util-is-element": "1.1.0"
},
"browserslist": {
"production": [

View File

@ -40,6 +40,16 @@ const sidebars = {
},
],
},
{
type: "category",
label: "Internal",
items: [
{
type: "autogenerated",
dirName: "Internal",
},
],
},
],
// But you can create a sidebar manually

120
yarn.lock
View File

@ -3890,6 +3890,11 @@
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4=
"@types/katex@^0.11.0":
version "0.11.1"
resolved "https://registry.yarnpkg.com/@types/katex/-/katex-0.11.1.tgz#34de04477dcf79e2ef6c8d23b41a3d81f9ebeaf5"
integrity sha512-DUlIj2nk0YnJdlWgsFuVKcX27MLW0KbKmGVoUHmFr+74FYYNUDAaj9ZqTADvsbE8rfxuVmSFc7KczYn5Y09ozg==
"@types/lodash@^4.14.181":
version "4.14.181"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.181.tgz#d1d3740c379fda17ab175165ba04e2d03389385d"
@ -4025,10 +4030,10 @@
dependencies:
"@types/react" "*"
"@types/react@*", "@types/react@^16.9.19", "@types/react@^18.0.1":
version "18.0.2"
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.2.tgz#bc6a0572d434642ebe8b4ac0f121d18e2f2d8f7f"
integrity sha512-2poV9ReTwwV5ZNxkKyk7t6Vp/odeTfYI3vRjtDYWfUdEstx9mp26jzELfMBwV6gXg1irhHUnmZJH/dJW7xafcA==
"@types/react@*", "@types/react@^16.9.19", "@types/react@^18.0.1", "@types/react@^18.0.3":
version "18.0.3"
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.3.tgz#baefa397561372015b9f8ba5bc83bc3f84ae8fcb"
integrity sha512-P8QUaMW4k+kH9aKNPl9b3XWcKMSSALYprLL8xpAMJOLUn3Pl6B+6nKC4F7dsk9oJPwkiRx+qlwhG/Zc1LxFVuQ==
dependencies:
"@types/prop-types" "*"
"@types/scheduler" "*"
@ -6394,7 +6399,7 @@ commander@^6.2.1:
resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==
commander@^8.3.0:
commander@^8.0.0, commander@^8.3.0:
version "8.3.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66"
integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==
@ -7936,10 +7941,10 @@ escodegen@^2.0.0:
optionalDependencies:
source-map "~0.6.1"
eslint-config-react-app@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/eslint-config-react-app/-/eslint-config-react-app-7.0.0.tgz#0fa96d5ec1dfb99c029b1554362ab3fa1c3757df"
integrity sha512-xyymoxtIt1EOsSaGag+/jmcywRuieQoA2JbPCjnw9HukFj9/97aGPoZVFioaotzk1K5Qt9sHO5EutZbkrAXS0g==
eslint-config-react-app@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz#73ba3929978001c5c86274c017ea57eb5fa644b4"
integrity sha512-K6rNzvkIeHaTd8m/QEh1Zko0KI7BACWkkneSs6s9cKZC/J27X3eZR6Upt1jkmZ/4FK+XUOPPxMEN7+lbUXfSlA==
dependencies:
"@babel/core" "^7.16.0"
"@babel/eslint-parser" "^7.16.3"
@ -9278,6 +9283,11 @@ hast-util-from-parse5@^6.0.0:
vfile-location "^3.2.0"
web-namespaces "^1.0.0"
hast-util-is-element@1.1.0, hast-util-is-element@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/hast-util-is-element/-/hast-util-is-element-1.1.0.tgz#3b3ed5159a2707c6137b48637fbfe068e175a425"
integrity sha512-oUmNua0bFbdrD/ELDSSEadRVtWZOf3iF6Lbv81naqsIV99RnSCieTbWuWCY8BAeEfKJTKl0gRdokv+dELutHGQ==
hast-util-parse-selector@^2.0.0:
version "2.2.5"
resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz#d57c23f4da16ae3c63b3b6ca4616683313499c3a"
@ -9310,6 +9320,15 @@ hast-util-to-parse5@^6.0.0:
xtend "^4.0.0"
zwitch "^1.0.0"
hast-util-to-text@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/hast-util-to-text/-/hast-util-to-text-2.0.1.tgz#04f2e065642a0edb08341976084aa217624a0f8b"
integrity sha512-8nsgCARfs6VkwH2jJU9b8LNTuR4700na+0h3PqCaEk4MAnMDeu5P0tP8mjk9LLNGxIeQRLbiDbZVw6rku+pYsQ==
dependencies:
hast-util-is-element "^1.0.0"
repeat-string "^1.0.0"
unist-util-find-after "^3.0.0"
hastscript@^5.0.0:
version "5.1.2"
resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-5.1.2.tgz#bde2c2e56d04c62dd24e8c5df288d050a355fb8a"
@ -10968,6 +10987,13 @@ junk@^3.1.0:
resolved "https://registry.yarnpkg.com/junk/-/junk-3.1.0.tgz#31499098d902b7e98c5d9b9c80f43457a88abfa1"
integrity sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==
katex@^0.13.0:
version "0.13.24"
resolved "https://registry.yarnpkg.com/katex/-/katex-0.13.24.tgz#fe55455eb455698cb24b911a353d16a3c855d905"
integrity sha512-jZxYuKCma3VS5UuxOx/rFV1QyGSl3Uy/i0kTJF3HgQ5xMinCQVF8Zd4bMY/9aI9b9A2pjIBOsjSSm68ykTAr8w==
dependencies:
commander "^8.0.0"
keyv@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9"
@ -14036,10 +14062,10 @@ react-colorful@^5.1.2:
resolved "https://registry.yarnpkg.com/react-colorful/-/react-colorful-5.5.1.tgz#29d9c4e496f2ca784dd2bb5053a3a4340cfaf784"
integrity sha512-M1TJH2X3RXEt12sWkpa6hLc/bbYS0H6F4rIqjQZ+RxNBstpY67d9TrFXtqdZwhpmBXcCwEi7stKqFue3ZRkiOg==
react-dev-utils@^12.0.0:
version "12.0.0"
resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-12.0.0.tgz#4eab12cdb95692a077616770b5988f0adf806526"
integrity sha512-xBQkitdxozPxt1YZ9O1097EJiVpwHr9FoAuEVURCKV0Av8NBERovJauzP7bo1ThvuhZ4shsQ1AJiu4vQpoT1AQ==
react-dev-utils@^12.0.0, react-dev-utils@^12.0.1:
version "12.0.1"
resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-12.0.1.tgz#ba92edb4a1f379bd46ccd6bcd4e7bc398df33e73"
integrity sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==
dependencies:
"@babel/code-frame" "^7.16.0"
address "^1.1.2"
@ -14060,7 +14086,7 @@ react-dev-utils@^12.0.0:
open "^8.4.0"
pkg-up "^3.1.0"
prompts "^2.4.2"
react-error-overlay "^6.0.10"
react-error-overlay "^6.0.11"
recursive-readdir "^2.2.2"
shell-quote "^1.7.3"
strip-ansi "^6.0.1"
@ -14112,10 +14138,10 @@ react-element-to-jsx-string@^14.3.4:
is-plain-object "5.0.0"
react-is "17.0.2"
react-error-overlay@^6.0.10:
version "6.0.10"
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.10.tgz#0fe26db4fa85d9dbb8624729580e90e7159a59a6"
integrity sha512-mKR90fX7Pm5seCOfz8q9F+66VCc1PGsWSBxKbITjfKVQHMNF2zudxHnMdJiB1fRCb+XsbQV9sO9DCkgsMQgBIA==
react-error-overlay@^6.0.11:
version "6.0.11"
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.11.tgz#92835de5841c5cf08ba00ddd2d677b6d17ff9adb"
integrity sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==
react-fast-compare@^3.0.1, react-fast-compare@^3.2.0:
version "3.2.0"
@ -14247,10 +14273,10 @@ react-router@6.3.0, react-router@^6.0.0:
dependencies:
history "^5.2.0"
react-scripts@5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/react-scripts/-/react-scripts-5.0.0.tgz#6547a6d7f8b64364ef95273767466cc577cb4b60"
integrity sha512-3i0L2CyIlROz7mxETEdfif6Sfhh9Lfpzi10CtcGs1emDQStmZfWjJbAIMtRD0opVUjQuFWqHZyRZ9PPzKCFxWg==
react-scripts@5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/react-scripts/-/react-scripts-5.0.1.tgz#6285dbd65a8ba6e49ca8d651ce30645a6d980003"
integrity sha512-8VAmEm/ZAwQzJ+GOMLbBsTdDKOpuZh7RPs0UymvBR2vRk4iZWCskjbFnxqjrzoIvlNNRZ3QJFx6/qDSi6zSnaQ==
dependencies:
"@babel/core" "^7.16.0"
"@pmmmwh/react-refresh-webpack-plugin" "^0.5.3"
@ -14268,7 +14294,7 @@ react-scripts@5.0.0:
dotenv "^10.0.0"
dotenv-expand "^5.1.0"
eslint "^8.3.0"
eslint-config-react-app "^7.0.0"
eslint-config-react-app "^7.0.1"
eslint-webpack-plugin "^3.1.1"
file-loader "^6.2.0"
fs-extra "^10.0.0"
@ -14285,7 +14311,7 @@ react-scripts@5.0.0:
postcss-preset-env "^7.0.1"
prompts "^2.4.2"
react-app-polyfill "^3.0.0"
react-dev-utils "^12.0.0"
react-dev-utils "^12.0.1"
react-refresh "^0.11.0"
resolve "^1.20.0"
resolve-url-loader "^4.0.0"
@ -14541,6 +14567,18 @@ regjsparser@^0.8.2:
dependencies:
jsesc "~0.5.0"
rehype-katex@^5:
version "5.0.0"
resolved "https://registry.yarnpkg.com/rehype-katex/-/rehype-katex-5.0.0.tgz#b556f24fde918f28ba1cb642ea71c7e82f3373d7"
integrity sha512-ksSuEKCql/IiIadOHiKRMjypva9BLhuwQNascMqaoGLDVd0k2NlE2wMvgZ3rpItzRKCd6vs8s7MFbb8pcR0AEg==
dependencies:
"@types/katex" "^0.11.0"
hast-util-to-text "^2.0.0"
katex "^0.13.0"
rehype-parse "^7.0.0"
unified "^9.0.0"
unist-util-visit "^2.0.0"
rehype-parse@^6.0.2:
version "6.0.2"
resolved "https://registry.yarnpkg.com/rehype-parse/-/rehype-parse-6.0.2.tgz#aeb3fdd68085f9f796f1d3137ae2b85a98406964"
@ -14550,6 +14588,14 @@ rehype-parse@^6.0.2:
parse5 "^5.0.0"
xtend "^4.0.0"
rehype-parse@^7.0.0:
version "7.0.1"
resolved "https://registry.yarnpkg.com/rehype-parse/-/rehype-parse-7.0.1.tgz#58900f6702b56767814afc2a9efa2d42b1c90c57"
integrity sha512-fOiR9a9xH+Le19i4fGzIEowAbwG7idy2Jzs4mOrFWBSJ0sNUgy0ev871dwWnbOo371SjgjG4pwzrbgSVrKxecw==
dependencies:
hast-util-from-parse5 "^6.0.0"
parse5 "^6.0.0"
relateurl@^0.2.7:
version "0.2.7"
resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
@ -14589,6 +14635,11 @@ remark-footnotes@2.0.0:
resolved "https://registry.yarnpkg.com/remark-footnotes/-/remark-footnotes-2.0.0.tgz#9001c4c2ffebba55695d2dd80ffb8b82f7e6303f"
integrity sha512-3Clt8ZMH75Ayjp9q4CorNeyjwIxHFcTkaektplKGl2A1jNGEUey8cKL0ZC5vJwfcD5GFGsNLImLG/NGzWIzoMQ==
remark-math@^3:
version "3.0.1"
resolved "https://registry.yarnpkg.com/remark-math/-/remark-math-3.0.1.tgz#85a02a15b15cad34b89a27244d4887b3a95185bb"
integrity sha512-epT77R/HK0x7NqrWHdSV75uNLwn8g9qTyMqCRCDujL0vj/6T6+yhdrR7mjELWtkse+Fw02kijAaBuVcHBor1+Q==
remark-mdx@1.6.22:
version "1.6.22"
resolved "https://registry.yarnpkg.com/remark-mdx/-/remark-mdx-1.6.22.tgz#06a8dab07dcfdd57f3373af7f86bd0e992108bbd"
@ -14673,7 +14724,7 @@ repeat-element@^1.1.2:
resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9"
integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==
repeat-string@^1.5.4, repeat-string@^1.6.1:
repeat-string@^1.0.0, repeat-string@^1.5.4, repeat-string@^1.6.1:
version "1.6.1"
resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc=
@ -16509,6 +16560,18 @@ unified@^8.4.2:
trough "^1.0.0"
vfile "^4.0.0"
unified@^9.0.0:
version "9.2.2"
resolved "https://registry.yarnpkg.com/unified/-/unified-9.2.2.tgz#67649a1abfc3ab85d2969502902775eb03146975"
integrity sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ==
dependencies:
bail "^1.0.0"
extend "^3.0.0"
is-buffer "^2.0.0"
is-plain-obj "^2.0.0"
trough "^1.0.0"
vfile "^4.0.0"
union-value@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847"
@ -16545,6 +16608,13 @@ unist-builder@2.0.3, unist-builder@^2.0.0:
resolved "https://registry.yarnpkg.com/unist-builder/-/unist-builder-2.0.3.tgz#77648711b5d86af0942f334397a33c5e91516436"
integrity sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw==
unist-util-find-after@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/unist-util-find-after/-/unist-util-find-after-3.0.0.tgz#5c65fcebf64d4f8f496db46fa8fd0fbf354b43e6"
integrity sha512-ojlBqfsBftYXExNu3+hHLfJQ/X1jYY/9vdm4yZWjIbf0VuWF6CRufci1ZyoD/wV2TYMKxXUoNuoqwy+CkgzAiQ==
dependencies:
unist-util-is "^4.0.0"
unist-util-generated@^1.0.0:
version "1.1.6"
resolved "https://registry.yarnpkg.com/unist-util-generated/-/unist-util-generated-1.1.6.tgz#5ab51f689e2992a472beb1b35f2ce7ff2f324d4b"