Merge pull request #210 from QURIresearch/GenericOperation-operation-functions

Turn GenericOperation into a bunch of manual functions for distributions
This commit is contained in:
Ozzie Gooen 2022-04-09 07:30:41 -04:00 committed by GitHub
commit 344c989cb5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 559 additions and 59 deletions

View File

@ -1,34 +1,86 @@
import { run } from '../src/js/index'; import { run, GenericDist, resultMap } from "../src/js/index";
let testRun = (x: string) => { let testRun = (x: string) => {
let result = run(x) let result = run(x);
if(result.tag == 'Ok'){ if (result.tag == "Ok") {
return { tag: 'Ok', value: result.value.exports } return { tag: "Ok", value: result.value.exports };
} else {
return result;
} }
else { };
return result
}
}
describe("Simple calculations and results", () => { describe("Simple calculations and results", () => {
test("mean(normal(5,2))", () => { test("mean(normal(5,2))", () => {
expect(testRun("mean(normal(5,2))")).toEqual({ tag: 'Ok', value: [ { NAME: 'Float', VAL: 5 } ] }) expect(testRun("mean(normal(5,2))")).toEqual({
}) tag: "Ok",
test("10+10", () => { value: [{ NAME: "Float", VAL: 5 }],
let foo = testRun("10 + 10") });
expect(foo).toEqual({ tag: 'Ok', value: [ { NAME: 'Float', VAL: 20 } ] }) });
}) test("10+10", () => {
}) let foo = testRun("10 + 10");
expect(foo).toEqual({ tag: "Ok", value: [{ NAME: "Float", VAL: 20 }] });
});
});
describe("Log function", () => { describe("Log function", () => {
test("log(1) = 0", () => { test("log(1) = 0", () => {
let foo = testRun("log(1)") let foo = testRun("log(1)");
expect(foo).toEqual({ tag: 'Ok', value: [ { NAME: 'Float', VAL: 0} ]}) expect(foo).toEqual({ tag: "Ok", value: [{ NAME: "Float", VAL: 0 }] });
}) });
}) });
describe("Multimodal too many weights error", () => { describe("Multimodal too many weights error", () => {
test("mm(0,0,[0,0,0])", () => { test("mm(0,0,[0,0,0])", () => {
let foo = testRun("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" }) 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("▁▂▅██▅▅▅▆▇█▆▅▃▃▂▂▁▁▁");
});
}); });

View File

@ -33,7 +33,7 @@
"gentypeconfig": { "gentypeconfig": {
"language": "typescript", "language": "typescript",
"module": "commonjs", "module": "commonjs",
"shims": {}, "shims": {"Js": "Js"},
"debug": { "debug": {
"all": false, "all": false,
"basic": false "basic": false

View File

@ -1,17 +1,223 @@
import {runAll} from '../rescript/ProgramEvaluator.gen'; import { runAll } from "../rescript/ProgramEvaluator.gen";
import type { Inputs_SamplingInputs_t as SamplingInputs, exportEnv, exportType, exportDistribution} from '../rescript/ProgramEvaluator.gen'; import type {
export type { SamplingInputs, exportEnv, exportDistribution } Inputs_SamplingInputs_t as SamplingInputs,
export type {t as DistPlus} from '../rescript/OldInterpreter/DistPlus.gen'; 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 = { export let defaultSamplingInputs: SamplingInputs = {
sampleCount : 10000, sampleCount: 10000,
outputXYPoints : 10000, outputXYPoints: 10000,
pointDistLength : 1000 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 } //This is clearly not fully typed. I think later we should use a functional library to
| { tag: "Error"; value: string } { // provide a better Either type and corresponding functions.
let si : SamplingInputs = samplingInputs ? samplingInputs : defaultSamplingInputs type result =
let env : exportEnv = environment ? environment : [] | {
return runAll(squiggleString, si, env) 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)
);
}
} }

View File

@ -114,8 +114,8 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => {
->E.R2.fmap(r => Float(r)) ->E.R2.fmap(r => Float(r))
->OutputLocal.fromResult ->OutputLocal.fromResult
| ToString(ToString) => dist->GenericDist.toString->String | ToString(ToString) => dist->GenericDist.toString->String
| ToString(ToSparkline(buckets)) => | ToString(ToSparkline(bucketCount)) =>
GenericDist.toSparkline(dist, ~sampleCount, ~buckets, ()) GenericDist.toSparkline(dist, ~sampleCount, ~bucketCount, ())
->E.R2.fmap(r => String(r)) ->E.R2.fmap(r => String(r))
->OutputLocal.fromResult ->OutputLocal.fromResult
| ToDist(Inspect) => { | ToDist(Inspect) => {
@ -185,3 +185,44 @@ module Output = {
newFnCall->E.R2.fmap(run(~env))->OutputLocal.fromResult 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
}

View File

@ -1,19 +1,24 @@
@genType
type env = { type env = {
sampleCount: int, sampleCount: int,
xyPointLength: int, xyPointLength: int,
} }
open GenericDist_Types
@genType
type outputType = type outputType =
| Dist(GenericDist_Types.genericDist) | Dist(genericDist)
| Float(float) | Float(float)
| String(string) | String(string)
| GenDistError(GenericDist_Types.error) | GenDistError(error)
@genType
let run: (~env: env, GenericDist_Types.Operation.genericFunctionCallInfo) => outputType let run: (~env: env, GenericDist_Types.Operation.genericFunctionCallInfo) => outputType
let runFromDist: ( let runFromDist: (
~env: env, ~env: env,
~functionCallInfo: GenericDist_Types.Operation.fromDist, ~functionCallInfo: GenericDist_Types.Operation.fromDist,
GenericDist_Types.genericDist, genericDist,
) => outputType ) => outputType
let runFromFloat: ( let runFromFloat: (
~env: env, ~env: env,
@ -23,12 +28,68 @@ let runFromFloat: (
module Output: { module Output: {
type t = outputType type t = outputType
let toDist: t => option<GenericDist_Types.genericDist> let toDist: t => option<genericDist>
let toDistR: t => result<GenericDist_Types.genericDist, GenericDist_Types.error> let toDistR: t => result<genericDist, error>
let toFloat: t => option<float> let toFloat: t => option<float>
let toFloatR: t => result<float, GenericDist_Types.error> let toFloatR: t => result<float, error>
let toString: t => option<string> let toString: t => option<string>
let toStringR: t => result<string, GenericDist_Types.error> let toStringR: t => result<string, error>
let toError: t => option<GenericDist_Types.error> let toError: t => option<error>
let fmap: (~env: env, t, GenericDist_Types.Operation.singleParamaterFunction) => t let fmap: (~env: env, t, GenericDist_Types.Operation.singleParamaterFunction) => t
} }
module Constructors: {
@genType
let mean: (~env: env, genericDist) => result<float, error>
@genType
let sample: (~env: env, genericDist) => result<float, error>
@genType
let cdf: (~env: env, genericDist, float) => result<float, error>
@genType
let inv: (~env: env, genericDist, float) => result<float, error>
@genType
let pdf: (~env: env, genericDist, float) => result<float, error>
@genType
let normalize: (~env: env, genericDist) => result<genericDist, error>
@genType
let toPointSet: (~env: env, genericDist) => result<genericDist, error>
@genType
let toSampleSet: (~env: env, genericDist, int) => result<genericDist, error>
@genType
let truncate: (
~env: env,
genericDist,
option<float>,
option<float>,
) => result<genericDist, error>
@genType
let inspect: (~env: env, genericDist) => result<genericDist, error>
@genType
let toString: (~env: env, genericDist) => result<string, error>
@genType
let toSparkline: (~env: env, genericDist, int) => result<string, error>
@genType
let algebraicAdd: (~env: env, genericDist, genericDist) => result<genericDist, error>
@genType
let algebraicMultiply: (~env: env, genericDist, genericDist) => result<genericDist, error>
@genType
let algebraicDivide: (~env: env, genericDist, genericDist) => result<genericDist, error>
@genType
let algebraicSubtract: (~env: env, genericDist, genericDist) => result<genericDist, error>
@genType
let algebraicLogarithm: (~env: env, genericDist, genericDist) => result<genericDist, error>
@genType
let algebraicExponentiate: (~env: env, genericDist, genericDist) => result<genericDist, error>
@genType
let pointwiseAdd: (~env: env, genericDist, genericDist) => result<genericDist, error>
@genType
let pointwiseMultiply: (~env: env, genericDist, genericDist) => result<genericDist, error>
@genType
let pointwiseDivide: (~env: env, genericDist, genericDist) => result<genericDist, error>
@genType
let pointwiseSubtract: (~env: env, genericDist, genericDist) => result<genericDist, error>
@genType
let pointwiseLogarithm: (~env: env, genericDist, genericDist) => result<genericDist, error>
@genType
let pointwiseExponentiate: (~env: env, genericDist, genericDist) => result<genericDist, error>
}

View File

@ -10,7 +10,7 @@ let sampleN = (t: t, n) =>
switch t { switch t {
| PointSet(r) => Ok(PointSetDist.sampleNRendered(n, r)) | PointSet(r) => Ok(PointSetDist.sampleNRendered(n, r))
| Symbolic(r) => Ok(SymbolicDist.T.sampleN(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)) 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<string, error> => /*
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<string, error> =>
t t
->toPointSet(~xSelection=#Linear, ~xyPointLength=buckets, ~sampleCount, ()) ->toPointSet(~xSelection=#Linear, ~xyPointLength=bucketCount * 3, ~sampleCount, ())
->E.R.bind(r => r->PointSetDist.toSparkline->E.R2.errMap(r => Error(GenericDist_Types.Other(r)))) ->E.R.bind(r =>
r->PointSetDist.toSparkline(bucketCount)->E.R2.errMap(r => Error(GenericDist_Types.Other(r)))
)
module Truncate = { module Truncate = {
let trySymbolicSimplification = (leftCutoff, rightCutoff, t: t): option<t> => let trySymbolicSimplification = (leftCutoff, rightCutoff, t: t): option<t> =>

View File

@ -26,7 +26,7 @@ let toPointSet: (
~xSelection: GenericDist_Types.Operation.pointsetXSelection=?, ~xSelection: GenericDist_Types.Operation.pointsetXSelection=?,
unit, unit,
) => result<PointSetTypes.pointSetDist, error> ) => result<PointSetTypes.pointSetDist, error>
let toSparkline: (t, ~sampleCount: int, ~buckets: int=?, unit) => result<string, error> let toSparkline: (t, ~sampleCount: int, ~bucketCount: int=?, unit) => result<string, error>
let truncate: ( let truncate: (
t, t,

View File

@ -3,6 +3,7 @@ type genericDist =
| SampleSet(SampleSet.t) | SampleSet(SampleSet.t)
| Symbolic(SymbolicDistTypes.symbolicDist) | Symbolic(SymbolicDistTypes.symbolicDist)
@genType
type error = type error =
| NotYetImplemented | NotYetImplemented
| Unreachable | Unreachable
@ -53,8 +54,8 @@ module Operation = {
type toFloatArray = Sample(int) type toFloatArray = Sample(int)
type toString = type toString =
| ToString | ToString
| ToSparkline(int) | ToSparkline(int)
type fromDist = type fromDist =
| ToFloat(toFloat) | ToFloat(toFloat)
@ -66,6 +67,7 @@ module Operation = {
| FromDist(fromDist) | FromDist(fromDist)
| FromFloat(fromDist) | FromFloat(fromDist)
@genType
type genericFunctionCallInfo = type genericFunctionCallInfo =
| FromDist(fromDist, genericDist) | FromDist(fromDist, genericDist)
| FromFloat(fromDist, float) | FromFloat(fromDist, float)
@ -95,3 +97,79 @@ module Operation = {
| Mixture(_) => `mixture` | 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,
)
}
}

View File

@ -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 /* This simply creates multiple copies of the continuous distribution, scaled and shifted according to
each discrete data point, and then adds them all together. */ each discrete data point, and then adds them all together. */
let combineAlgebraicallyWithDiscrete = ( let combineAlgebraicallyWithDiscrete = (

View File

@ -203,7 +203,8 @@ let operate = (distToFloatOp: Operation.distToFloatOperation, s): float =>
| #Mean => T.mean(s) | #Mean => T.mean(s)
} }
let toSparkline = (t: t) => let toSparkline = (t: t, bucketCount) =>
T.toContinuous(t) T.toContinuous(t)
->E.O2.fmap(Continuous.downsampleEquallyOverX(bucketCount))
->E.O2.toResult("toContinous Error: Could not convert into continuous distribution") ->E.O2.toResult("toContinous Error: Could not convert into continuous distribution")
->E.R2.fmap(r => Continuous.getShape(r).ys->Sparklines.create()) ->E.R2.fmap(r => Continuous.getShape(r).ys->Sparklines.create())

View File

@ -1,3 +1,4 @@
@genType
type t = array<float> type t = array<float>
// TODO: Refactor to raise correct error when not enough samples // TODO: Refactor to raise correct error when not enough samples
@ -59,6 +60,7 @@ module Internals = {
: { : {
let _ = Js.Array.push(element, continuous) let _ = Js.Array.push(element, continuous)
} }
() ()
}) })
(continuous, discrete) (continuous, discrete)
@ -143,3 +145,25 @@ let toPointSetDist = (
samplesParse samplesParse
} }
//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))
}
}

View File

@ -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<genericDist, error>
@genType
type resultFloat = result<float, error>
@genType
type resultString = result<string, error>

View File

@ -24,6 +24,7 @@ module FloatFloatMap = {
module Int = { module Int = {
let max = (i1: int, i2: int) => i1 > i2 ? i1 : i2 let max = (i1: int, i2: int) => i1 > i2 ? i1 : i2
let random = (~min, ~max) => Js.Math.random_int(min, max)
} }
/* Utils */ /* Utils */
module U = { module U = {
@ -277,6 +278,7 @@ module A = {
let fold_right = Array.fold_right let fold_right = Array.fold_right
let concatMany = Belt.Array.concatMany let concatMany = Belt.Array.concatMany
let keepMap = Belt.Array.keepMap let keepMap = Belt.Array.keepMap
let slice = Belt.Array.slice
let init = Array.init let init = Array.init
let reduce = Belt.Array.reduce let reduce = Belt.Array.reduce
let reducei = Belt.Array.reduceWithIndex let reducei = Belt.Array.reduceWithIndex