Merge pull request #221 from QURIresearch/ts-interface-typing

Ts interface typing
This commit is contained in:
Sam Nolan 2022-04-11 11:25:59 +10:00 committed by GitHub
commit abb41871d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 107 additions and 100 deletions

View File

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

View File

@ -1,7 +1,9 @@
# Squiggle language # Squiggle language
## Build for development ## Build for development
We assume that you ran `yarn` at the monorepo level. We assume that you ran `yarn` at the monorepo level.
```sh ```sh
yarn build yarn build
``` ```
@ -9,6 +11,7 @@ yarn build
`yarn bundle` is needed for a deployment. `yarn bundle` is needed for a deployment.
Other: Other:
```sh ```sh
yarn start # listens to files and recompiles at every mutation yarn start # listens to files and recompiles at every mutation
yarn test yarn test
@ -19,6 +22,7 @@ yarn coverage; o _coverage/index.html # produces coverage report and opens it i
``` ```
## Information ## Information
Squiggle is a language for representing probability distributions, as well as functions that return probability distributions. Its original intended use is for improving epistemics around EA decisions. Squiggle is a language for representing probability distributions, as well as functions that return probability distributions. Its original intended use is for improving epistemics around EA decisions.
This package, `@quri/squiggle-lang`, contains the core language of squiggle. The main feature revolves around evaluating squiggle expressions. Currently the package only exports a single function, named "run", which from a squiggle string returns an object representing the result of the evaluation. This package, `@quri/squiggle-lang`, contains the core language of squiggle. The main feature revolves around evaluating squiggle expressions. Currently the package only exports a single function, named "run", which from a squiggle string returns an object representing the result of the evaluation.
@ -32,7 +36,9 @@ ReScript has an interesting philosophy of not providing much in the way of effec
`.gen.ts` files are created by the [`@genType`](https://rescript-lang.org/docs/gentype/latest/getting-started) decorator, which creates typescript typings for needed parts of the codebase so that they can be easily used in typescript. These .gen.ts files reference the .bs.js files generated by rescript. `.gen.ts` files are created by the [`@genType`](https://rescript-lang.org/docs/gentype/latest/getting-started) decorator, which creates typescript typings for needed parts of the codebase so that they can be easily used in typescript. These .gen.ts files reference the .bs.js files generated by rescript.
### Errors regarding the `rationale` package ### Errors regarding the `rationale` package
You may notice sometimes, that there are errors about the `rationale` package. If you ever get these errors, `yarn build` should fix this issue. These errors occur because `yarn build` also needs to create build files that are in `node_modules`. So if you replace `node_modules` you may need to rebuild to get those files back. You may notice sometimes, that there are errors about the `rationale` package. If you ever get these errors, `yarn build` should fix this issue. These errors occur because `yarn build` also needs to create build files that are in `node_modules`. So if you replace `node_modules` you may need to rebuild to get those files back.
## Distributing this package or using this package from other monorepo packages ## Distributing this package or using this package from other monorepo packages
As it says in the other `packages/*/README.md`s, building this package is an essential step of building other packages. As it says in the other `packages/*/README.md`s, building this package is an essential step of building other packages.

View File

@ -1,4 +1,4 @@
import { run, GenericDist, resultMap, makeSampleSetDist } from "../src/js/index"; import { run, Distribution, resultMap } from "../src/js/index";
let testRun = (x: string) => { let testRun = (x: string) => {
let result = run(x); let result = run(x);
@ -9,6 +9,10 @@ let testRun = (x: string) => {
} }
}; };
function Ok<b>(x: b) {
return { tag: "Ok", value: x };
}
describe("Simple calculations and results", () => { describe("Simple calculations and results", () => {
test("mean(normal(5,2))", () => { test("mean(normal(5,2))", () => {
expect(testRun("mean(normal(5,2))")).toEqual({ expect(testRun("mean(normal(5,2))")).toEqual({
@ -38,16 +42,15 @@ describe("Multimodal too many weights error", () => {
}); });
}); });
describe("GenericDist", () => { describe("Distribution", () => {
//It's important that sampleCount is less than 9. If it's more, than that will create randomness //It's important that sampleCount is less than 9. If it's more, than that will create randomness
//Also, note, the value should be created using makeSampleSetDist() later on. //Also, note, the value should be created using makeSampleSetDist() later on.
let env = { sampleCount: 8, xyPointLength: 100 }; let env = { sampleCount: 8, xyPointLength: 100 };
let dist = new GenericDist( let dist = new Distribution(
{ tag: "SampleSet", value: [3, 4, 5, 6, 6, 7, 10, 15, 30] }, { tag: "SampleSet", value: [3, 4, 5, 6, 6, 7, 10, 15, 30] },
env env
); );
let dist2 = new GenericDist( let dist2 = new Distribution(
{ tag: "SampleSet", value: [20, 22, 24, 29, 30, 35, 38, 44, 52] }, { tag: "SampleSet", value: [20, 22, 24, 29, 30, 35, 38, 44, 52] },
env env
); );
@ -66,22 +69,24 @@ describe("GenericDist", () => {
}); });
test("toPointSet", () => { test("toPointSet", () => {
expect( expect(
resultMap(dist.toPointSet(), (r: GenericDist) => r.toString()).value.value resultMap(dist.toPointSet(), (r: Distribution) => r.toString()).value
).toBe("Point Set Distribution"); ).toEqual(Ok("Point Set Distribution"));
}); });
test("toSparkline", () => { test("toSparkline", () => {
expect(dist.toSparkline(20).value).toBe("▁▁▃▅███▆▄▃▂▁▁▂▂▃▂▁▁▁"); expect(dist.toSparkline(20).value).toEqual("▁▁▃▅███▆▄▃▂▁▁▂▂▃▂▁▁▁");
}); });
test("algebraicAdd", () => { test("algebraicAdd", () => {
expect( expect(
resultMap(dist.algebraicAdd(dist2), (r: GenericDist) => r.toSparkline(20)) resultMap(dist.algebraicAdd(dist2), (r: Distribution) =>
.value.value r.toSparkline(20)
).toBe("▁▁▂▄▆████▇▆▄▄▃▃▃▂▁▁▁"); ).value
).toEqual(Ok("▁▁▂▄▆████▇▆▄▄▃▃▃▂▁▁▁"));
}); });
test("pointwiseAdd", () => { test("pointwiseAdd", () => {
expect( expect(
resultMap(dist.pointwiseAdd(dist2), (r: GenericDist) => r.toSparkline(20)) resultMap(dist.pointwiseAdd(dist2), (r: Distribution) =>
.value.value r.toSparkline(20)
).toBe("▁▂▅██▅▅▅▆▇█▆▅▃▃▂▂▁▁▁"); ).value
).toEqual(Ok("▁▂▅██▅▅▅▆▇█▆▅▃▃▂▂▁▁▁"));
}); });
}); });

View File

@ -11,11 +11,7 @@
"subdirs": true "subdirs": true
} }
], ],
"bsc-flags": [ "bsc-flags": ["-bs-super-errors", "-bs-no-version-header", "-bs-g"],
"-bs-super-errors",
"-bs-no-version-header",
"-bs-g"
],
"package-specs": [ "package-specs": [
{ {
"module": "commonjs", "module": "commonjs",
@ -44,10 +40,6 @@
"number": "+A-42-48-9-30-4-102-20-27-41" "number": "+A-42-48-9-30-4-102-20-27-41"
}, },
"ppx-flags": [ "ppx-flags": [
[ ["../../node_modules/bisect_ppx/ppx", "--exclude-files", ".*_test\\.res$$"]
"../../node_modules/bisect_ppx/ppx",
"--exclude-files",
".*_test\\.res$$"
]
] ]
} }

View File

@ -7,13 +7,7 @@ import type {
} from "../rescript/ProgramEvaluator.gen"; } from "../rescript/ProgramEvaluator.gen";
export type { SamplingInputs, exportEnv, exportDistribution }; export type { SamplingInputs, exportEnv, exportDistribution };
export type { t as DistPlus } from "../rescript/OldInterpreter/DistPlus.gen"; export type { t as DistPlus } from "../rescript/OldInterpreter/DistPlus.gen";
import { import { genericDist, env, error } from "../rescript/TypescriptInterface.gen";
genericDist,
env,
resultDist,
resultFloat,
resultString,
} from "../rescript/TypescriptInterface.gen";
export { makeSampleSetDist } from "../rescript/TypescriptInterface.gen"; export { makeSampleSetDist } from "../rescript/TypescriptInterface.gen";
import { import {
Constructors_mean, Constructors_mean,
@ -52,7 +46,7 @@ export function run(
squiggleString: string, squiggleString: string,
samplingInputs?: SamplingInputs, samplingInputs?: SamplingInputs,
environment?: exportEnv environment?: exportEnv
): { tag: "Ok"; value: exportType } | { tag: "Error"; value: string } { ): result<exportType, string> {
let si: SamplingInputs = samplingInputs let si: SamplingInputs = samplingInputs
? samplingInputs ? samplingInputs
: defaultSamplingInputs; : defaultSamplingInputs;
@ -60,19 +54,20 @@ export function run(
return runAll(squiggleString, si, env); return runAll(squiggleString, si, env);
} }
//This is clearly not fully typed. I think later we should use a functional library to type result<a, b> =
// provide a better Either type and corresponding functions.
type result =
| { | {
tag: "Ok"; tag: "Ok";
value: any; value: a;
} }
| { | {
tag: "Error"; tag: "Error";
value: any; value: b;
}; };
export function resultMap(r: result, mapFn: any): result { export function resultMap<a, b, c>(
r: result<a, c>,
mapFn: (x: a) => b
): result<b, c> {
if (r.tag === "Ok") { if (r.tag === "Ok") {
return { tag: "Ok", value: mapFn(r.value) }; return { tag: "Ok", value: mapFn(r.value) };
} else { } else {
@ -80,11 +75,11 @@ export function resultMap(r: result, mapFn: any): result {
} }
} }
export function resultExn(r: result): any { export function resultExn<a, c>(r: result<a, c>): a | c {
r.value return r.value;
} }
export class GenericDist { export class Distribution {
t: genericDist; t: genericDist;
env: env; env: env;
@ -94,133 +89,133 @@ export class GenericDist {
return this; return this;
} }
mapResultDist(r: resultDist) { mapResultDist(r: result<genericDist, error>): result<Distribution, error> {
return resultMap(r, (v: genericDist) => new GenericDist(v, this.env)); return resultMap(r, (v: genericDist) => new Distribution(v, this.env));
} }
mean() { mean(): result<number, error> {
return Constructors_mean({ env: this.env }, this.t); return Constructors_mean({ env: this.env }, this.t);
} }
sample(): resultFloat { sample(): result<number, error> {
return Constructors_sample({ env: this.env }, this.t); return Constructors_sample({ env: this.env }, this.t);
} }
pdf(n: number): resultFloat { pdf(n: number): result<number, error> {
return Constructors_pdf({ env: this.env }, this.t, n); return Constructors_pdf({ env: this.env }, this.t, n);
} }
cdf(n: number): resultFloat { cdf(n: number): result<number, error> {
return Constructors_cdf({ env: this.env }, this.t, n); return Constructors_cdf({ env: this.env }, this.t, n);
} }
inv(n: number): resultFloat { inv(n: number): result<number, error> {
return Constructors_inv({ env: this.env }, this.t, n); return Constructors_inv({ env: this.env }, this.t, n);
} }
normalize() { normalize(): result<Distribution, error> {
return this.mapResultDist( return this.mapResultDist(
Constructors_normalize({ env: this.env }, this.t) Constructors_normalize({ env: this.env }, this.t)
); );
} }
toPointSet() { toPointSet(): result<Distribution, error> {
return this.mapResultDist( return this.mapResultDist(
Constructors_toPointSet({ env: this.env }, this.t) Constructors_toPointSet({ env: this.env }, this.t)
); );
} }
toSampleSet(n: number) { toSampleSet(n: number): result<Distribution, error> {
return this.mapResultDist( return this.mapResultDist(
Constructors_toSampleSet({ env: this.env }, this.t, n) Constructors_toSampleSet({ env: this.env }, this.t, n)
); );
} }
truncate(left: number, right: number) { truncate(left: number, right: number): result<Distribution, error> {
return this.mapResultDist( return this.mapResultDist(
Constructors_truncate({ env: this.env }, this.t, left, right) Constructors_truncate({ env: this.env }, this.t, left, right)
); );
} }
inspect() { inspect(): result<Distribution, error> {
return this.mapResultDist(Constructors_inspect({ env: this.env }, this.t)); return this.mapResultDist(Constructors_inspect({ env: this.env }, this.t));
} }
toString(): resultString { toString(): result<string, error> {
return Constructors_toString({ env: this.env }, this.t); return Constructors_toString({ env: this.env }, this.t);
} }
toSparkline(n: number): resultString { toSparkline(n: number): result<string, error> {
return Constructors_toSparkline({ env: this.env }, this.t, n); return Constructors_toSparkline({ env: this.env }, this.t, n);
} }
algebraicAdd(d2: GenericDist) { algebraicAdd(d2: Distribution): result<Distribution, error> {
return this.mapResultDist( return this.mapResultDist(
Constructors_algebraicAdd({ env: this.env }, this.t, d2.t) Constructors_algebraicAdd({ env: this.env }, this.t, d2.t)
); );
} }
algebraicMultiply(d2: GenericDist) { algebraicMultiply(d2: Distribution): result<Distribution, error> {
return this.mapResultDist( return this.mapResultDist(
Constructors_algebraicMultiply({ env: this.env }, this.t, d2.t) Constructors_algebraicMultiply({ env: this.env }, this.t, d2.t)
); );
} }
algebraicDivide(d2: GenericDist) { algebraicDivide(d2: Distribution): result<Distribution, error> {
return this.mapResultDist( return this.mapResultDist(
Constructors_algebraicDivide({ env: this.env }, this.t, d2.t) Constructors_algebraicDivide({ env: this.env }, this.t, d2.t)
); );
} }
algebraicSubtract(d2: GenericDist) { algebraicSubtract(d2: Distribution): result<Distribution, error> {
return this.mapResultDist( return this.mapResultDist(
Constructors_algebraicSubtract({ env: this.env }, this.t, d2.t) Constructors_algebraicSubtract({ env: this.env }, this.t, d2.t)
); );
} }
algebraicLogarithm(d2: GenericDist) { algebraicLogarithm(d2: Distribution): result<Distribution, error> {
return this.mapResultDist( return this.mapResultDist(
Constructors_algebraicLogarithm({ env: this.env }, this.t, d2.t) Constructors_algebraicLogarithm({ env: this.env }, this.t, d2.t)
); );
} }
algebraicPower(d2: GenericDist) { algebraicPower(d2: Distribution): result<Distribution, error> {
return this.mapResultDist( return this.mapResultDist(
Constructors_algebraicPower({ env: this.env }, this.t, d2.t) Constructors_algebraicPower({ env: this.env }, this.t, d2.t)
); );
} }
pointwiseAdd(d2: GenericDist) { pointwiseAdd(d2: Distribution): result<Distribution, error> {
return this.mapResultDist( return this.mapResultDist(
Constructors_pointwiseAdd({ env: this.env }, this.t, d2.t) Constructors_pointwiseAdd({ env: this.env }, this.t, d2.t)
); );
} }
pointwiseMultiply(d2: GenericDist) { pointwiseMultiply(d2: Distribution): result<Distribution, error> {
return this.mapResultDist( return this.mapResultDist(
Constructors_pointwiseMultiply({ env: this.env }, this.t, d2.t) Constructors_pointwiseMultiply({ env: this.env }, this.t, d2.t)
); );
} }
pointwiseDivide(d2: GenericDist) { pointwiseDivide(d2: Distribution): result<Distribution, error> {
return this.mapResultDist( return this.mapResultDist(
Constructors_pointwiseDivide({ env: this.env }, this.t, d2.t) Constructors_pointwiseDivide({ env: this.env }, this.t, d2.t)
); );
} }
pointwiseSubtract(d2: GenericDist) { pointwiseSubtract(d2: Distribution): result<Distribution, error> {
return this.mapResultDist( return this.mapResultDist(
Constructors_pointwiseSubtract({ env: this.env }, this.t, d2.t) Constructors_pointwiseSubtract({ env: this.env }, this.t, d2.t)
); );
} }
pointwiseLogarithm(d2: GenericDist) { pointwiseLogarithm(d2: Distribution): result<Distribution, error> {
return this.mapResultDist( return this.mapResultDist(
Constructors_pointwiseLogarithm({ env: this.env }, this.t, d2.t) Constructors_pointwiseLogarithm({ env: this.env }, this.t, d2.t)
); );
} }
pointwisePower(d2: GenericDist) { pointwisePower(d2: Distribution): result<Distribution, error> {
return this.mapResultDist( return this.mapResultDist(
Constructors_pointwisePower({ env: this.env }, this.t, d2.t) Constructors_pointwisePower({ env: this.env }, this.t, d2.t)
); );

View File

@ -4,11 +4,12 @@ This library provides one interface to generic distributions. These distribution
Different internal formats (symbolic, point set, sample set) allow for benefits and features. It's common for distributions to be converted into either point sets or sample sets to enable certain functions. Different internal formats (symbolic, point set, sample set) allow for benefits and features. It's common for distributions to be converted into either point sets or sample sets to enable certain functions.
In addition to this interface, there's a second, generic function, for calling functions on this generic distribution type. This ``genericOperation`` standardizes the inputs and outputs for these various function calls. See it's ``run()`` function. In addition to this interface, there's a second, generic function, for calling functions on this generic distribution type. This `genericOperation` standardizes the inputs and outputs for these various function calls. See it's `run()` function.
Performance is very important. Some operations can take a long time to run, and even then, be inaccurate. Because of this, we plan to have a lot of logging and stack tracing functionality eventually built in. Performance is very important. Some operations can take a long time to run, and even then, be inaccurate. Because of this, we plan to have a lot of logging and stack tracing functionality eventually built in.
## Diagram of Distribution Types ## Diagram of Distribution Types
```mermaid ```mermaid
graph TD graph TD
A[Generic Distribution] -->B{Point Set} A[Generic Distribution] -->B{Point Set}
@ -34,6 +35,7 @@ graph TD
## Diagram of Generic Distribution Types ## Diagram of Generic Distribution Types
## Todo ## Todo
- [ ] Lots of cleanup - [ ] Lots of cleanup
- [ ] Simple test story - [ ] Simple test story
- [ ] Provide decent stack traces for key calls in GenericOperation. This could be very useful for debugging. - [ ] Provide decent stack traces for key calls in GenericOperation. This could be very useful for debugging.

View File

@ -1,4 +1,4 @@
const pdfast = require('pdfast'); const pdfast = require("pdfast");
const _ = require("lodash"); const _ = require("lodash");
const samplesToContinuousPdf = ( const samplesToContinuousPdf = (
@ -6,13 +6,17 @@ const samplesToContinuousPdf = (
size, size,
width, width,
min = false, min = false,
max = false, max = false
) => { ) => {
let _samples = _.filter(samples, _.isFinite); let _samples = _.filter(samples, _.isFinite);
if (_.isFinite(min)) { _samples = _.filter(_samples, r => r > min) }; if (_.isFinite(min)) {
if (_.isFinite(max)) { _samples = _.filter(_samples, r => r < max) }; _samples = _.filter(_samples, (r) => r > min);
}
if (_.isFinite(max)) {
_samples = _.filter(_samples, (r) => r < max);
}
let pdf = pdfast.create(_samples, { size, width }); let pdf = pdfast.create(_samples, { size, width });
return {xs: pdf.map(r => r.x), ys: pdf.map(r => r.y)}; return { xs: pdf.map((r) => r.x), ys: pdf.map((r) => r.y) };
}; };
module.exports = { module.exports = {

View File

@ -1,8 +1,8 @@
const math = require("mathjs"); const math = require("mathjs");
function parseMath(f) { function parseMath(f) {
return JSON.parse(JSON.stringify(math.parse(f))) return JSON.parse(JSON.stringify(math.parse(f)));
}; }
module.exports = { module.exports = {
parseMath, parseMath,

View File

@ -1,26 +1,26 @@
const path = require('path'); const path = require("path");
module.exports = { module.exports = {
mode: 'production', mode: "production",
entry: './src/js/index.ts', entry: "./src/js/index.ts",
module: { module: {
rules: [ rules: [
{ {
test: /\.tsx?$/, test: /\.tsx?$/,
use: 'ts-loader', use: "ts-loader",
exclude: /node_modules/, exclude: /node_modules/,
}, },
], ],
}, },
resolve: { resolve: {
extensions: ['.tsx', '.ts', '.js'], extensions: [".tsx", ".ts", ".js"],
}, },
output: { output: {
filename: 'bundle.js', filename: "bundle.js",
path: path.resolve(__dirname, 'dist'), path: path.resolve(__dirname, "dist"),
library: { library: {
name: 'squiggle_lang', name: "squiggle_lang",
type: 'umd', type: "umd",
}, },
}, },
}; };