diff --git a/packages/squiggle-lang/__tests__/JS__Test.ts b/packages/squiggle-lang/__tests__/JS__Test.ts index aded69c1..ba2f91f4 100644 --- a/packages/squiggle-lang/__tests__/JS__Test.ts +++ b/packages/squiggle-lang/__tests__/JS__Test.ts @@ -1,34 +1,86 @@ -import { run } from '../src/js/index'; +import { run, GenericDist, resultMap } from "../src/js/index"; let testRun = (x: string) => { - let result = run(x) - if(result.tag == 'Ok'){ - return { tag: 'Ok', value: result.value.exports } + let result = run(x); + if (result.tag == "Ok") { + return { tag: "Ok", value: result.value.exports }; + } else { + return result; } - else { - return result - } -} +}; describe("Simple calculations and results", () => { - test("mean(normal(5,2))", () => { - expect(testRun("mean(normal(5,2))")).toEqual({ tag: 'Ok', value: [ { NAME: 'Float', VAL: 5 } ] }) - }) - test("10+10", () => { - let foo = testRun("10 + 10") - expect(foo).toEqual({ tag: 'Ok', value: [ { NAME: 'Float', VAL: 20 } ] }) - }) -}) + test("mean(normal(5,2))", () => { + expect(testRun("mean(normal(5,2))")).toEqual({ + tag: "Ok", + value: [{ NAME: "Float", VAL: 5 }], + }); + }); + test("10+10", () => { + let foo = testRun("10 + 10"); + expect(foo).toEqual({ tag: "Ok", value: [{ NAME: "Float", VAL: 20 }] }); + }); +}); describe("Log function", () => { - test("log(1) = 0", () => { - let foo = testRun("log(1)") - expect(foo).toEqual({ tag: 'Ok', value: [ { NAME: 'Float', VAL: 0} ]}) - }) -}) + test("log(1) = 0", () => { + let foo = testRun("log(1)"); + expect(foo).toEqual({ tag: "Ok", value: [{ NAME: "Float", VAL: 0 }] }); + }); +}); describe("Multimodal too many weights error", () => { - test("mm(0,0,[0,0,0])", () => { - let foo = testRun("mm(0,0,[0,0,0])") - expect(foo).toEqual({ "tag": "Error", "value": "Function multimodal error: Too many weights provided" }) - }) + test("mm(0,0,[0,0,0])", () => { + let foo = testRun("mm(0,0,[0,0,0])"); + expect(foo).toEqual({ + tag: "Error", + value: "Function multimodal error: Too many weights provided", + }); + }); +}); + +describe("GenericDist", () => { + + //It's important that sampleCount is less than 9. If it's more, than that will create randomness + let env = { sampleCount: 8, xyPointLength: 100 }; + let dist = new GenericDist( + { tag: "SampleSet", value: [3, 4, 5, 6, 6, 7, 10, 15, 30] }, + env + ); + let dist2 = new GenericDist( + { tag: "SampleSet", value: [20, 22, 24, 29, 30, 35, 38, 44, 52] }, + env + ); + + test("mean", () => { + expect(dist.mean().value).toBeCloseTo(3.737); + }); + test("pdf", () => { + expect(dist.pdf(5.0).value).toBeCloseTo(0.0431); + }); + test("cdf", () => { + expect(dist.cdf(5.0).value).toBeCloseTo(0.155); + }); + test("inv", () => { + expect(dist.inv(0.5).value).toBeCloseTo(9.458); + }); + test("toPointSet", () => { + expect( + resultMap(dist.toPointSet(), (r: GenericDist) => r.toString()).value.value + ).toBe("Point Set Distribution"); + }); + test("toSparkline", () => { + expect(dist.toSparkline(20).value).toBe("▁▁▃▅███▆▄▃▂▁▁▂▂▃▂▁▁▁"); + }); + test("algebraicAdd", () => { + expect( + resultMap(dist.algebraicAdd(dist2), (r: GenericDist) => r.toSparkline(20)) + .value.value + ).toBe("▁▁▂▄▆████▇▆▄▄▃▃▃▂▁▁▁"); + }); + test("pointwiseAdd", () => { + expect( + resultMap(dist.pointwiseAdd(dist2), (r: GenericDist) => r.toSparkline(20)) + .value.value + ).toBe("▁▂▅██▅▅▅▆▇█▆▅▃▃▂▂▁▁▁"); + }); }); diff --git a/packages/squiggle-lang/bsconfig.json b/packages/squiggle-lang/bsconfig.json index e936eb82..958a3af8 100644 --- a/packages/squiggle-lang/bsconfig.json +++ b/packages/squiggle-lang/bsconfig.json @@ -33,7 +33,7 @@ "gentypeconfig": { "language": "typescript", "module": "commonjs", - "shims": {}, + "shims": {"Js": "Js"}, "debug": { "all": false, "basic": false diff --git a/packages/squiggle-lang/src/js/index.ts b/packages/squiggle-lang/src/js/index.ts index 7856ef6f..25515db6 100644 --- a/packages/squiggle-lang/src/js/index.ts +++ b/packages/squiggle-lang/src/js/index.ts @@ -1,17 +1,223 @@ -import {runAll} from '../rescript/ProgramEvaluator.gen'; -import type { Inputs_SamplingInputs_t as SamplingInputs, exportEnv, exportType, exportDistribution} from '../rescript/ProgramEvaluator.gen'; -export type { SamplingInputs, exportEnv, exportDistribution } -export type {t as DistPlus} from '../rescript/OldInterpreter/DistPlus.gen'; +import { runAll } from "../rescript/ProgramEvaluator.gen"; +import type { + Inputs_SamplingInputs_t as SamplingInputs, + exportEnv, + exportType, + exportDistribution, +} from "../rescript/ProgramEvaluator.gen"; +export type { SamplingInputs, exportEnv, exportDistribution }; +export type { t as DistPlus } from "../rescript/OldInterpreter/DistPlus.gen"; +import { + genericDist, + env, + resultDist, + resultFloat, + resultString, +} from "../rescript/TypescriptInterface.gen"; +import { + Constructors_mean, + Constructors_sample, + Constructors_pdf, + Constructors_cdf, + Constructors_inv, + Constructors_normalize, + Constructors_toPointSet, + Constructors_toSampleSet, + Constructors_truncate, + Constructors_inspect, + Constructors_toString, + Constructors_toSparkline, + Constructors_algebraicAdd, + Constructors_algebraicMultiply, + Constructors_algebraicDivide, + Constructors_algebraicSubtract, + Constructors_algebraicLogarithm, + Constructors_algebraicExponentiate, + Constructors_pointwiseAdd, + Constructors_pointwiseMultiply, + Constructors_pointwiseDivide, + Constructors_pointwiseSubtract, + Constructors_pointwiseLogarithm, + Constructors_pointwiseExponentiate, +} from "../rescript/Distributions/DistributionOperation/DistributionOperation.gen"; -export let defaultSamplingInputs : SamplingInputs = { - sampleCount : 10000, - outputXYPoints : 10000, - pointDistLength : 1000 +export let defaultSamplingInputs: SamplingInputs = { + sampleCount: 10000, + outputXYPoints: 10000, + pointDistLength: 1000, +}; + +export function run( + squiggleString: string, + samplingInputs?: SamplingInputs, + environment?: exportEnv +): { tag: "Ok"; value: exportType } | { tag: "Error"; value: string } { + let si: SamplingInputs = samplingInputs + ? samplingInputs + : defaultSamplingInputs; + let env: exportEnv = environment ? environment : []; + return runAll(squiggleString, si, env); } -export function run(squiggleString : string, samplingInputs? : SamplingInputs, environment?: exportEnv) : { tag: "Ok"; value: exportType } - | { tag: "Error"; value: string } { - let si : SamplingInputs = samplingInputs ? samplingInputs : defaultSamplingInputs - let env : exportEnv = environment ? environment : [] - return runAll(squiggleString, si, env) +//This is clearly not fully typed. I think later we should use a functional library to +// provide a better Either type and corresponding functions. +type result = + | { + tag: "Ok"; + value: any; + } + | { + tag: "Error"; + value: any; + }; + +export function resultMap(r: result, mapFn: any): result { + if (r.tag === "Ok") { + return { tag: "Ok", value: mapFn(r.value) }; + } else { + return r; + } +} + +export class GenericDist { + t: genericDist; + env: env; + + constructor(t: genericDist, env: env) { + this.t = t; + this.env = env; + return this; + } + + mapResultDist(r: resultDist) { + return resultMap(r, (v: genericDist) => new GenericDist(v, this.env)); + } + + mean() { + return Constructors_mean({ env: this.env }, this.t); + } + + sample(): resultFloat { + return Constructors_sample({ env: this.env }, this.t); + } + + pdf(n: number): resultFloat { + return Constructors_pdf({ env: this.env }, this.t, n); + } + + cdf(n: number): resultFloat { + return Constructors_cdf({ env: this.env }, this.t, n); + } + + inv(n: number): resultFloat { + return Constructors_inv({ env: this.env }, this.t, n); + } + + normalize() { + return this.mapResultDist( + Constructors_normalize({ env: this.env }, this.t) + ); + } + + toPointSet() { + return this.mapResultDist( + Constructors_toPointSet({ env: this.env }, this.t) + ); + } + + toSampleSet(n: number) { + return this.mapResultDist( + Constructors_toSampleSet({ env: this.env }, this.t, n) + ); + } + + truncate(left: number, right: number) { + return this.mapResultDist( + Constructors_truncate({ env: this.env }, this.t, left, right) + ); + } + + inspect() { + return this.mapResultDist(Constructors_inspect({ env: this.env }, this.t)); + } + + toString(): resultString { + return Constructors_toString({ env: this.env }, this.t); + } + + toSparkline(n: number): resultString { + return Constructors_toSparkline({ env: this.env }, this.t, n); + } + + algebraicAdd(d2: GenericDist) { + return this.mapResultDist( + Constructors_algebraicAdd({ env: this.env }, this.t, d2.t) + ); + } + + algebraicMultiply(d2: GenericDist) { + return this.mapResultDist( + Constructors_algebraicMultiply({ env: this.env }, this.t, d2.t) + ); + } + + algebraicDivide(d2: GenericDist) { + return this.mapResultDist( + Constructors_algebraicDivide({ env: this.env }, this.t, d2.t) + ); + } + + algebraicSubtract(d2: GenericDist) { + return this.mapResultDist( + Constructors_algebraicSubtract({ env: this.env }, this.t, d2.t) + ); + } + + algebraicLogarithm(d2: GenericDist) { + return this.mapResultDist( + Constructors_algebraicLogarithm({ env: this.env }, this.t, d2.t) + ); + } + + algebraicExponentiate(d2: GenericDist) { + return this.mapResultDist( + Constructors_algebraicExponentiate({ env: this.env }, this.t, d2.t) + ); + } + + pointwiseAdd(d2: GenericDist) { + return this.mapResultDist( + Constructors_pointwiseAdd({ env: this.env }, this.t, d2.t) + ); + } + + pointwiseMultiply(d2: GenericDist) { + return this.mapResultDist( + Constructors_pointwiseMultiply({ env: this.env }, this.t, d2.t) + ); + } + + pointwiseDivide(d2: GenericDist) { + return this.mapResultDist( + Constructors_pointwiseDivide({ env: this.env }, this.t, d2.t) + ); + } + + pointwiseSubtract(d2: GenericDist) { + return this.mapResultDist( + Constructors_pointwiseSubtract({ env: this.env }, this.t, d2.t) + ); + } + + pointwiseLogarithm(d2: GenericDist) { + return this.mapResultDist( + Constructors_pointwiseLogarithm({ env: this.env }, this.t, d2.t) + ); + } + + pointwiseExponentiate(d2: GenericDist) { + return this.mapResultDist( + Constructors_pointwiseExponentiate({ env: this.env }, this.t, d2.t) + ); + } } diff --git a/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.res b/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.res index 14e7b6c4..a36674db 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.res +++ b/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.res @@ -114,8 +114,8 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => { ->E.R2.fmap(r => Float(r)) ->OutputLocal.fromResult | ToString(ToString) => dist->GenericDist.toString->String - | ToString(ToSparkline(buckets)) => - GenericDist.toSparkline(dist, ~sampleCount, ~buckets, ()) + | ToString(ToSparkline(bucketCount)) => + GenericDist.toSparkline(dist, ~sampleCount, ~bucketCount, ()) ->E.R2.fmap(r => String(r)) ->OutputLocal.fromResult | ToDist(Inspect) => { @@ -185,3 +185,44 @@ module Output = { newFnCall->E.R2.fmap(run(~env))->OutputLocal.fromResult } } + +// See comment above GenericDist_Types.Constructors to explain the purpose of this module. +// I tried having another internal module called UsingDists, similar to how its done in +// GenericDist_Types.Constructors. However, this broke GenType for me, so beware. +module Constructors = { + module C = GenericDist_Types.Constructors.UsingDists + open OutputLocal + let mean = (~env, dist) => C.mean(dist)->run(~env)->toFloatR + let sample = (~env, dist) => C.sample(dist)->run(~env)->toFloatR + let cdf = (~env, dist, f) => C.cdf(dist, f)->run(~env)->toFloatR + let inv = (~env, dist, f) => C.inv(dist, f)->run(~env)->toFloatR + let pdf = (~env, dist, f) => C.pdf(dist, f)->run(~env)->toFloatR + let normalize = (~env, dist) => C.normalize(dist)->run(~env)->toDistR + let toPointSet = (~env, dist) => C.toPointSet(dist)->run(~env)->toDistR + let toSampleSet = (~env, dist, n) => C.toSampleSet(dist, n)->run(~env)->toDistR + let truncate = (~env, dist, leftCutoff, rightCutoff) => + C.truncate(dist, leftCutoff, rightCutoff)->run(~env)->toDistR + let inspect = (~env, dist) => C.inspect(dist)->run(~env)->toDistR + let toString = (~env, dist) => C.toString(dist)->run(~env)->toStringR + let toSparkline = (~env, dist, bucketCount) => C.toSparkline(dist, bucketCount)->run(~env)->toStringR + let algebraicAdd = (~env, dist1, dist2) => C.algebraicAdd(dist1, dist2)->run(~env)->toDistR + let algebraicMultiply = (~env, dist1, dist2) => + C.algebraicMultiply(dist1, dist2)->run(~env)->toDistR + let algebraicDivide = (~env, dist1, dist2) => C.algebraicDivide(dist1, dist2)->run(~env)->toDistR + let algebraicSubtract = (~env, dist1, dist2) => + C.algebraicSubtract(dist1, dist2)->run(~env)->toDistR + let algebraicLogarithm = (~env, dist1, dist2) => + C.algebraicLogarithm(dist1, dist2)->run(~env)->toDistR + let algebraicExponentiate = (~env, dist1, dist2) => + C.algebraicExponentiate(dist1, dist2)->run(~env)->toDistR + let pointwiseAdd = (~env, dist1, dist2) => C.pointwiseAdd(dist1, dist2)->run(~env)->toDistR + let pointwiseMultiply = (~env, dist1, dist2) => + C.pointwiseMultiply(dist1, dist2)->run(~env)->toDistR + let pointwiseDivide = (~env, dist1, dist2) => C.pointwiseDivide(dist1, dist2)->run(~env)->toDistR + let pointwiseSubtract = (~env, dist1, dist2) => + C.pointwiseSubtract(dist1, dist2)->run(~env)->toDistR + let pointwiseLogarithm = (~env, dist1, dist2) => + C.pointwiseLogarithm(dist1, dist2)->run(~env)->toDistR + let pointwiseExponentiate = (~env, dist1, dist2) => + C.pointwiseExponentiate(dist1, dist2)->run(~env)->toDistR +} diff --git a/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.resi b/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.resi index 3c3e132a..ce0fca72 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.resi +++ b/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.resi @@ -1,19 +1,24 @@ +@genType type env = { sampleCount: int, xyPointLength: int, } +open GenericDist_Types + +@genType type outputType = - | Dist(GenericDist_Types.genericDist) + | Dist(genericDist) | Float(float) | String(string) - | GenDistError(GenericDist_Types.error) + | GenDistError(error) +@genType let run: (~env: env, GenericDist_Types.Operation.genericFunctionCallInfo) => outputType let runFromDist: ( ~env: env, ~functionCallInfo: GenericDist_Types.Operation.fromDist, - GenericDist_Types.genericDist, + genericDist, ) => outputType let runFromFloat: ( ~env: env, @@ -23,12 +28,68 @@ let runFromFloat: ( module Output: { type t = outputType - let toDist: t => option - let toDistR: t => result + let toDist: t => option + let toDistR: t => result let toFloat: t => option - let toFloatR: t => result + let toFloatR: t => result let toString: t => option - let toStringR: t => result - let toError: t => option + let toStringR: t => result + let toError: t => option let fmap: (~env: env, t, GenericDist_Types.Operation.singleParamaterFunction) => t } + +module Constructors: { + @genType + let mean: (~env: env, genericDist) => result + @genType + let sample: (~env: env, genericDist) => result + @genType + let cdf: (~env: env, genericDist, float) => result + @genType + let inv: (~env: env, genericDist, float) => result + @genType + let pdf: (~env: env, genericDist, float) => result + @genType + let normalize: (~env: env, genericDist) => result + @genType + let toPointSet: (~env: env, genericDist) => result + @genType + let toSampleSet: (~env: env, genericDist, int) => result + @genType + let truncate: ( + ~env: env, + genericDist, + option, + option, + ) => result + @genType + let inspect: (~env: env, genericDist) => result + @genType + let toString: (~env: env, genericDist) => result + @genType + let toSparkline: (~env: env, genericDist, int) => result + @genType + let algebraicAdd: (~env: env, genericDist, genericDist) => result + @genType + let algebraicMultiply: (~env: env, genericDist, genericDist) => result + @genType + let algebraicDivide: (~env: env, genericDist, genericDist) => result + @genType + let algebraicSubtract: (~env: env, genericDist, genericDist) => result + @genType + let algebraicLogarithm: (~env: env, genericDist, genericDist) => result + @genType + let algebraicExponentiate: (~env: env, genericDist, genericDist) => result + @genType + let pointwiseAdd: (~env: env, genericDist, genericDist) => result + @genType + let pointwiseMultiply: (~env: env, genericDist, genericDist) => result + @genType + let pointwiseDivide: (~env: env, genericDist, genericDist) => result + @genType + let pointwiseSubtract: (~env: env, genericDist, genericDist) => result + @genType + let pointwiseLogarithm: (~env: env, genericDist, genericDist) => result + @genType + let pointwiseExponentiate: (~env: env, genericDist, genericDist) => result +} diff --git a/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.res b/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.res index 58366a77..6bcbb221 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.res +++ b/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.res @@ -10,7 +10,7 @@ let sampleN = (t: t, n) => switch t { | PointSet(r) => Ok(PointSetDist.sampleNRendered(n, r)) | Symbolic(r) => Ok(SymbolicDist.T.sampleN(n, r)) - | SampleSet(_) => Error(GenericDist_Types.NotYetImplemented) + | SampleSet(r) => Ok(SampleSet.sampleN(r, n)) } let fromFloat = (f: float): t => Symbolic(SymbolicDist.Float.make(f)) @@ -81,10 +81,18 @@ let toPointSet = ( } } -let toSparkline = (t: t, ~sampleCount: int, ~buckets: int=20, unit): result => +/* + PointSetDist.toSparkline calls "downsampleEquallyOverX", which downsamples it to n=bucketCount. + It first needs a pointSetDist, so we convert to a pointSetDist. In this process we want the + xyPointLength to be a bit longer than the eventual toSparkline downsampling. I chose 3 + fairly arbitrarily. + */ +let toSparkline = (t: t, ~sampleCount: int, ~bucketCount: int=20, unit): result => t - ->toPointSet(~xSelection=#Linear, ~xyPointLength=buckets, ~sampleCount, ()) - ->E.R.bind(r => r->PointSetDist.toSparkline->E.R2.errMap(r => Error(GenericDist_Types.Other(r)))) + ->toPointSet(~xSelection=#Linear, ~xyPointLength=bucketCount * 3, ~sampleCount, ()) + ->E.R.bind(r => + r->PointSetDist.toSparkline(bucketCount)->E.R2.errMap(r => Error(GenericDist_Types.Other(r))) + ) module Truncate = { let trySymbolicSimplification = (leftCutoff, rightCutoff, t: t): option => diff --git a/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.resi b/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.resi index 5fa24de9..adf3b9d4 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.resi +++ b/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist.resi @@ -26,7 +26,7 @@ let toPointSet: ( ~xSelection: GenericDist_Types.Operation.pointsetXSelection=?, unit, ) => result -let toSparkline: (t, ~sampleCount: int, ~buckets: int=?, unit) => result +let toSparkline: (t, ~sampleCount: int, ~bucketCount: int=?, unit) => result let truncate: ( t, diff --git a/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist_Types.res b/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist_Types.res index 0eb64bd7..b3aeddac 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist_Types.res +++ b/packages/squiggle-lang/src/rescript/Distributions/GenericDist/GenericDist_Types.res @@ -3,6 +3,7 @@ type genericDist = | SampleSet(SampleSet.t) | Symbolic(SymbolicDistTypes.symbolicDist) +@genType type error = | NotYetImplemented | Unreachable @@ -52,9 +53,9 @@ module Operation = { type toFloatArray = Sample(int) - type toString = - | ToString - | ToSparkline(int) + type toString = + | ToString + | ToSparkline(int) type fromDist = | ToFloat(toFloat) @@ -66,6 +67,7 @@ module Operation = { | FromDist(fromDist) | FromFloat(fromDist) + @genType type genericFunctionCallInfo = | FromDist(fromDist, genericDist) | FromFloat(fromDist, float) @@ -95,3 +97,79 @@ module Operation = { | Mixture(_) => `mixture` } } + +/* +It can be a pain to write out the genericFunctionCallInfo. The constructors help with this. +This code only covers some of genericFunctionCallInfo: many arguments could be called with either a +float or a distribution. The "UsingDists" module assumes that everything is a distribution. +This is a tradeoff of some generality in order to get a bit more simplicity. +I could see having a longer interface in the future, but it could be messy. +Like, algebraicAddDistFloat vs. algebraicAddDistDist +*/ +module Constructors = { + type t = Operation.genericFunctionCallInfo + + module UsingDists = { + @genType + let mean = (dist): t => FromDist(ToFloat(#Mean), dist) + let sample = (dist): t => FromDist(ToFloat(#Sample), dist) + let cdf = (dist, x): t => FromDist(ToFloat(#Cdf(x)), dist) + let inv = (dist, x): t => FromDist(ToFloat(#Inv(x)), dist) + let pdf = (dist, x): t => FromDist(ToFloat(#Pdf(x)), dist) + let normalize = (dist): t => FromDist(ToDist(Normalize), dist) + let toPointSet = (dist): t => FromDist(ToDist(ToPointSet), dist) + let toSampleSet = (dist, r): t => FromDist(ToDist(ToSampleSet(r)), dist) + let truncate = (dist, left, right): t => FromDist(ToDist(Truncate(left, right)), dist) + let inspect = (dist): t => FromDist(ToDist(Inspect), dist) + let toString = (dist): t => FromDist(ToString(ToString), dist) + let toSparkline = (dist, n): t => FromDist(ToString(ToSparkline(n)), dist) + let algebraicAdd = (dist1, dist2: genericDist): t => FromDist( + ToDistCombination(Algebraic, #Add, #Dist(dist2)), + dist1, + ) + let algebraicMultiply = (dist1, dist2): t => FromDist( + ToDistCombination(Algebraic, #Multiply, #Dist(dist2)), + dist1, + ) + let algebraicDivide = (dist1, dist2): t => FromDist( + ToDistCombination(Algebraic, #Divide, #Dist(dist2)), + dist1, + ) + let algebraicSubtract = (dist1, dist2): t => FromDist( + ToDistCombination(Algebraic, #Subtract, #Dist(dist2)), + dist1, + ) + let algebraicLogarithm = (dist1, dist2): t => FromDist( + ToDistCombination(Algebraic, #Logarithm, #Dist(dist2)), + dist1, + ) + let algebraicExponentiate = (dist1, dist2): t => FromDist( + ToDistCombination(Algebraic, #Exponentiate, #Dist(dist2)), + dist1, + ) + let pointwiseAdd = (dist1, dist2): t => FromDist( + ToDistCombination(Pointwise, #Add, #Dist(dist2)), + dist1, + ) + let pointwiseMultiply = (dist1, dist2): t => FromDist( + ToDistCombination(Pointwise, #Multiply, #Dist(dist2)), + dist1, + ) + let pointwiseDivide = (dist1, dist2): t => FromDist( + ToDistCombination(Pointwise, #Divide, #Dist(dist2)), + dist1, + ) + let pointwiseSubtract = (dist1, dist2): t => FromDist( + ToDistCombination(Pointwise, #Subtract, #Dist(dist2)), + dist1, + ) + let pointwiseLogarithm = (dist1, dist2): t => FromDist( + ToDistCombination(Pointwise, #Logarithm, #Dist(dist2)), + dist1, + ) + let pointwiseExponentiate = (dist1, dist2): t => FromDist( + ToDistCombination(Pointwise, #Exponentiate, #Dist(dist2)), + dist1, + ) + } +} diff --git a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Continuous.res b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Continuous.res index f01457b7..aa27fb62 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Continuous.res +++ b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/Continuous.res @@ -249,6 +249,9 @@ module T = Dist({ ) }) +let downsampleEquallyOverX = (length, t): t => + t |> shapeMap(XYShape.XsConversion.proportionEquallyOverX(length)) + /* This simply creates multiple copies of the continuous distribution, scaled and shifted according to each discrete data point, and then adds them all together. */ let combineAlgebraicallyWithDiscrete = ( diff --git a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetDist.res b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetDist.res index 8224f4cb..f3f3c20c 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetDist.res +++ b/packages/squiggle-lang/src/rescript/Distributions/PointSetDist/PointSetDist.res @@ -203,7 +203,8 @@ let operate = (distToFloatOp: Operation.distToFloatOperation, s): float => | #Mean => T.mean(s) } -let toSparkline = (t: t) => +let toSparkline = (t: t, bucketCount) => T.toContinuous(t) + ->E.O2.fmap(Continuous.downsampleEquallyOverX(bucketCount)) ->E.O2.toResult("toContinous Error: Could not convert into continuous distribution") ->E.R2.fmap(r => Continuous.getShape(r).ys->Sparklines.create()) \ No newline at end of file diff --git a/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSet.res b/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSet.res index eebb9efc..f8bce6f6 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSet.res +++ b/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSet.res @@ -1,3 +1,4 @@ +@genType type t = array // TODO: Refactor to raise correct error when not enough samples @@ -59,6 +60,7 @@ module Internals = { : { let _ = Js.Array.push(element, continuous) } + () }) (continuous, discrete) @@ -142,4 +144,26 @@ let toPointSetDist = ( } samplesParse -} \ No newline at end of file +} + +//Randomly get one sample from the distribution +let sample = (t: t): float => { + let i = E.Int.random(~min=0, ~max=E.A.length(t) - 1) + E.A.unsafe_get(t, i) +} + +/* +If asked for a length of samples shorter or equal the length of the distribution, +return this first n samples of this distribution. +Else, return n random samples of the distribution. +The former helps in cases where multiple distributions are correlated. +However, if n > length(t), then there's no clear right answer, so we just randomly +sample everything. +*/ +let sampleN = (t: t, n) => { + if n <= E.A.length(t) { + E.A.slice(t, ~offset=0, ~len=n) + } else { + Belt.Array.makeBy(n, _ => sample(t)) + } +} diff --git a/packages/squiggle-lang/src/rescript/TypescriptInterface.res b/packages/squiggle-lang/src/rescript/TypescriptInterface.res new file mode 100644 index 00000000..fb30ea24 --- /dev/null +++ b/packages/squiggle-lang/src/rescript/TypescriptInterface.res @@ -0,0 +1,24 @@ +/* +This is meant as a file to contain @genType declarations as needed for Typescript. +I would ultimately want to have all @genType declarations here, vs. other files, but +@genType doesn't play as nicely with renaming Modules and functions as +would be preferable. + +The below few seem to work fine. In the future there's definitely more work to do here. +*/ + +@genType +type env = DistributionOperation.env + +@genType +type genericDist = GenericDist_Types.genericDist + +@genType +type error = GenericDist_Types.error + +@genType +type resultDist = result +@genType +type resultFloat = result +@genType +type resultString = result diff --git a/packages/squiggle-lang/src/rescript/Utility/E.res b/packages/squiggle-lang/src/rescript/Utility/E.res index 01dbde7d..16d87896 100644 --- a/packages/squiggle-lang/src/rescript/Utility/E.res +++ b/packages/squiggle-lang/src/rescript/Utility/E.res @@ -24,6 +24,7 @@ module FloatFloatMap = { module Int = { let max = (i1: int, i2: int) => i1 > i2 ? i1 : i2 + let random = (~min, ~max) => Js.Math.random_int(min, max) } /* Utils */ module U = { @@ -277,6 +278,7 @@ module A = { let fold_right = Array.fold_right let concatMany = Belt.Array.concatMany let keepMap = Belt.Array.keepMap + let slice = Belt.Array.slice let init = Array.init let reduce = Belt.Array.reduce let reducei = Belt.Array.reduceWithIndex