From 0b8da034c626a1f3eb0d0bef676004044f9416d4 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Tue, 10 May 2022 19:59:50 -0400 Subject: [PATCH 01/23] `klDivergence` on mixed distributions works for one test case Value: [1e-4 to 5e-2] --- .../Distributions/KlDivergence_test.res | 60 ++++++++++++++++++- .../Distributions/PointSetDist/Mixed.res | 7 +-- .../PointSetDist/PointSetDist.res | 1 + 3 files changed, 63 insertions(+), 5 deletions(-) diff --git a/packages/squiggle-lang/__tests__/Distributions/KlDivergence_test.res b/packages/squiggle-lang/__tests__/Distributions/KlDivergence_test.res index 96e95899..31f03ae2 100644 --- a/packages/squiggle-lang/__tests__/Distributions/KlDivergence_test.res +++ b/packages/squiggle-lang/__tests__/Distributions/KlDivergence_test.res @@ -5,7 +5,6 @@ open GenericDist_Fixtures describe("klDivergence: continuous -> continuous -> float", () => { let klDivergence = DistributionOperation.Constructors.klDivergence(~env) - exception KlFailed let testUniform = (lowAnswer, highAnswer, lowPrediction, highPrediction) => { test("of two uniforms is equal to the analytic expression", () => { @@ -59,6 +58,24 @@ describe("klDivergence: continuous -> continuous -> float", () => { } } }) + + test("of a normal and a uniform is equal to the formula", () => { + let prediction = normalDist10 + let answer = uniformDist + let kl = klDivergence(prediction, answer) + let analyticalKl = + -.Js.Math.log((10.0 -. 9.0) /. Js.Math.sqrt(2.0 *. MagicNumbers.Math.pi *. 2.0 ** 2.0)) +. + 1.0 /. + 2.0 ** 2.0 *. + (10.0 ** 2.0 -. (10.0 +. 9.0) *. 10.0 +. (9.0 ** 2.0 +. 10.0 *. 9.0 +. 10.0 ** 2.0) /. 3.0) + switch kl { + | Ok(kl') => kl'->expect->toBeSoCloseTo(analyticalKl, ~digits=1) + | Error(err) => { + Js.Console.log(DistributionTypes.Error.toString(err)) + raise(KlFailed) + } + } + }) }) describe("klDivergence: discrete -> discrete -> float", () => { @@ -96,6 +113,47 @@ describe("klDivergence: discrete -> discrete -> float", () => { }) }) +describe("klDivergence: mixed -> mixed -> float", () => { + let klDivergence = DistributionOperation.Constructors.klDivergence(~env) + let mixture = a => DistributionTypes.DistributionOperation.Mixture(a) + let a' = [(floatDist, 1e0), (uniformDist, 1e0)]->mixture->run + let b' = [(point3, 1e0), (floatDist, 1e0), (normalDist10, 1e0)]->mixture->run + let (a, b) = switch (a', b') { + | (Dist(a''), Dist(b'')) => (a'', b'') + | _ => raise(MixtureFailed) + } + test("finite klDivergence returns is correct", () => { + let prediction = b + let answer = a + let kl = klDivergence(prediction, answer) + // high = 10; low = 9; mean = 10; stdev = 2 + let analyticalKlContinuousPart = + Js.Math.log(Js.Math.sqrt(2.0 *. MagicNumbers.Math.pi *. 2.0 ** 2.0) /. (10.0 -. 9.0)) +. + 1.0 /. + 2.0 ** 2.0 *. + (10.0 ** 2.0 -. (10.0 +. 9.0) *. 10.0 +. (9.0 ** 2.0 +. 10.0 *. 9.0 +. 10.0 ** 2.0) /. 3.0) + let analyticalKlDiscretePart = -2.0 /. 3.0 *. Js.Math.log(3.0 /. 2.0) + switch kl { + | Ok(kl') => + kl'->expect->toBeSoCloseTo(analyticalKlContinuousPart +. analyticalKlDiscretePart, ~digits=0) + | Error(err) => + Js.Console.log(DistributionTypes.Error.toString(err)) + raise(KlFailed) + } + }) + test("returns infinity when infinite", () => { + let prediction = a + let answer = b + let kl = klDivergence(prediction, answer) + switch kl { + | Ok(kl') => kl'->expect->toEqual(infinity) + | Error(err) => + Js.Console.log(DistributionTypes.Error.toString(err)) + raise(KlFailed) + } + }) +}) + describe("combineAlongSupportOfSecondArgument0", () => { // This tests the version of the function that we're NOT using. Haven't deleted the test in case we use the code later. test("test on two uniforms", _ => { diff --git a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Mixed.res b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Mixed.res index 05465728..7bbe2065 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Mixed.res +++ b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Mixed.res @@ -302,10 +302,9 @@ module T = Dist({ } let klDivergence = (prediction: t, answer: t) => { - Error(Operation.NotYetImplemented) - // combinePointwise(PointSetDist_Scoring.KLDivergence.integrand, prediction, answer) |> E.R.fmap( - // integralEndY, - // ) + let klDiscretePart = Discrete.T.klDivergence(prediction.discrete, answer.discrete) + let klContinuousPart = Continuous.T.klDivergence(prediction.continuous, answer.continuous) + E.R.merge(klDiscretePart, klContinuousPart)->E.R2.fmap(t => fst(t) +. snd(t)) } }) diff --git a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetDist.res b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetDist.res index 1879ebdd..db47d1e1 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetDist.res +++ b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetDist.res @@ -200,6 +200,7 @@ module T = Dist({ switch (t1, t2) { | (Continuous(t1), Continuous(t2)) => Continuous.T.klDivergence(t1, t2) | (Discrete(t1), Discrete(t2)) => Discrete.T.klDivergence(t1, t2) + | (Mixed(t1), Mixed(t2)) => Mixed.T.klDivergence(t1, t2) | _ => Error(NotYetImplemented) } }) From a2b5dbf6c3cf1a962fae3efd995d76af7d20b3ca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 11 May 2022 14:42:12 +0000 Subject: [PATCH 02/23] :arrow_up: Bump @types/node from 17.0.31 to 17.0.32 Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 17.0.31 to 17.0.32. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- packages/components/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/components/package.json b/packages/components/package.json index b5f1ab7c..b269735f 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -31,7 +31,7 @@ "@testing-library/user-event": "^14.1.1", "@types/jest": "^27.5.0", "@types/lodash": "^4.14.182", - "@types/node": "^17.0.31", + "@types/node": "^17.0.32", "@types/react": "^18.0.9", "@types/react-dom": "^18.0.2", "@types/styled-components": "^5.1.24", diff --git a/yarn.lock b/yarn.lock index 351a1e62..99712b31 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4038,10 +4038,10 @@ "@types/node" "*" form-data "^3.0.0" -"@types/node@*", "@types/node@^17.0.31", "@types/node@^17.0.5": - version "17.0.31" - resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.31.tgz#a5bb84ecfa27eec5e1c802c6bbf8139bdb163a5d" - integrity sha512-AR0x5HbXGqkEx9CadRH3EBYx/VkiUgZIhP4wvPn/+5KIsgpNoyFaRlVe0Zlx9gRtg8fA06a9tskE2MSN7TcG4Q== +"@types/node@*", "@types/node@^17.0.32", "@types/node@^17.0.5": + version "17.0.32" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.32.tgz#51d59d7a90ef2d0ae961791e0900cad2393a0149" + integrity sha512-eAIcfAvhf/BkHcf4pkLJ7ECpBAhh9kcxRBpip9cTiO+hf+aJrsxYxBeS6OXvOd9WqNAJmavXVpZvY1rBjNsXmw== "@types/node@^14.0.10": version "14.18.16" From 4df4597ed333b5ccaa0dcd89d974253cd6630e6b Mon Sep 17 00:00:00 2001 From: NunoSempere Date: Wed, 11 May 2022 12:23:36 -0400 Subject: [PATCH 03/23] fix: "Enrich" (add more x points) when integrating in order to get more numerical precision. Note: not complete yet. Value: [1e-3 to 3e-1] --- .../Distributions/KlDivergence_test.res | 2 +- .../Distributions/PointSetDist/Continuous.res | 3 +- .../src/rescript/MagicNumbers.res | 1 + .../src/rescript/Utility/XYShape.res | 38 +++++++++++++++++++ 4 files changed, 42 insertions(+), 2 deletions(-) diff --git a/packages/squiggle-lang/__tests__/Distributions/KlDivergence_test.res b/packages/squiggle-lang/__tests__/Distributions/KlDivergence_test.res index 31f03ae2..e3efe446 100644 --- a/packages/squiggle-lang/__tests__/Distributions/KlDivergence_test.res +++ b/packages/squiggle-lang/__tests__/Distributions/KlDivergence_test.res @@ -69,7 +69,7 @@ describe("klDivergence: continuous -> continuous -> float", () => { 2.0 ** 2.0 *. (10.0 ** 2.0 -. (10.0 +. 9.0) *. 10.0 +. (9.0 ** 2.0 +. 10.0 *. 9.0 +. 10.0 ** 2.0) /. 3.0) switch kl { - | Ok(kl') => kl'->expect->toBeSoCloseTo(analyticalKl, ~digits=1) + | Ok(kl') => kl'->expect->toBeSoCloseTo(analyticalKl, ~digits=3) | Error(err) => { Js.Console.log(DistributionTypes.Error.toString(err)) raise(KlFailed) diff --git a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Continuous.res b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Continuous.res index 3aca0c66..c6bac10b 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Continuous.res +++ b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Continuous.res @@ -272,10 +272,11 @@ module T = Dist({ XYShape.Analysis.getVarianceDangerously(t, mean, Analysis.getMeanOfSquares) let klDivergence = (prediction: t, answer: t) => { + let enrichedAnswer = XYShape.PointwiseCombination.enrichXyShape(answer.xyShape) // answer.xyShape // let newShape = XYShape.PointwiseCombination.combineAlongSupportOfSecondArgument( PointSetDist_Scoring.KLDivergence.integrand, prediction.xyShape, - answer.xyShape, + enrichedAnswer, ) let xyShapeToContinuous: XYShape.xyShape => t = xyShape => { xyShape: xyShape, diff --git a/packages/squiggle-lang/src/rescript/MagicNumbers.res b/packages/squiggle-lang/src/rescript/MagicNumbers.res index 13beafe4..cd8cfd2f 100644 --- a/packages/squiggle-lang/src/rescript/MagicNumbers.res +++ b/packages/squiggle-lang/src/rescript/MagicNumbers.res @@ -12,6 +12,7 @@ module Epsilon = { module Environment = { let defaultXYPointLength = 1000 let defaultSampleCount = 10000 + let enrichmentFactor = 10 } module OpCost = { diff --git a/packages/squiggle-lang/src/rescript/Utility/XYShape.res b/packages/squiggle-lang/src/rescript/Utility/XYShape.res index 60d0bbde..16ad64ab 100644 --- a/packages/squiggle-lang/src/rescript/Utility/XYShape.res +++ b/packages/squiggle-lang/src/rescript/Utility/XYShape.res @@ -453,6 +453,44 @@ module PointwiseCombination = { T.filterOkYs(newXs, newYs)->Ok } + let enrichXyShape = (t: T.t): T.t => { + let length = E.A.length(t.xs) + Js.Console.log(length) + let points = switch length < MagicNumbers.Environment.defaultXYPointLength { + | true => + Belt.Int.fromFloat( + Belt.Float.fromInt( + MagicNumbers.Environment.enrichmentFactor * MagicNumbers.Environment.defaultXYPointLength, + ) /. + Belt.Float.fromInt(length), + ) + | false => MagicNumbers.Environment.enrichmentFactor + } + + let getInBetween = (x1: float, x2: float): array => { + let newPointsArray = Belt.Array.makeBy(points - 1, i => i) + // don't repeat the x2 point, it will be gotten in the next iteration. + let result = Js.Array.mapi((pos, i) => + switch i { + | 0 => x1 + | _ => + x1 *. + (Belt.Float.fromInt(points) -. Belt.Float.fromInt(pos)) /. + Belt.Float.fromInt(points) +. x2 *. Belt.Float.fromInt(pos) /. Belt.Float.fromInt(points) + } + , newPointsArray) + result + } + let newXsUnflattened = Js.Array.mapi((x, i) => + switch i < length - 1 { + | true => getInBetween(x, t.xs[i + 1]) + | false => [x] + } + , t.xs) + let newXs = Belt.Array.concatMany(newXsUnflattened) + let newYs = E.A.fmap(x => XtoY.linear(x, t), newXs) //XtoY.linear(newXs) + {xs: newXs, ys: newYs} + } // This function is used for klDivergence let combineAlongSupportOfSecondArgument: ( (float, float) => result, From 599c14b32c1b515a28cc8bc4ab391c70d11fd7d2 Mon Sep 17 00:00:00 2001 From: NunoSempere Date: Wed, 11 May 2022 12:39:03 -0400 Subject: [PATCH 04/23] tweak: Pass off to Quinn --- .../src/rescript/Distributions/PointSetDist/Continuous.res | 5 ++++- packages/squiggle-lang/src/rescript/Utility/XYShape.res | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Continuous.res b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Continuous.res index c6bac10b..e8049688 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Continuous.res +++ b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Continuous.res @@ -272,7 +272,10 @@ module T = Dist({ XYShape.Analysis.getVarianceDangerously(t, mean, Analysis.getMeanOfSquares) let klDivergence = (prediction: t, answer: t) => { - let enrichedAnswer = XYShape.PointwiseCombination.enrichXyShape(answer.xyShape) // answer.xyShape // + let enrich = true + let enrichedAnswer = enrich + ? XYShape.PointwiseCombination.enrichXyShape(answer.xyShape) + : answer.xyShape // let newShape = XYShape.PointwiseCombination.combineAlongSupportOfSecondArgument( PointSetDist_Scoring.KLDivergence.integrand, prediction.xyShape, diff --git a/packages/squiggle-lang/src/rescript/Utility/XYShape.res b/packages/squiggle-lang/src/rescript/Utility/XYShape.res index 16ad64ab..3daa6c6d 100644 --- a/packages/squiggle-lang/src/rescript/Utility/XYShape.res +++ b/packages/squiggle-lang/src/rescript/Utility/XYShape.res @@ -482,13 +482,13 @@ module PointwiseCombination = { result } let newXsUnflattened = Js.Array.mapi((x, i) => - switch i < length - 1 { + switch i < length - 2 { | true => getInBetween(x, t.xs[i + 1]) | false => [x] } , t.xs) let newXs = Belt.Array.concatMany(newXsUnflattened) - let newYs = E.A.fmap(x => XtoY.linear(x, t), newXs) //XtoY.linear(newXs) + let newYs = E.A.fmap(x => XtoY.linear(x, t), newXs) {xs: newXs, ys: newYs} } // This function is used for klDivergence From 5c9f57a24a66196cace59b5856c981608cf84712 Mon Sep 17 00:00:00 2001 From: NunoSempere Date: Wed, 11 May 2022 12:56:56 -0400 Subject: [PATCH 05/23] fix: Attain parity with previous implementation This is achieved by not adding more points in between i the points are already pretty close --- .../src/rescript/MagicNumbers.res | 2 +- .../src/rescript/Utility/XYShape.res | 28 +++++++++++-------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/packages/squiggle-lang/src/rescript/MagicNumbers.res b/packages/squiggle-lang/src/rescript/MagicNumbers.res index cd8cfd2f..57e33e3b 100644 --- a/packages/squiggle-lang/src/rescript/MagicNumbers.res +++ b/packages/squiggle-lang/src/rescript/MagicNumbers.res @@ -11,7 +11,7 @@ module Epsilon = { module Environment = { let defaultXYPointLength = 1000 - let defaultSampleCount = 10000 + let defaultSampleCount = 1000 let enrichmentFactor = 10 } diff --git a/packages/squiggle-lang/src/rescript/Utility/XYShape.res b/packages/squiggle-lang/src/rescript/Utility/XYShape.res index 3daa6c6d..01b59670 100644 --- a/packages/squiggle-lang/src/rescript/Utility/XYShape.res +++ b/packages/squiggle-lang/src/rescript/Utility/XYShape.res @@ -468,18 +468,24 @@ module PointwiseCombination = { } let getInBetween = (x1: float, x2: float): array => { - let newPointsArray = Belt.Array.makeBy(points - 1, i => i) - // don't repeat the x2 point, it will be gotten in the next iteration. - let result = Js.Array.mapi((pos, i) => - switch i { - | 0 => x1 - | _ => - x1 *. - (Belt.Float.fromInt(points) -. Belt.Float.fromInt(pos)) /. - Belt.Float.fromInt(points) +. x2 *. Belt.Float.fromInt(pos) /. Belt.Float.fromInt(points) + switch x1 -. x2 > 2.0 *. MagicNumbers.Epsilon.seven { + | false => [x1] + | true => { + let newPointsArray = Belt.Array.makeBy(points - 1, i => i) + // don't repeat the x2 point, it will be gotten in the next iteration. + let result = Js.Array.mapi((pos, i) => + switch i { + | 0 => x1 + | _ => + x1 *. + (Belt.Float.fromInt(points) -. Belt.Float.fromInt(pos)) /. + Belt.Float.fromInt(points) +. + x2 *. Belt.Float.fromInt(pos) /. Belt.Float.fromInt(points) + } + , newPointsArray) + result } - , newPointsArray) - result + } } let newXsUnflattened = Js.Array.mapi((x, i) => switch i < length - 2 { From b2a972605dc8eb48b4f509ab232dd88b728931f8 Mon Sep 17 00:00:00 2001 From: Sam Nolan Date: Tue, 10 May 2022 15:52:13 +0000 Subject: [PATCH 06/23] (rebase): Function charting --- .../src/components/FunctionChart.tsx | 121 ++++++++++++------ .../src/components/SquiggleChart.tsx | 65 +++++----- .../src/components/SquiggleEditor.tsx | 35 ++--- .../src/components/SquigglePlayground.tsx | 24 +++- packages/squiggle-lang/src/js/index.ts | 37 ++++-- .../squiggle-lang/src/js/rescript_interop.ts | 21 +++ .../src/rescript/TypescriptInterface.res | 3 + 7 files changed, 202 insertions(+), 104 deletions(-) diff --git a/packages/components/src/components/FunctionChart.tsx b/packages/components/src/components/FunctionChart.tsx index 86678b27..2ee71721 100644 --- a/packages/components/src/components/FunctionChart.tsx +++ b/packages/components/src/components/FunctionChart.tsx @@ -1,18 +1,24 @@ import * as React from "react"; import _ from "lodash"; import type { Spec } from "vega"; -import type { Distribution, errorValue, result } from "@quri/squiggle-lang"; +import { + Distribution, + result, + lambdaValue, + environment, + runForeign, + errorValueToString, +} from "@quri/squiggle-lang"; import { createClassFromSpec } from "react-vega"; import * as percentilesSpec from "../vega-specs/spec-percentiles.json"; import { DistributionChart } from "./DistributionChart"; +import { NumberShower } from "./NumberShower"; import { ErrorBox } from "./ErrorBox"; let SquigglePercentilesChart = createClassFromSpec({ spec: percentilesSpec as Spec, }); -type distPlusFn = (a: number) => result; - const _rangeByCount = (start: number, stop: number, count: number) => { const step = (stop - start) / (count - 1); const items = _.range(start, stop, step); @@ -27,38 +33,36 @@ function unwrap(x: result): a { throw Error("FAILURE TO UNWRAP"); } } +export type FunctionChartSettings = { + start: number; + stop: number; + count: number; +}; -function mapFilter(xs: a[], f: (x: a) => b | undefined): b[] { - let initial: b[] = []; - return xs.reduce((previous, current) => { - let value: b | undefined = f(current); - if (value !== undefined) { - return previous.concat([value]); - } else { - return previous; - } - }, initial); +interface FunctionChartProps { + fn: lambdaValue; + chartSettings: FunctionChartSettings; + environment: environment; } -export const FunctionChart: React.FC<{ - distPlusFn: distPlusFn; - diagramStart: number; - diagramStop: number; - diagramCount: number; -}> = ({ distPlusFn, diagramStart, diagramStop, diagramCount }) => { +export const FunctionChart: React.FC = ({ + fn, + chartSettings, + environment, +}: FunctionChartProps) => { let [mouseOverlay, setMouseOverlay] = React.useState(0); - function handleHover(...args) { - setMouseOverlay(args[1]); + function handleHover(_name: string, value: unknown) { + setMouseOverlay(value as number); } function handleOut() { setMouseOverlay(NaN); } const signalListeners = { mousemove: handleHover, mouseout: handleOut }; - let mouseItem = distPlusFn(mouseOverlay); + let mouseItem = runForeign(fn, [mouseOverlay], environment); let showChart = - mouseItem.tag === "Ok" ? ( + mouseItem.tag === "Ok" && mouseItem.value.tag == "distribution" ? ( ); - let data1 = _rangeByCount(diagramStart, diagramStop, diagramCount); - let valueData = mapFilter(data1, (x) => { - let result = distPlusFn(x); + let data1 = _rangeByCount( + chartSettings.start, + chartSettings.stop, + chartSettings.count + ); + type point = { x: number; value: result }; + let valueData: point[] = data1.map((x) => { + let result = runForeign(fn, [x], environment); if (result.tag === "Ok") { - return { x: x, value: result.value }; + if (result.value.tag == "distribution") { + return { x, value: { tag: "Ok", value: result.value.value } }; + } else { + return { + x, + value: { + tag: "Error", + value: + "Cannot currently render functions that don't return distributions", + }, + }; + } + } else { + return { + x, + value: { tag: "Error", value: errorValueToString(result.value) }, + }; } - }).map(({ x, value }) => { + }); + + let initialPartition: [ + { x: number; value: Distribution }[], + { x: number; value: string }[] + ] = [[], []]; + let [functionImage, errors] = valueData.reduce((acc, current) => { + if (current.value.tag === "Ok") { + acc[0].push({ x: current.x, value: current.value.value }); + } else { + acc[1].push({ x: current.x, value: current.value.value }); + } + return acc; + }, initialPartition); + + let percentiles = functionImage.map(({ x, value }) => { return { x: x, p1: unwrap(value.inv(0.01)), @@ -91,24 +131,25 @@ export const FunctionChart: React.FC<{ }; }); - let errorData = mapFilter(data1, (x) => { - let result = distPlusFn(x); - if (result.tag === "Error") { - return { x: x, error: result.value }; - } - }); - let error2 = _.groupBy(errorData, (x) => x.error); + let groupedErrors = _.groupBy(errors, (x) => x.value); return ( <> {showChart} - {_.keysIn(error2).map((k) => ( - - {`Values: [${error2[k].map((r) => r.x.toFixed(2)).join(",")}]`} + {_.entries(groupedErrors).map(([errorName, errorPoints]) => ( + + Values:{" "} + {errorPoints + .map((r) => ) + .reduce((a, b) => ( + <> + {a}, {b} + + ))} ))} diff --git a/packages/components/src/components/SquiggleChart.tsx b/packages/components/src/components/SquiggleChart.tsx index 21fa8e14..f6799de4 100644 --- a/packages/components/src/components/SquiggleChart.tsx +++ b/packages/components/src/components/SquiggleChart.tsx @@ -6,7 +6,7 @@ import { errorValueToString, squiggleExpression, bindings, - samplingParams, + environment, jsImports, defaultImports, defaultBindings, @@ -14,6 +14,7 @@ import { import { NumberShower } from "./NumberShower"; import { DistributionChart } from "./DistributionChart"; import { ErrorBox } from "./ErrorBox"; +import { FunctionChart, FunctionChartSettings } from "./FunctionChart"; const variableBox = { Component: styled.div` @@ -36,7 +37,7 @@ const variableBox = { interface VariableBoxProps { heading: string; children: React.ReactNode; - showTypes?: boolean; + showTypes: boolean; } export const VariableBox: React.FC = ({ @@ -68,9 +69,13 @@ export interface SquiggleItemProps { /** Whether to show a summary of statistics for distributions */ showSummary: boolean; /** Whether to show type information */ - showTypes?: boolean; + showTypes: boolean; /** Whether to show users graph controls (scale etc) */ - showControls?: boolean; + showControls: boolean; + /** Settings for displaying functions */ + chartSettings: FunctionChartSettings; + /** Environment for further function executions */ + environment: environment; } const SquiggleItem: React.FC = ({ @@ -80,6 +85,8 @@ const SquiggleItem: React.FC = ({ showSummary, showTypes = false, showControls = false, + chartSettings, + environment, }: SquiggleItemProps) => { switch (expression.tag) { case "number": @@ -147,6 +154,8 @@ const SquiggleItem: React.FC = ({ height={50} showTypes={showTypes} showControls={showControls} + chartSettings={chartSettings} + environment={environment} showSummary={showSummary} /> ))} @@ -165,6 +174,8 @@ const SquiggleItem: React.FC = ({ showTypes={showTypes} showSummary={showSummary} showControls={showControls} + chartSettings={chartSettings} + environment={environment} /> ))} @@ -178,9 +189,11 @@ const SquiggleItem: React.FC = ({ ); case "lambda": return ( - - There is no viewer currently available for function types. - + ); } }; @@ -191,30 +204,24 @@ export interface SquiggleChartProps { /** If the output requires monte carlo sampling, the amount of samples */ sampleCount?: number; /** The amount of points returned to draw the distribution */ - outputXYPoints?: number; - kernelWidth?: number; - pointDistLength?: number; - /** If the result is a function, where the function starts */ - diagramStart?: number; - /** If the result is a function, where the function ends */ - diagramStop?: number; - /** If the result is a function, how many points along the function it samples */ - diagramCount?: number; + environment: environment; + /** If the result is a function, where the function starts, ends and the amount of stops */ + chartSettings?: FunctionChartSettings; /** When the environment changes */ onChange?(expr: squiggleExpression): void; /** CSS width of the element */ width?: number; height?: number; /** Bindings of previous variables declared */ - bindings?: bindings; + bindings: bindings; /** JS imported parameters */ - jsImports?: jsImports; + jsImports: jsImports; /** Whether to show a summary of the distirbution */ showSummary?: boolean; /** Whether to show type information about returns, default false */ - showTypes?: boolean; + showTypes: boolean; /** Whether to show graph controls (scale etc)*/ - showControls?: boolean; + showControls: boolean; } const ChartWrapper = styled.div` @@ -223,10 +230,10 @@ const ChartWrapper = styled.div` "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; `; +let defaultChartSettings = { start: 0, stop: 10, count: 100 }; export const SquiggleChart: React.FC = ({ squiggleString = "", - sampleCount = 1000, - outputXYPoints = 1000, + environment, onChange = () => {}, height = 60, bindings = defaultBindings, @@ -235,17 +242,9 @@ export const SquiggleChart: React.FC = ({ width, showTypes = false, showControls = false, + chartSettings = defaultChartSettings, }: SquiggleChartProps) => { - let samplingInputs: samplingParams = { - sampleCount: sampleCount, - xyPointLength: outputXYPoints, - }; - let expressionResult = run( - squiggleString, - bindings, - samplingInputs, - jsImports - ); + let expressionResult = run(squiggleString, bindings, environment, jsImports); let internal: JSX.Element; if (expressionResult.tag === "Ok") { let expression = expressionResult.value; @@ -258,6 +257,8 @@ export const SquiggleChart: React.FC = ({ showSummary={showSummary} showTypes={showTypes} showControls={showControls} + chartSettings={chartSettings} + environment={environment} /> ); } else { diff --git a/packages/components/src/components/SquiggleEditor.tsx b/packages/components/src/components/SquiggleEditor.tsx index 2601bc21..4b17b585 100644 --- a/packages/components/src/components/SquiggleEditor.tsx +++ b/packages/components/src/components/SquiggleEditor.tsx @@ -5,7 +5,7 @@ import { CodeEditor } from "./CodeEditor"; import styled from "styled-components"; import type { squiggleExpression, - samplingParams, + environment, bindings, jsImports, } from "@quri/squiggle-lang"; @@ -24,8 +24,6 @@ export interface SquiggleEditorProps { sampleCount?: number; /** The amount of points returned to draw the distribution */ outputXYPoints?: number; - kernelWidth?: number; - pointDistLength?: number; /** If the result is a function, where the function starts */ diagramStart?: number; /** If the result is a function, where the function ends */ @@ -57,13 +55,11 @@ const Input = styled.div` export let SquiggleEditor: React.FC = ({ initialSquiggleString = "", width, - sampleCount, - outputXYPoints, - kernelWidth, - pointDistLength, - diagramStart, - diagramStop, - diagramCount, + sampleCount = 1000, + outputXYPoints = 1000, + diagramStart = 0, + diagramStop = 10, + diagramCount = 100, onChange, bindings = defaultBindings, jsImports = defaultImports, @@ -72,6 +68,15 @@ export let SquiggleEditor: React.FC = ({ showSummary = false, }: SquiggleEditorProps) => { let [expression, setExpression] = React.useState(initialSquiggleString); + let chartSettings = { + start: diagramStart, + stop: diagramStop, + count: diagramCount, + }; + let env: environment = { + sampleCount: sampleCount, + xyPointLength: outputXYPoints, + }; return (
@@ -85,14 +90,10 @@ export let SquiggleEditor: React.FC = ({ = ({ outputXYPoints = 1000, jsImports = defaultImports, }: SquigglePartialProps) => { - let samplingInputs: samplingParams = { + let samplingInputs: environment = { sampleCount: sampleCount, xyPointLength: outputXYPoints, }; diff --git a/packages/components/src/components/SquigglePlayground.tsx b/packages/components/src/components/SquigglePlayground.tsx index dd2b8ecf..9f4c9153 100644 --- a/packages/components/src/components/SquigglePlayground.tsx +++ b/packages/components/src/components/SquigglePlayground.tsx @@ -4,6 +4,11 @@ import ReactDOM from "react-dom"; import { SquiggleChart } from "./SquiggleChart"; import CodeEditor from "./CodeEditor"; import styled from "styled-components"; +import { + defaultBindings, + environment, + defaultImports, +} from "@quri/squiggle-lang"; interface FieldFloatProps { label: string; @@ -96,6 +101,15 @@ let SquigglePlayground: FC = ({ let [diagramStart, setDiagramStart] = useState(0); let [diagramStop, setDiagramStop] = useState(10); let [diagramCount, setDiagramCount] = useState(20); + let chartSettings = { + start: diagramStart, + stop: diagramStop, + count: diagramCount, + }; + let env: environment = { + sampleCount: sampleCount, + xyPointLength: outputXYPoints, + }; return ( @@ -112,16 +126,14 @@ let SquigglePlayground: FC = ({ diff --git a/packages/squiggle-lang/src/js/index.ts b/packages/squiggle-lang/src/js/index.ts index 961d4935..306e5a07 100644 --- a/packages/squiggle-lang/src/js/index.ts +++ b/packages/squiggle-lang/src/js/index.ts @@ -1,6 +1,5 @@ import * as _ from "lodash"; import { - samplingParams, environment, defaultEnvironment, evaluatePartialUsingExternalBindings, @@ -8,6 +7,7 @@ import { externalBindings, expressionValue, errorValue, + foreignFunctionInterface, } from "../rescript/TypescriptInterface.gen"; export { makeSampleSetDist, @@ -15,25 +15,30 @@ export { distributionErrorToString, distributionError, } from "../rescript/TypescriptInterface.gen"; -export type { - samplingParams, - errorValue, - externalBindings as bindings, - jsImports, -}; +export type { errorValue, externalBindings as bindings, jsImports }; import { jsValueToBinding, + jsValueToExpressionValue, jsValue, rescriptExport, squiggleExpression, convertRawToTypescript, + lambdaValue, } from "./rescript_interop"; import { result, resultMap, tag, tagged } from "./types"; import { Distribution, shape } from "./distribution"; -export { Distribution, squiggleExpression, result, resultMap, shape }; +export { + Distribution, + squiggleExpression, + result, + resultMap, + shape, + lambdaValue, + environment, +}; -export let defaultSamplingInputs: samplingParams = { +export let defaultSamplingInputs: environment = { sampleCount: 10000, xyPointLength: 10000, }; @@ -72,6 +77,20 @@ export function runPartial( ); } +export function runForeign( + fn: lambdaValue, + args: jsValue[], + environment?: environment +): result { + let e = environment ? environment : defaultEnvironment; + let res: result = foreignFunctionInterface( + fn, + args.map(jsValueToExpressionValue), + e + ); + return resultMap(res, (x) => createTsExport(x, e)); +} + function mergeImportsWithBindings( bindings: externalBindings, imports: jsImports diff --git a/packages/squiggle-lang/src/js/rescript_interop.ts b/packages/squiggle-lang/src/js/rescript_interop.ts index 45f4124b..0781f081 100644 --- a/packages/squiggle-lang/src/js/rescript_interop.ts +++ b/packages/squiggle-lang/src/js/rescript_interop.ts @@ -1,5 +1,6 @@ import * as _ from "lodash"; import { + expressionValue, mixedShape, sampleSetDist, genericDist, @@ -87,6 +88,8 @@ export type squiggleExpression = | tagged<"number", number> | tagged<"record", { [key: string]: squiggleExpression }>; +export { lambdaValue }; + export function convertRawToTypescript( result: rescriptExport, environment: environment @@ -168,3 +171,21 @@ export function jsValueToBinding(value: jsValue): rescriptExport { return { TAG: 7, _0: _.mapValues(value, jsValueToBinding) }; } } + +export function jsValueToExpressionValue(value: jsValue): expressionValue { + if (typeof value === "boolean") { + return { tag: "EvBool", value: value as boolean }; + } else if (typeof value === "string") { + return { tag: "EvString", value: value as string }; + } else if (typeof value === "number") { + return { tag: "EvNumber", value: value as number }; + } else if (Array.isArray(value)) { + return { tag: "EvArray", value: value.map(jsValueToExpressionValue) }; + } else { + // Record + return { + tag: "EvRecord", + value: _.mapValues(value, jsValueToExpressionValue), + }; + } +} diff --git a/packages/squiggle-lang/src/rescript/TypescriptInterface.res b/packages/squiggle-lang/src/rescript/TypescriptInterface.res index 6ebb8377..13763e72 100644 --- a/packages/squiggle-lang/src/rescript/TypescriptInterface.res +++ b/packages/squiggle-lang/src/rescript/TypescriptInterface.res @@ -84,3 +84,6 @@ type environment = ReducerInterface_ExpressionValue.environment @genType let defaultEnvironment = ReducerInterface_ExpressionValue.defaultEnvironment + +@genType +let foreignFunctionInterface = Reducer.foreignFunctionInterface From b0c84450ffb84c31ae5faad4b12f2bed9eb834e5 Mon Sep 17 00:00:00 2001 From: Sam Nolan Date: Tue, 10 May 2022 16:08:12 +0000 Subject: [PATCH 07/23] Make optional arguments actually optional --- packages/components/src/components/SquiggleChart.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/components/src/components/SquiggleChart.tsx b/packages/components/src/components/SquiggleChart.tsx index f6799de4..ab6fbb9c 100644 --- a/packages/components/src/components/SquiggleChart.tsx +++ b/packages/components/src/components/SquiggleChart.tsx @@ -213,15 +213,15 @@ export interface SquiggleChartProps { width?: number; height?: number; /** Bindings of previous variables declared */ - bindings: bindings; + bindings?: bindings; /** JS imported parameters */ jsImports: jsImports; /** Whether to show a summary of the distirbution */ showSummary?: boolean; /** Whether to show type information about returns, default false */ - showTypes: boolean; + showTypes?: boolean; /** Whether to show graph controls (scale etc)*/ - showControls: boolean; + showControls?: boolean; } const ChartWrapper = styled.div` From 4758fca19b9223319bd91a1c94c04e1386446739 Mon Sep 17 00:00:00 2001 From: Sam Nolan Date: Tue, 10 May 2022 16:16:36 +0000 Subject: [PATCH 08/23] Keep props consistent --- .../src/components/SquiggleChart.tsx | 6 ++-- .../src/components/SquiggleEditor.tsx | 29 ++++--------------- 2 files changed, 10 insertions(+), 25 deletions(-) diff --git a/packages/components/src/components/SquiggleChart.tsx b/packages/components/src/components/SquiggleChart.tsx index ab6fbb9c..15400d3f 100644 --- a/packages/components/src/components/SquiggleChart.tsx +++ b/packages/components/src/components/SquiggleChart.tsx @@ -10,6 +10,7 @@ import { jsImports, defaultImports, defaultBindings, + defaultEnvironment, } from "@quri/squiggle-lang"; import { NumberShower } from "./NumberShower"; import { DistributionChart } from "./DistributionChart"; @@ -204,7 +205,7 @@ export interface SquiggleChartProps { /** If the output requires monte carlo sampling, the amount of samples */ sampleCount?: number; /** The amount of points returned to draw the distribution */ - environment: environment; + environment?: environment; /** If the result is a function, where the function starts, ends and the amount of stops */ chartSettings?: FunctionChartSettings; /** When the environment changes */ @@ -245,6 +246,7 @@ export const SquiggleChart: React.FC = ({ chartSettings = defaultChartSettings, }: SquiggleChartProps) => { let expressionResult = run(squiggleString, bindings, environment, jsImports); + let e = environment ? environment : defaultEnvironment; let internal: JSX.Element; if (expressionResult.tag === "Ok") { let expression = expressionResult.value; @@ -258,7 +260,7 @@ export const SquiggleChart: React.FC = ({ showTypes={showTypes} showControls={showControls} chartSettings={chartSettings} - environment={environment} + environment={e} /> ); } else { diff --git a/packages/components/src/components/SquiggleEditor.tsx b/packages/components/src/components/SquiggleEditor.tsx index 4b17b585..c4ac1876 100644 --- a/packages/components/src/components/SquiggleEditor.tsx +++ b/packages/components/src/components/SquiggleEditor.tsx @@ -21,9 +21,7 @@ export interface SquiggleEditorProps { /** The input string for squiggle */ initialSquiggleString?: string; /** If the output requires monte carlo sampling, the amount of samples */ - sampleCount?: number; - /** The amount of points returned to draw the distribution */ - outputXYPoints?: number; + environment?: environment; /** If the result is a function, where the function starts */ diagramStart?: number; /** If the result is a function, where the function ends */ @@ -55,8 +53,7 @@ const Input = styled.div` export let SquiggleEditor: React.FC = ({ initialSquiggleString = "", width, - sampleCount = 1000, - outputXYPoints = 1000, + environment, diagramStart = 0, diagramStop = 10, diagramCount = 100, @@ -73,10 +70,6 @@ export let SquiggleEditor: React.FC = ({ stop: diagramStop, count: diagramCount, }; - let env: environment = { - sampleCount: sampleCount, - xyPointLength: outputXYPoints, - }; return (
@@ -90,9 +83,8 @@ export let SquiggleEditor: React.FC = ({ = ({ initialSquiggleString = "", onChange, bindings = defaultBindings, - sampleCount = 1000, - outputXYPoints = 1000, + environment, jsImports = defaultImports, }: SquigglePartialProps) => { - let samplingInputs: environment = { - sampleCount: sampleCount, - xyPointLength: outputXYPoints, - }; let [expression, setExpression] = React.useState(initialSquiggleString); let [error, setError] = React.useState(null); @@ -181,7 +164,7 @@ export let SquigglePartial: React.FC = ({ let squiggleResult = runPartial( expression, bindings, - samplingInputs, + environment, jsImports ); if (squiggleResult.tag == "Ok") { From a3402a42a440522861f1172f063ef2a9d883385d Mon Sep 17 00:00:00 2001 From: Sam Nolan Date: Tue, 10 May 2022 16:20:31 +0000 Subject: [PATCH 09/23] Add default environment as export --- packages/squiggle-lang/src/js/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/squiggle-lang/src/js/index.ts b/packages/squiggle-lang/src/js/index.ts index 306e5a07..5549223b 100644 --- a/packages/squiggle-lang/src/js/index.ts +++ b/packages/squiggle-lang/src/js/index.ts @@ -36,6 +36,7 @@ export { shape, lambdaValue, environment, + defaultEnvironment }; export let defaultSamplingInputs: environment = { From c13f49a7bca8b9a8a9edd37e7203285e52b7def1 Mon Sep 17 00:00:00 2001 From: Sam Nolan Date: Tue, 10 May 2022 16:24:08 +0000 Subject: [PATCH 10/23] Lint fix --- packages/squiggle-lang/src/js/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/squiggle-lang/src/js/index.ts b/packages/squiggle-lang/src/js/index.ts index 5549223b..d3d074fa 100644 --- a/packages/squiggle-lang/src/js/index.ts +++ b/packages/squiggle-lang/src/js/index.ts @@ -36,7 +36,7 @@ export { shape, lambdaValue, environment, - defaultEnvironment + defaultEnvironment, }; export let defaultSamplingInputs: environment = { From 95fe117ef00d5a1fdbe413649ee55d21a0b14e7c Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Wed, 11 May 2022 14:14:10 -0400 Subject: [PATCH 11/23] Factored continuous part of normal and uniform kldivergence into it's own function Value: [1e-4 to 1e-3] --- .../Distributions/KlDivergence_test.res | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/packages/squiggle-lang/__tests__/Distributions/KlDivergence_test.res b/packages/squiggle-lang/__tests__/Distributions/KlDivergence_test.res index e3efe446..03652738 100644 --- a/packages/squiggle-lang/__tests__/Distributions/KlDivergence_test.res +++ b/packages/squiggle-lang/__tests__/Distributions/KlDivergence_test.res @@ -3,6 +3,12 @@ open Expect open TestHelpers open GenericDist_Fixtures +let klNormalUniform = (mean, stdev, low, high): float => + -.Js.Math.log((high -. low) /. Js.Math.sqrt(2.0 *. MagicNumbers.Math.pi *. stdev ** 2.0)) +. + 1.0 /. + stdev ** 2.0 *. + (mean ** 2.0 -. (high +. low) *. mean +. (low ** 2.0 +. high *. low +. high ** 2.0) /. 3.0) + describe("klDivergence: continuous -> continuous -> float", () => { let klDivergence = DistributionOperation.Constructors.klDivergence(~env) @@ -63,11 +69,7 @@ describe("klDivergence: continuous -> continuous -> float", () => { let prediction = normalDist10 let answer = uniformDist let kl = klDivergence(prediction, answer) - let analyticalKl = - -.Js.Math.log((10.0 -. 9.0) /. Js.Math.sqrt(2.0 *. MagicNumbers.Math.pi *. 2.0 ** 2.0)) +. - 1.0 /. - 2.0 ** 2.0 *. - (10.0 ** 2.0 -. (10.0 +. 9.0) *. 10.0 +. (9.0 ** 2.0 +. 10.0 *. 9.0 +. 10.0 ** 2.0) /. 3.0) + let analyticalKl = klNormalUniform(10.0, 2.0, 9.0, 10.0) switch kl { | Ok(kl') => kl'->expect->toBeSoCloseTo(analyticalKl, ~digits=3) | Error(err) => { @@ -122,17 +124,13 @@ describe("klDivergence: mixed -> mixed -> float", () => { | (Dist(a''), Dist(b'')) => (a'', b'') | _ => raise(MixtureFailed) } - test("finite klDivergence returns is correct", () => { + test("finite klDivergence return is correct", () => { let prediction = b let answer = a let kl = klDivergence(prediction, answer) // high = 10; low = 9; mean = 10; stdev = 2 - let analyticalKlContinuousPart = - Js.Math.log(Js.Math.sqrt(2.0 *. MagicNumbers.Math.pi *. 2.0 ** 2.0) /. (10.0 -. 9.0)) +. - 1.0 /. - 2.0 ** 2.0 *. - (10.0 ** 2.0 -. (10.0 +. 9.0) *. 10.0 +. (9.0 ** 2.0 +. 10.0 *. 9.0 +. 10.0 ** 2.0) /. 3.0) - let analyticalKlDiscretePart = -2.0 /. 3.0 *. Js.Math.log(3.0 /. 2.0) + let analyticalKlContinuousPart = klNormalUniform(10.0, 2.0, 9.0, 10.0) + let analyticalKlDiscretePart = 2.0 /. 3.0 *. Js.Math.log(2.0 /. 3.0) switch kl { | Ok(kl') => kl'->expect->toBeSoCloseTo(analyticalKlContinuousPart +. analyticalKlDiscretePart, ~digits=0) From 26afc964954e1d6bb35e75e1bb7187860e91d01a Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Wed, 11 May 2022 15:12:36 -0400 Subject: [PATCH 12/23] Tests are as good as I can get them Value: [1e-4 to 1e-2] --- .../src/components/SquigglePlayground.tsx | 2 - .../Distributions/KlDivergence_test.res | 9 +-- .../src/rescript/MagicNumbers.res | 3 +- .../src/rescript/Utility/XYShape.res | 59 ++++++++----------- 4 files changed, 31 insertions(+), 42 deletions(-) diff --git a/packages/components/src/components/SquigglePlayground.tsx b/packages/components/src/components/SquigglePlayground.tsx index 012a5069..f403a4ce 100644 --- a/packages/components/src/components/SquigglePlayground.tsx +++ b/packages/components/src/components/SquigglePlayground.tsx @@ -134,8 +134,6 @@ let SquigglePlayground: FC = ({ bindings={defaultBindings} jsImports={defaultImports} showSummary={showSummary} - bindings={defaultBindings} - jsImports={defaultImports} /> diff --git a/packages/squiggle-lang/__tests__/Distributions/KlDivergence_test.res b/packages/squiggle-lang/__tests__/Distributions/KlDivergence_test.res index 03652738..c575accf 100644 --- a/packages/squiggle-lang/__tests__/Distributions/KlDivergence_test.res +++ b/packages/squiggle-lang/__tests__/Distributions/KlDivergence_test.res @@ -3,6 +3,7 @@ open Expect open TestHelpers open GenericDist_Fixtures +// integral of from low to high of 1 / (high - low) log(normal(mean, stdev)(x) / (1 / (high - low))) dx let klNormalUniform = (mean, stdev, low, high): float => -.Js.Math.log((high -. low) /. Js.Math.sqrt(2.0 *. MagicNumbers.Math.pi *. stdev ** 2.0)) +. 1.0 /. @@ -71,7 +72,7 @@ describe("klDivergence: continuous -> continuous -> float", () => { let kl = klDivergence(prediction, answer) let analyticalKl = klNormalUniform(10.0, 2.0, 9.0, 10.0) switch kl { - | Ok(kl') => kl'->expect->toBeSoCloseTo(analyticalKl, ~digits=3) + | Ok(kl') => kl'->expect->toBeSoCloseTo(analyticalKl, ~digits=1) | Error(err) => { Js.Console.log(DistributionTypes.Error.toString(err)) raise(KlFailed) @@ -118,8 +119,8 @@ describe("klDivergence: discrete -> discrete -> float", () => { describe("klDivergence: mixed -> mixed -> float", () => { let klDivergence = DistributionOperation.Constructors.klDivergence(~env) let mixture = a => DistributionTypes.DistributionOperation.Mixture(a) - let a' = [(floatDist, 1e0), (uniformDist, 1e0)]->mixture->run - let b' = [(point3, 1e0), (floatDist, 1e0), (normalDist10, 1e0)]->mixture->run + let a' = [(point1, 1e0), (uniformDist, 1e0)]->mixture->run + let b' = [(point1, 1e0), (floatDist, 1e0), (normalDist10, 1e0)]->mixture->run let (a, b) = switch (a', b') { | (Dist(a''), Dist(b'')) => (a'', b'') | _ => raise(MixtureFailed) @@ -130,7 +131,7 @@ describe("klDivergence: mixed -> mixed -> float", () => { let kl = klDivergence(prediction, answer) // high = 10; low = 9; mean = 10; stdev = 2 let analyticalKlContinuousPart = klNormalUniform(10.0, 2.0, 9.0, 10.0) - let analyticalKlDiscretePart = 2.0 /. 3.0 *. Js.Math.log(2.0 /. 3.0) + let analyticalKlDiscretePart = Js.Math.log(2.0 /. 3.0) /. 2.0 switch kl { | Ok(kl') => kl'->expect->toBeSoCloseTo(analyticalKlContinuousPart +. analyticalKlDiscretePart, ~digits=0) diff --git a/packages/squiggle-lang/src/rescript/MagicNumbers.res b/packages/squiggle-lang/src/rescript/MagicNumbers.res index 57e33e3b..13beafe4 100644 --- a/packages/squiggle-lang/src/rescript/MagicNumbers.res +++ b/packages/squiggle-lang/src/rescript/MagicNumbers.res @@ -11,8 +11,7 @@ module Epsilon = { module Environment = { let defaultXYPointLength = 1000 - let defaultSampleCount = 1000 - let enrichmentFactor = 10 + let defaultSampleCount = 10000 } module OpCost = { diff --git a/packages/squiggle-lang/src/rescript/Utility/XYShape.res b/packages/squiggle-lang/src/rescript/Utility/XYShape.res index 01b59670..e3bd9016 100644 --- a/packages/squiggle-lang/src/rescript/Utility/XYShape.res +++ b/packages/squiggle-lang/src/rescript/Utility/XYShape.res @@ -453,46 +453,37 @@ module PointwiseCombination = { T.filterOkYs(newXs, newYs)->Ok } + // Nuño wrote this function to try to increase precision, but it didn't work. let enrichXyShape = (t: T.t): T.t => { + let enrichmentFactor = 10 let length = E.A.length(t.xs) - Js.Console.log(length) - let points = switch length < MagicNumbers.Environment.defaultXYPointLength { - | true => - Belt.Int.fromFloat( - Belt.Float.fromInt( - MagicNumbers.Environment.enrichmentFactor * MagicNumbers.Environment.defaultXYPointLength, - ) /. - Belt.Float.fromInt(length), - ) - | false => MagicNumbers.Environment.enrichmentFactor - } + let points = + length < MagicNumbers.Environment.defaultXYPointLength + ? enrichmentFactor * MagicNumbers.Environment.defaultXYPointLength / length + : enrichmentFactor let getInBetween = (x1: float, x2: float): array => { - switch x1 -. x2 > 2.0 *. MagicNumbers.Epsilon.seven { - | false => [x1] - | true => { - let newPointsArray = Belt.Array.makeBy(points - 1, i => i) - // don't repeat the x2 point, it will be gotten in the next iteration. - let result = Js.Array.mapi((pos, i) => - switch i { - | 0 => x1 - | _ => - x1 *. - (Belt.Float.fromInt(points) -. Belt.Float.fromInt(pos)) /. - Belt.Float.fromInt(points) +. - x2 *. Belt.Float.fromInt(pos) /. Belt.Float.fromInt(points) - } - , newPointsArray) - result - } + if abs_float(x1 -. x2) < 2.0 *. MagicNumbers.Epsilon.seven { + [x1] + } else { + let newPointsArray = Belt.Array.makeBy(points - 1, i => i) + // don't repeat the x2 point, it will be gotten in the next iteration. + let result = Js.Array.mapi((pos, i) => + if i == 0 { + x1 + } else { + let points' = Belt.Float.fromInt(points) + let pos' = Belt.Float.fromInt(pos) + x1 *. (points' -. pos') /. points' +. x2 *. pos' /. points' + } + , newPointsArray) + result } } - let newXsUnflattened = Js.Array.mapi((x, i) => - switch i < length - 2 { - | true => getInBetween(x, t.xs[i + 1]) - | false => [x] - } - , t.xs) + let newXsUnflattened = Js.Array.mapi( + (x, i) => i < length - 2 ? getInBetween(x, t.xs[i + 1]) : [x], + t.xs, + ) let newXs = Belt.Array.concatMany(newXsUnflattened) let newYs = E.A.fmap(x => XtoY.linear(x, t), newXs) {xs: newXs, ys: newYs} From e1e5e3305da1f1a4d5ab417d31eefa7f919879b1 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Wed, 11 May 2022 15:46:57 -0400 Subject: [PATCH 13/23] Gained precision Value: [1e-3 to 2e-2] --- .../Distributions/GenericDist_Fixtures.res | 1 + .../Distributions/KlDivergence_test.res | 29 +++++++++++++++---- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/packages/squiggle-lang/__tests__/Distributions/GenericDist_Fixtures.res b/packages/squiggle-lang/__tests__/Distributions/GenericDist_Fixtures.res index 8e315599..d184b61b 100644 --- a/packages/squiggle-lang/__tests__/Distributions/GenericDist_Fixtures.res +++ b/packages/squiggle-lang/__tests__/Distributions/GenericDist_Fixtures.res @@ -11,6 +11,7 @@ let triangularDist: DistributionTypes.genericDist = Symbolic( ) let exponentialDist: DistributionTypes.genericDist = Symbolic(#Exponential({rate: 2.0})) let uniformDist: DistributionTypes.genericDist = Symbolic(#Uniform({low: 9.0, high: 10.0})) +let uniformDist2: DistributionTypes.genericDist = Symbolic(#Uniform({low: 8.0, high: 11.0})) let floatDist: DistributionTypes.genericDist = Symbolic(#Float(1e1)) exception KlFailed diff --git a/packages/squiggle-lang/__tests__/Distributions/KlDivergence_test.res b/packages/squiggle-lang/__tests__/Distributions/KlDivergence_test.res index c575accf..5355f14d 100644 --- a/packages/squiggle-lang/__tests__/Distributions/KlDivergence_test.res +++ b/packages/squiggle-lang/__tests__/Distributions/KlDivergence_test.res @@ -121,8 +121,13 @@ describe("klDivergence: mixed -> mixed -> float", () => { let mixture = a => DistributionTypes.DistributionOperation.Mixture(a) let a' = [(point1, 1e0), (uniformDist, 1e0)]->mixture->run let b' = [(point1, 1e0), (floatDist, 1e0), (normalDist10, 1e0)]->mixture->run - let (a, b) = switch (a', b') { - | (Dist(a''), Dist(b'')) => (a'', b'') + let c' = [(point1, 1e0), (point2, 1e0), (point3, 1e0), (uniformDist, 1e0)]->mixture->run + let d' = + [(point1, 1e0), (point2, 1e0), (point3, 1e0), (floatDist, 1e0), (uniformDist2, 1e0)] + ->mixture + ->run + let (a, b, c, d) = switch (a', b', c', d') { + | (Dist(a''), Dist(b''), Dist(c''), Dist(d'')) => (a'', b'', c'', d'') | _ => raise(MixtureFailed) } test("finite klDivergence return is correct", () => { @@ -130,11 +135,11 @@ describe("klDivergence: mixed -> mixed -> float", () => { let answer = a let kl = klDivergence(prediction, answer) // high = 10; low = 9; mean = 10; stdev = 2 - let analyticalKlContinuousPart = klNormalUniform(10.0, 2.0, 9.0, 10.0) - let analyticalKlDiscretePart = Js.Math.log(2.0 /. 3.0) /. 2.0 + let analyticalKlContinuousPart = klNormalUniform(10.0, 2.0, 9.0, 10.0) /. 2.0 + let analyticalKlDiscretePart = 1.0 /. 2.0 *. Js.Math.log(2.0 /. 1.0) switch kl { | Ok(kl') => - kl'->expect->toBeSoCloseTo(analyticalKlContinuousPart +. analyticalKlDiscretePart, ~digits=0) + kl'->expect->toBeSoCloseTo(analyticalKlContinuousPart +. analyticalKlDiscretePart, ~digits=1) | Error(err) => Js.Console.log(DistributionTypes.Error.toString(err)) raise(KlFailed) @@ -151,6 +156,20 @@ describe("klDivergence: mixed -> mixed -> float", () => { raise(KlFailed) } }) + test("finite klDivergence return is correct", () => { + let prediction = d + let answer = c + let kl = klDivergence(prediction, answer) + let analyticalKlContinuousPart = Js.Math.log((11.0 -. 8.0) /. (10.0 -. 9.0)) /. 4.0 // 4 = length of c' array + let analyticalKlDiscretePart = 3.0 /. 4.0 *. Js.Math.log(4.0 /. 3.0) + switch kl { + | Ok(kl') => + kl'->expect->toBeSoCloseTo(analyticalKlContinuousPart +. analyticalKlDiscretePart, ~digits=1) + | Error(err) => + Js.Console.log(DistributionTypes.Error.toString(err)) + raise(KlFailed) + } + }) }) describe("combineAlongSupportOfSecondArgument0", () => { From b90a0e7a1a73d1f2a0ab9f577c771831ad61fcbd Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Wed, 11 May 2022 15:48:50 -0400 Subject: [PATCH 14/23] re-added `?`, artefact from MC resolve mistake Value: [1e-7 to 1e-4] --- packages/components/src/components/SquiggleChart.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/components/SquiggleChart.tsx b/packages/components/src/components/SquiggleChart.tsx index 15400d3f..241cd772 100644 --- a/packages/components/src/components/SquiggleChart.tsx +++ b/packages/components/src/components/SquiggleChart.tsx @@ -216,7 +216,7 @@ export interface SquiggleChartProps { /** Bindings of previous variables declared */ bindings?: bindings; /** JS imported parameters */ - jsImports: jsImports; + jsImports?: jsImports; /** Whether to show a summary of the distirbution */ showSummary?: boolean; /** Whether to show type information about returns, default false */ From f5e3701a79875e2a43aeaa4b4fd83bf14e4017a7 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Thu, 12 May 2022 09:51:20 -0400 Subject: [PATCH 15/23] Clean up for CR Value: [1e-7 to 43-4] --- .../__tests__/Distributions/KlDivergence_test.res | 12 ++++++------ .../Distributions/PointSetDist/Continuous.res | 6 +----- .../squiggle-lang/src/rescript/Utility/XYShape.res | 9 +++++---- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/packages/squiggle-lang/__tests__/Distributions/KlDivergence_test.res b/packages/squiggle-lang/__tests__/Distributions/KlDivergence_test.res index 5355f14d..6fb73366 100644 --- a/packages/squiggle-lang/__tests__/Distributions/KlDivergence_test.res +++ b/packages/squiggle-lang/__tests__/Distributions/KlDivergence_test.res @@ -119,18 +119,18 @@ describe("klDivergence: discrete -> discrete -> float", () => { describe("klDivergence: mixed -> mixed -> float", () => { let klDivergence = DistributionOperation.Constructors.klDivergence(~env) let mixture = a => DistributionTypes.DistributionOperation.Mixture(a) - let a' = [(point1, 1e0), (uniformDist, 1e0)]->mixture->run - let b' = [(point1, 1e0), (floatDist, 1e0), (normalDist10, 1e0)]->mixture->run - let c' = [(point1, 1e0), (point2, 1e0), (point3, 1e0), (uniformDist, 1e0)]->mixture->run + let a' = [(point1, 1.0), (uniformDist, 1.0)]->mixture->run + let b' = [(point1, 1.0), (floatDist, 1.0), (normalDist10, 1.0)]->mixture->run + let c' = [(point1, 1.0), (point2, 1.0), (point3, 1.0), (uniformDist, 1.0)]->mixture->run let d' = - [(point1, 1e0), (point2, 1e0), (point3, 1e0), (floatDist, 1e0), (uniformDist2, 1e0)] + [(point1, 1.0), (point2, 1.0), (point3, 1.0), (floatDist, 1.0), (uniformDist2, 1.0)] ->mixture ->run let (a, b, c, d) = switch (a', b', c', d') { | (Dist(a''), Dist(b''), Dist(c''), Dist(d'')) => (a'', b'', c'', d'') | _ => raise(MixtureFailed) } - test("finite klDivergence return is correct", () => { + test("finite klDivergence produces correct answer", () => { let prediction = b let answer = a let kl = klDivergence(prediction, answer) @@ -156,7 +156,7 @@ describe("klDivergence: mixed -> mixed -> float", () => { raise(KlFailed) } }) - test("finite klDivergence return is correct", () => { + test("finite klDivergence produces correct answer", () => { let prediction = d let answer = c let kl = klDivergence(prediction, answer) diff --git a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Continuous.res b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Continuous.res index e8049688..3aca0c66 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Continuous.res +++ b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Continuous.res @@ -272,14 +272,10 @@ module T = Dist({ XYShape.Analysis.getVarianceDangerously(t, mean, Analysis.getMeanOfSquares) let klDivergence = (prediction: t, answer: t) => { - let enrich = true - let enrichedAnswer = enrich - ? XYShape.PointwiseCombination.enrichXyShape(answer.xyShape) - : answer.xyShape // let newShape = XYShape.PointwiseCombination.combineAlongSupportOfSecondArgument( PointSetDist_Scoring.KLDivergence.integrand, prediction.xyShape, - enrichedAnswer, + answer.xyShape, ) let xyShapeToContinuous: XYShape.xyShape => t = xyShape => { xyShape: xyShape, diff --git a/packages/squiggle-lang/src/rescript/Utility/XYShape.res b/packages/squiggle-lang/src/rescript/Utility/XYShape.res index e3bd9016..5c3055f5 100644 --- a/packages/squiggle-lang/src/rescript/Utility/XYShape.res +++ b/packages/squiggle-lang/src/rescript/Utility/XYShape.res @@ -453,14 +453,15 @@ module PointwiseCombination = { T.filterOkYs(newXs, newYs)->Ok } - // Nuño wrote this function to try to increase precision, but it didn't work. + // *Dead code*: Nuño wrote this function to try to increase precision, but it didn't work. + // If another traveler comes through with a similar idea, we hope this implementation will help them. let enrichXyShape = (t: T.t): T.t => { - let enrichmentFactor = 10 + let defaultEnrichmentFactor = 10 let length = E.A.length(t.xs) let points = length < MagicNumbers.Environment.defaultXYPointLength - ? enrichmentFactor * MagicNumbers.Environment.defaultXYPointLength / length - : enrichmentFactor + ? defaultEnrichmentFactor * MagicNumbers.Environment.defaultXYPointLength / length + : defaultEnrichmentFactor let getInBetween = (x1: float, x2: float): array => { if abs_float(x1 -. x2) < 2.0 *. MagicNumbers.Epsilon.seven { From 239f6b0f81657fbe8259e3ca6e7d9bc536c1a728 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 12 May 2022 14:40:06 +0000 Subject: [PATCH 16/23] :arrow_up: Bump @testing-library/user-event from 14.1.1 to 14.2.0 Bumps [@testing-library/user-event](https://github.com/testing-library/user-event) from 14.1.1 to 14.2.0. - [Release notes](https://github.com/testing-library/user-event/releases) - [Changelog](https://github.com/testing-library/user-event/blob/main/CHANGELOG.md) - [Commits](https://github.com/testing-library/user-event/compare/v14.1.1...v14.2) --- updated-dependencies: - dependency-name: "@testing-library/user-event" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- packages/components/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/components/package.json b/packages/components/package.json index b269735f..e8513982 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -28,7 +28,7 @@ "@storybook/react": "^6.4.22", "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^13.2.0", - "@testing-library/user-event": "^14.1.1", + "@testing-library/user-event": "^14.2.0", "@types/jest": "^27.5.0", "@types/lodash": "^4.14.182", "@types/node": "^17.0.32", diff --git a/yarn.lock b/yarn.lock index 99712b31..81c8c21f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3726,10 +3726,10 @@ "@testing-library/dom" "^8.5.0" "@types/react-dom" "^18.0.0" -"@testing-library/user-event@^14.1.1": - version "14.1.1" - resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.1.1.tgz#e1ff6118896e4b22af31e5ea2f9da956adde23d8" - integrity sha512-XrjH/iEUqNl9lF2HX9YhPNV7Amntkcnpw0Bo1KkRzowNDcgSN9i0nm4Q8Oi5wupgdfPaJNMAWa61A+voD6Kmwg== +"@testing-library/user-event@^14.2.0": + version "14.2.0" + resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.2.0.tgz#8293560f8f80a00383d6c755ec3e0b918acb1683" + integrity sha512-+hIlG4nJS6ivZrKnOP7OGsDu9Fxmryj9vCl8x0ZINtTJcCHs2zLsYif5GzuRiBF2ck5GZG2aQr7Msg+EHlnYVQ== "@tootallnate/once@1": version "1.1.2" From 12c0959f38d0cec4904fc34aa0af14983f7ea6d9 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Thu, 12 May 2022 11:14:59 -0400 Subject: [PATCH 17/23] factored mixture construction into function Value: [1e-6 to 1e-4] --- .../Distributions/KlDivergence_test.res | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/packages/squiggle-lang/__tests__/Distributions/KlDivergence_test.res b/packages/squiggle-lang/__tests__/Distributions/KlDivergence_test.res index 6fb73366..fc528e08 100644 --- a/packages/squiggle-lang/__tests__/Distributions/KlDivergence_test.res +++ b/packages/squiggle-lang/__tests__/Distributions/KlDivergence_test.res @@ -3,7 +3,7 @@ open Expect open TestHelpers open GenericDist_Fixtures -// integral of from low to high of 1 / (high - low) log(normal(mean, stdev)(x) / (1 / (high - low))) dx +// integral from low to high of 1 / (high - low) log(normal(mean, stdev)(x) / (1 / (high - low))) dx let klNormalUniform = (mean, stdev, low, high): float => -.Js.Math.log((high -. low) /. Js.Math.sqrt(2.0 *. MagicNumbers.Math.pi *. stdev ** 2.0)) +. 1.0 /. @@ -118,18 +118,20 @@ describe("klDivergence: discrete -> discrete -> float", () => { describe("klDivergence: mixed -> mixed -> float", () => { let klDivergence = DistributionOperation.Constructors.klDivergence(~env) - let mixture = a => DistributionTypes.DistributionOperation.Mixture(a) - let a' = [(point1, 1.0), (uniformDist, 1.0)]->mixture->run - let b' = [(point1, 1.0), (floatDist, 1.0), (normalDist10, 1.0)]->mixture->run - let c' = [(point1, 1.0), (point2, 1.0), (point3, 1.0), (uniformDist, 1.0)]->mixture->run - let d' = - [(point1, 1.0), (point2, 1.0), (point3, 1.0), (floatDist, 1.0), (uniformDist2, 1.0)] - ->mixture - ->run - let (a, b, c, d) = switch (a', b', c', d') { - | (Dist(a''), Dist(b''), Dist(c''), Dist(d'')) => (a'', b'', c'', d'') - | _ => raise(MixtureFailed) + let mixture' = a => DistributionTypes.DistributionOperation.Mixture(a) + let mixture = a => { + let dist' = a->mixture'->run + switch dist' { + | Dist(dist) => dist + | _ => raise(MixtureFailed) + } } + let a = [(point1, 1.0), (uniformDist, 1.0)]->mixture + let b = [(point1, 1.0), (floatDist, 1.0), (normalDist10, 1.0)]->mixture + let c = [(point1, 1.0), (point2, 1.0), (point3, 1.0), (uniformDist, 1.0)]->mixture + let d = + [(point1, 1.0), (point2, 1.0), (point3, 1.0), (floatDist, 1.0), (uniformDist2, 1.0)]->mixture + test("finite klDivergence produces correct answer", () => { let prediction = b let answer = a From eee9a46c3742fa2f526447b5ed047b0927f9eb63 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Thu, 12 May 2022 11:16:51 -0400 Subject: [PATCH 18/23] Improved comment for `enrich` Value: [1e-9 to 1e-6] --- packages/squiggle-lang/src/rescript/Utility/XYShape.res | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/squiggle-lang/src/rescript/Utility/XYShape.res b/packages/squiggle-lang/src/rescript/Utility/XYShape.res index 5c3055f5..b4758dfd 100644 --- a/packages/squiggle-lang/src/rescript/Utility/XYShape.res +++ b/packages/squiggle-lang/src/rescript/Utility/XYShape.res @@ -453,8 +453,10 @@ module PointwiseCombination = { T.filterOkYs(newXs, newYs)->Ok } - // *Dead code*: Nuño wrote this function to try to increase precision, but it didn't work. - // If another traveler comes through with a similar idea, we hope this implementation will help them. + /* *Dead code*: Nuño wrote this function to try to increase precision, but it didn't work. + If another traveler comes through with a similar idea, we hope this implementation will help them. + By "enrich" we mean to increase granularity. + */ let enrichXyShape = (t: T.t): T.t => { let defaultEnrichmentFactor = 10 let length = E.A.length(t.xs) From ae2ee485119dff66584b6d6d9634c78fb8a5452e Mon Sep 17 00:00:00 2001 From: Sam Nolan Date: Fri, 13 May 2022 15:19:16 +0000 Subject: [PATCH 19/23] Memo the sampling of functions --- .../src/components/FunctionChart.tsx | 48 ++++++++++--------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/packages/components/src/components/FunctionChart.tsx b/packages/components/src/components/FunctionChart.tsx index 2ee71721..c5068d9a 100644 --- a/packages/components/src/components/FunctionChart.tsx +++ b/packages/components/src/components/FunctionChart.tsx @@ -76,28 +76,32 @@ export const FunctionChart: React.FC = ({ chartSettings.count ); type point = { x: number; value: result }; - let valueData: point[] = data1.map((x) => { - let result = runForeign(fn, [x], environment); - if (result.tag === "Ok") { - if (result.value.tag == "distribution") { - return { x, value: { tag: "Ok", value: result.value.value } }; - } else { - return { - x, - value: { - tag: "Error", - value: - "Cannot currently render functions that don't return distributions", - }, - }; - } - } else { - return { - x, - value: { tag: "Error", value: errorValueToString(result.value) }, - }; - } - }); + let valueData: point[] = React.useMemo( + () => + data1.map((x) => { + let result = runForeign(fn, [x], environment); + if (result.tag === "Ok") { + if (result.value.tag == "distribution") { + return { x, value: { tag: "Ok", value: result.value.value } }; + } else { + return { + x, + value: { + tag: "Error", + value: + "Cannot currently render functions that don't return distributions", + }, + }; + } + } else { + return { + x, + value: { tag: "Error", value: errorValueToString(result.value) }, + }; + } + }), + [environment, fn] + ); let initialPartition: [ { x: number; value: Distribution }[], From 006204f1ac25aba8370d9a09abc93ef3a44081d2 Mon Sep 17 00:00:00 2001 From: Sam Nolan Date: Fri, 13 May 2022 15:40:52 +0000 Subject: [PATCH 20/23] Add react keys to remove warnings --- packages/components/src/components/FunctionChart.tsx | 4 ++-- packages/components/src/components/SquiggleChart.tsx | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/components/src/components/FunctionChart.tsx b/packages/components/src/components/FunctionChart.tsx index 2ee71721..8038a36c 100644 --- a/packages/components/src/components/FunctionChart.tsx +++ b/packages/components/src/components/FunctionChart.tsx @@ -141,10 +141,10 @@ export const FunctionChart: React.FC = ({ /> {showChart} {_.entries(groupedErrors).map(([errorName, errorPoints]) => ( - + Values:{" "} {errorPoints - .map((r) => ) + .map((r, i) => ) .reduce((a, b) => ( <> {a}, {b} diff --git a/packages/components/src/components/SquiggleChart.tsx b/packages/components/src/components/SquiggleChart.tsx index 241cd772..a54ac64d 100644 --- a/packages/components/src/components/SquiggleChart.tsx +++ b/packages/components/src/components/SquiggleChart.tsx @@ -148,8 +148,9 @@ const SquiggleItem: React.FC = ({ case "array": return ( - {expression.value.map((r) => ( + {expression.value.map((r, i) => ( = ({ return ( {Object.entries(expression.value).map(([key, r]) => ( - <> +
{key} = ({ chartSettings={chartSettings} environment={environment} /> - +
))}
); case "arraystring": return ( - {expression.value.map((r) => `"${r}"`)} + {expression.value.map((r) => `"${r}"`).join(", ")} ); case "lambda": From fbb79b261dfeb7b8253443392f1f0fa98b309412 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Fri, 13 May 2022 13:40:59 -0400 Subject: [PATCH 21/23] hotfix: increment package version --- packages/squiggle-lang/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/squiggle-lang/package.json b/packages/squiggle-lang/package.json index ff786b91..d21f2eb6 100644 --- a/packages/squiggle-lang/package.json +++ b/packages/squiggle-lang/package.json @@ -1,6 +1,6 @@ { "name": "@quri/squiggle-lang", - "version": "0.2.8", + "version": "0.2.9", "homepage": "https://squiggle-language.com", "license": "MIT", "scripts": { From 80e05ea5bea27865e8127f3aaa4b0a9cfd8145ce Mon Sep 17 00:00:00 2001 From: Sam Nolan Date: Fri, 13 May 2022 20:16:52 +0000 Subject: [PATCH 22/23] Pass environment down to GenericDist --- .../ReducerInterface_GenericDistribution.res | 126 +++++++++++------- 1 file changed, 76 insertions(+), 50 deletions(-) diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res index 8dae2586..a50fbede 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res @@ -6,8 +6,6 @@ let defaultEnv: DistributionOperation.env = { xyPointLength: MagicNumbers.Environment.defaultXYPointLength, } -let runGenericOperation = DistributionOperation.run(~env=defaultEnv) - module Helpers = { let arithmeticMap = r => switch r { @@ -39,37 +37,44 @@ module Helpers = { let toFloatFn = ( fnCall: DistributionTypes.DistributionOperation.toFloat, dist: DistributionTypes.genericDist, + ~env: DistributionOperation.env, ) => { FromDist(DistributionTypes.DistributionOperation.ToFloat(fnCall), dist) - ->runGenericOperation + ->DistributionOperation.run(~env) ->Some } let toStringFn = ( fnCall: DistributionTypes.DistributionOperation.toString, dist: DistributionTypes.genericDist, + ~env: DistributionOperation.env, ) => { FromDist(DistributionTypes.DistributionOperation.ToString(fnCall), dist) - ->runGenericOperation + ->DistributionOperation.run(~env) ->Some } let toBoolFn = ( fnCall: DistributionTypes.DistributionOperation.toBool, dist: DistributionTypes.genericDist, + ~env: DistributionOperation.env, ) => { FromDist(DistributionTypes.DistributionOperation.ToBool(fnCall), dist) - ->runGenericOperation + ->DistributionOperation.run(~env) ->Some } - let toDistFn = (fnCall: DistributionTypes.DistributionOperation.toDist, dist) => { + let toDistFn = ( + fnCall: DistributionTypes.DistributionOperation.toDist, + dist, + ~env: DistributionOperation.env, + ) => { FromDist(DistributionTypes.DistributionOperation.ToDist(fnCall), dist) - ->runGenericOperation + ->DistributionOperation.run(~env) ->Some } - let twoDiststoDistFn = (direction, arithmetic, dist1, dist2) => { + let twoDiststoDistFn = (direction, arithmetic, dist1, dist2, ~env: DistributionOperation.env) => { FromDist( DistributionTypes.DistributionOperation.ToDistCombination( direction, @@ -77,7 +82,7 @@ module Helpers = { #Dist(dist2), ), dist1, - )->runGenericOperation + )->DistributionOperation.run(~env) } let parseNumber = (args: expressionValue): Belt.Result.t => @@ -104,33 +109,38 @@ module Helpers = { let mixtureWithGivenWeights = ( distributions: array, weights: array, + ~env: DistributionOperation.env, ): DistributionOperation.outputType => E.A.length(distributions) == E.A.length(weights) - ? Mixture(Belt.Array.zip(distributions, weights))->runGenericOperation + ? Mixture(Belt.Array.zip(distributions, weights))->DistributionOperation.run(~env) : GenDistError( ArgumentError("Error, mixture call has different number of distributions and weights"), ) let mixtureWithDefaultWeights = ( distributions: array, + ~env: DistributionOperation.env, ): DistributionOperation.outputType => { let length = E.A.length(distributions) let weights = Belt.Array.make(length, 1.0 /. Belt.Int.toFloat(length)) - mixtureWithGivenWeights(distributions, weights) + mixtureWithGivenWeights(distributions, weights, ~env) } - let mixture = (args: array): DistributionOperation.outputType => { + let mixture = ( + args: array, + ~env: DistributionOperation.env, + ): DistributionOperation.outputType => { let error = (err: string): DistributionOperation.outputType => err->DistributionTypes.ArgumentError->GenDistError switch args { | [EvArray(distributions)] => switch parseDistributionArray(distributions) { - | Ok(distrs) => mixtureWithDefaultWeights(distrs) + | Ok(distrs) => mixtureWithDefaultWeights(distrs, ~env) | Error(err) => error(err) } | [EvArray(distributions), EvArray(weights)] => switch (parseDistributionArray(distributions), parseNumberArray(weights)) { - | (Ok(distrs), Ok(wghts)) => mixtureWithGivenWeights(distrs, wghts) + | (Ok(distrs), Ok(wghts)) => mixtureWithGivenWeights(distrs, wghts, ~env) | (Error(err), Ok(_)) => error(err) | (Ok(_), Error(err)) => error(err) | (Error(err1), Error(err2)) => error(`${err1}|${err2}`) @@ -143,14 +153,14 @@ module Helpers = { Belt.Array.slice(args, ~offset=0, ~len=E.A.length(args) - 1), ) switch E.R.merge(distributions, weights) { - | Ok(d, w) => mixtureWithGivenWeights(d, w) + | Ok(d, w) => mixtureWithGivenWeights(d, w, ~env) | Error(err) => error(err) } } | Some(EvNumber(_)) | Some(EvDistribution(_)) => switch parseDistributionArray(args) { - | Ok(distributions) => mixtureWithDefaultWeights(distributions) + | Ok(distributions) => mixtureWithDefaultWeights(distributions, ~env) | Error(err) => error(err) } | _ => error("Last argument of mx must be array or distribution") @@ -193,9 +203,10 @@ module SymbolicConstructors = { } } -let dispatchToGenericOutput = (call: ExpressionValue.functionCall, _environment): option< - DistributionOperation.outputType, -> => { +let dispatchToGenericOutput = ( + call: ExpressionValue.functionCall, + env: DistributionOperation.env, +): option => { let (fnName, args) = call switch (fnName, args) { | ("exponential" as fnName, [EvNumber(f)]) => @@ -215,13 +226,13 @@ let dispatchToGenericOutput = (call: ExpressionValue.functionCall, _environment) SymbolicConstructors.threeFloat(fnName) ->E.R.bind(r => r(f1, f2, f3)) ->SymbolicConstructors.symbolicResultToOutput - | ("sample", [EvDistribution(dist)]) => Helpers.toFloatFn(#Sample, dist) - | ("mean", [EvDistribution(dist)]) => Helpers.toFloatFn(#Mean, dist) - | ("integralSum", [EvDistribution(dist)]) => Helpers.toFloatFn(#IntegralSum, dist) - | ("toString", [EvDistribution(dist)]) => Helpers.toStringFn(ToString, dist) - | ("toSparkline", [EvDistribution(dist)]) => Helpers.toStringFn(ToSparkline(20), dist) + | ("sample", [EvDistribution(dist)]) => Helpers.toFloatFn(#Sample, dist, ~env) + | ("mean", [EvDistribution(dist)]) => Helpers.toFloatFn(#Mean, dist, ~env) + | ("integralSum", [EvDistribution(dist)]) => Helpers.toFloatFn(#IntegralSum, dist, ~env) + | ("toString", [EvDistribution(dist)]) => Helpers.toStringFn(ToString, dist, ~env) + | ("toSparkline", [EvDistribution(dist)]) => Helpers.toStringFn(ToSparkline(20), dist, ~env) | ("toSparkline", [EvDistribution(dist), EvNumber(n)]) => - Helpers.toStringFn(ToSparkline(Belt.Float.toInt(n)), dist) + Helpers.toStringFn(ToSparkline(Belt.Float.toInt(n)), dist, ~env) | ("exp", [EvDistribution(a)]) => // https://mathjs.org/docs/reference/functions/exp.html Helpers.twoDiststoDistFn( @@ -229,60 +240,74 @@ let dispatchToGenericOutput = (call: ExpressionValue.functionCall, _environment) "pow", GenericDist.fromFloat(MagicNumbers.Math.e), a, + ~env, )->Some - | ("normalize", [EvDistribution(dist)]) => Helpers.toDistFn(Normalize, dist) + | ("normalize", [EvDistribution(dist)]) => Helpers.toDistFn(Normalize, dist, ~env) | ("klDivergence", [EvDistribution(a), EvDistribution(b)]) => - Some(runGenericOperation(FromDist(ToScore(KLDivergence(b)), a))) - | ("isNormalized", [EvDistribution(dist)]) => Helpers.toBoolFn(IsNormalized, dist) - | ("toPointSet", [EvDistribution(dist)]) => Helpers.toDistFn(ToPointSet, dist) + Some(DistributionOperation.run(FromDist(ToScore(KLDivergence(b)), a), ~env)) + | ("isNormalized", [EvDistribution(dist)]) => Helpers.toBoolFn(IsNormalized, dist, ~env) + | ("toPointSet", [EvDistribution(dist)]) => Helpers.toDistFn(ToPointSet, dist, ~env) | ("scaleLog", [EvDistribution(dist)]) => - Helpers.toDistFn(Scale(#Logarithm, MagicNumbers.Math.e), dist) - | ("scaleLog10", [EvDistribution(dist)]) => Helpers.toDistFn(Scale(#Logarithm, 10.0), dist) + Helpers.toDistFn(Scale(#Logarithm, MagicNumbers.Math.e), dist, ~env) + | ("scaleLog10", [EvDistribution(dist)]) => Helpers.toDistFn(Scale(#Logarithm, 10.0), dist, ~env) | ("scaleLog", [EvDistribution(dist), EvNumber(float)]) => - Helpers.toDistFn(Scale(#Logarithm, float), dist) + Helpers.toDistFn(Scale(#Logarithm, float), dist, ~env) | ("scaleLogWithThreshold", [EvDistribution(dist), EvNumber(base), EvNumber(eps)]) => - Helpers.toDistFn(Scale(#LogarithmWithThreshold(eps), base), dist) + Helpers.toDistFn(Scale(#LogarithmWithThreshold(eps), base), dist, ~env) | ("scalePow", [EvDistribution(dist), EvNumber(float)]) => - Helpers.toDistFn(Scale(#Power, float), dist) + Helpers.toDistFn(Scale(#Power, float), dist, ~env) | ("scaleExp", [EvDistribution(dist)]) => - Helpers.toDistFn(Scale(#Power, MagicNumbers.Math.e), dist) - | ("cdf", [EvDistribution(dist), EvNumber(float)]) => Helpers.toFloatFn(#Cdf(float), dist) - | ("pdf", [EvDistribution(dist), EvNumber(float)]) => Helpers.toFloatFn(#Pdf(float), dist) - | ("inv", [EvDistribution(dist), EvNumber(float)]) => Helpers.toFloatFn(#Inv(float), dist) + Helpers.toDistFn(Scale(#Power, MagicNumbers.Math.e), dist, ~env) + | ("cdf", [EvDistribution(dist), EvNumber(float)]) => Helpers.toFloatFn(#Cdf(float), dist, ~env) + | ("pdf", [EvDistribution(dist), EvNumber(float)]) => Helpers.toFloatFn(#Pdf(float), dist, ~env) + | ("inv", [EvDistribution(dist), EvNumber(float)]) => Helpers.toFloatFn(#Inv(float), dist, ~env) | ("toSampleSet", [EvDistribution(dist), EvNumber(float)]) => - Helpers.toDistFn(ToSampleSet(Belt.Int.fromFloat(float)), dist) + Helpers.toDistFn(ToSampleSet(Belt.Int.fromFloat(float)), dist, ~env) | ("toSampleSet", [EvDistribution(dist)]) => - Helpers.toDistFn(ToSampleSet(MagicNumbers.Environment.defaultSampleCount), dist) + Helpers.toDistFn(ToSampleSet(env.sampleCount), dist, ~env) | ("fromSamples", [EvArray(inputArray)]) => { let _wrapInputErrors = x => SampleSetDist.NonNumericInput(x) let parsedArray = Helpers.parseNumberArray(inputArray)->E.R2.errMap(_wrapInputErrors) switch parsedArray { - | Ok(array) => runGenericOperation(FromSamples(array)) + | Ok(array) => DistributionOperation.run(FromSamples(array), ~env) | Error(e) => GenDistError(SampleSetError(e)) }->Some } - | ("inspect", [EvDistribution(dist)]) => Helpers.toDistFn(Inspect, dist) + | ("inspect", [EvDistribution(dist)]) => Helpers.toDistFn(Inspect, dist, ~env) | ("truncateLeft", [EvDistribution(dist), EvNumber(float)]) => - Helpers.toDistFn(Truncate(Some(float), None), dist) + Helpers.toDistFn(Truncate(Some(float), None), dist, ~env) | ("truncateRight", [EvDistribution(dist), EvNumber(float)]) => - Helpers.toDistFn(Truncate(None, Some(float)), dist) + Helpers.toDistFn(Truncate(None, Some(float)), dist, ~env) | ("truncate", [EvDistribution(dist), EvNumber(float1), EvNumber(float2)]) => - Helpers.toDistFn(Truncate(Some(float1), Some(float2)), dist) - | ("mx" | "mixture", args) => Helpers.mixture(args)->Some + Helpers.toDistFn(Truncate(Some(float1), Some(float2)), dist, ~env) + | ("mx" | "mixture", args) => Helpers.mixture(args, ~env)->Some | ("log", [EvDistribution(a)]) => Helpers.twoDiststoDistFn( Algebraic(AsDefault), "log", a, GenericDist.fromFloat(MagicNumbers.Math.e), + ~env, )->Some | ("log10", [EvDistribution(a)]) => - Helpers.twoDiststoDistFn(Algebraic(AsDefault), "log", a, GenericDist.fromFloat(10.0))->Some + Helpers.twoDiststoDistFn( + Algebraic(AsDefault), + "log", + a, + GenericDist.fromFloat(10.0), + ~env, + )->Some | ("unaryMinus", [EvDistribution(a)]) => - Helpers.twoDiststoDistFn(Algebraic(AsDefault), "multiply", a, GenericDist.fromFloat(-1.0))->Some + Helpers.twoDiststoDistFn( + Algebraic(AsDefault), + "multiply", + a, + GenericDist.fromFloat(-1.0), + ~env, + )->Some | (("add" | "multiply" | "subtract" | "divide" | "pow" | "log") as arithmetic, [_, _] as args) => Helpers.catchAndConvertTwoArgsToDists(args)->E.O2.fmap(((fst, snd)) => - Helpers.twoDiststoDistFn(Algebraic(AsDefault), arithmetic, fst, snd) + Helpers.twoDiststoDistFn(Algebraic(AsDefault), arithmetic, fst, snd, ~env) ) | ( ("dotAdd" @@ -293,7 +318,7 @@ let dispatchToGenericOutput = (call: ExpressionValue.functionCall, _environment) [_, _] as args, ) => Helpers.catchAndConvertTwoArgsToDists(args)->E.O2.fmap(((fst, snd)) => - Helpers.twoDiststoDistFn(Pointwise, arithmetic, fst, snd) + Helpers.twoDiststoDistFn(Pointwise, arithmetic, fst, snd, ~env) ) | ("dotExp", [EvDistribution(a)]) => Helpers.twoDiststoDistFn( @@ -301,6 +326,7 @@ let dispatchToGenericOutput = (call: ExpressionValue.functionCall, _environment) "dotPow", GenericDist.fromFloat(MagicNumbers.Math.e), a, + ~env, )->Some | _ => None } From 95d0212ffc32c70ee643afd2f2443a75fe0c484b Mon Sep 17 00:00:00 2001 From: Sam Nolan Date: Fri, 13 May 2022 20:23:41 +0000 Subject: [PATCH 23/23] Remove redundant defaultEnv --- .../ReducerInterface_GenericDistribution.res | 5 ----- .../ReducerInterface_GenericDistribution.resi | 1 - packages/squiggle-lang/src/rescript/TypescriptInterface.res | 2 +- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res index a50fbede..73614aee 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res @@ -1,11 +1,6 @@ module ExpressionValue = ReducerInterface_ExpressionValue type expressionValue = ReducerInterface_ExpressionValue.expressionValue -let defaultEnv: DistributionOperation.env = { - sampleCount: MagicNumbers.Environment.defaultSampleCount, - xyPointLength: MagicNumbers.Environment.defaultXYPointLength, -} - module Helpers = { let arithmeticMap = r => switch r { diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.resi b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.resi index 038f4479..7f26a610 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.resi +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.resi @@ -1,4 +1,3 @@ -let defaultEnv: DistributionOperation.env let dispatch: ( ReducerInterface_ExpressionValue.functionCall, ReducerInterface_ExpressionValue.environment, diff --git a/packages/squiggle-lang/src/rescript/TypescriptInterface.res b/packages/squiggle-lang/src/rescript/TypescriptInterface.res index 13763e72..93af9832 100644 --- a/packages/squiggle-lang/src/rescript/TypescriptInterface.res +++ b/packages/squiggle-lang/src/rescript/TypescriptInterface.res @@ -77,7 +77,7 @@ let distributionErrorToString = DistributionTypes.Error.toString type lambdaValue = ReducerInterface_ExpressionValue.lambdaValue @genType -let defaultSamplingEnv = ReducerInterface_GenericDistribution.defaultEnv +let defaultSamplingEnv = DistributionOperation.defaultEnv @genType type environment = ReducerInterface_ExpressionValue.environment