Merge pull request #211 from QURIresearch/Refactor-april
SampleSetDist refactors
This commit is contained in:
commit
66c1081218
|
@ -4,10 +4,10 @@ open Expect
|
|||
describe("Bandwidth", () => {
|
||||
test("nrd0()", () => {
|
||||
let data = [1., 4., 3., 2.]
|
||||
expect(Bandwidth.nrd0(data)) -> toEqual(0.7625801874014622)
|
||||
expect(SampleSetDist_Bandwidth.nrd0(data)) -> toEqual(0.7625801874014622)
|
||||
})
|
||||
test("nrd()", () => {
|
||||
let data = [1., 4., 3., 2.]
|
||||
expect(Bandwidth.nrd(data)) -> toEqual(0.8981499984950554)
|
||||
expect(SampleSetDist_Bandwidth.nrd(data)) -> toEqual(0.8981499984950554)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -90,14 +90,6 @@ describe("toPointSet", () => {
|
|||
expect(result)->toBeSoCloseTo(5.0, ~digits=0)
|
||||
})
|
||||
|
||||
test("on sample set distribution with under 4 points", () => {
|
||||
let result =
|
||||
run(FromDist(ToDist(ToPointSet), SampleSet([0.0, 1.0, 2.0, 3.0])))->outputMap(
|
||||
FromDist(ToFloat(#Mean)),
|
||||
)
|
||||
expect(result)->toEqual(GenDistError(Other("Converting sampleSet to pointSet failed")))
|
||||
})
|
||||
|
||||
test("on sample set", () => {
|
||||
let result =
|
||||
run(FromDist(ToDist(ToPointSet), normalDist5))
|
||||
|
|
|
@ -4,12 +4,12 @@ open TestHelpers
|
|||
describe("Continuous and discrete splits", () => {
|
||||
makeTest(
|
||||
"splits (1)",
|
||||
SampleSet.Internals.T.splitContinuousAndDiscrete([1.432, 1.33455, 2.0]),
|
||||
SampleSetDist_ToPointSet.Internals.T.splitContinuousAndDiscrete([1.432, 1.33455, 2.0]),
|
||||
([1.432, 1.33455, 2.0], E.FloatFloatMap.empty()),
|
||||
)
|
||||
makeTest(
|
||||
"splits (2)",
|
||||
SampleSet.Internals.T.splitContinuousAndDiscrete([
|
||||
SampleSetDist_ToPointSet.Internals.T.splitContinuousAndDiscrete([
|
||||
1.432,
|
||||
1.33455,
|
||||
2.0,
|
||||
|
@ -26,13 +26,13 @@ describe("Continuous and discrete splits", () => {
|
|||
E.A.concatMany([sorted, sorted, sorted, sorted]) |> Belt.SortArray.stableSortBy(_, compare)
|
||||
}
|
||||
|
||||
let (_, discrete1) = SampleSet.Internals.T.splitContinuousAndDiscrete(
|
||||
let (_, discrete1) = SampleSetDist_ToPointSet.Internals.T.splitContinuousAndDiscrete(
|
||||
makeDuplicatedArray(10),
|
||||
)
|
||||
let toArr1 = discrete1 |> E.FloatFloatMap.toArray
|
||||
makeTest("splitMedium at count=10", toArr1 |> Belt.Array.length, 10)
|
||||
|
||||
let (_c, discrete2) = SampleSet.Internals.T.splitContinuousAndDiscrete(
|
||||
let (_c, discrete2) = SampleSetDist_ToPointSet.Internals.T.splitContinuousAndDiscrete(
|
||||
makeDuplicatedArray(500),
|
||||
)
|
||||
let toArr2 = discrete2 |> E.FloatFloatMap.toArray
|
|
@ -1,4 +1,4 @@
|
|||
import { run, GenericDist, resultMap } from "../src/js/index";
|
||||
import { run, GenericDist, resultMap, makeSampleSetDist } from "../src/js/index";
|
||||
|
||||
let testRun = (x: string) => {
|
||||
let result = run(x);
|
||||
|
@ -41,6 +41,7 @@ describe("Multimodal too many weights error", () => {
|
|||
describe("GenericDist", () => {
|
||||
|
||||
//It's important that sampleCount is less than 9. If it's more, than that will create randomness
|
||||
//Also, note, the value should be created using makeSampleSetDist() later on.
|
||||
let env = { sampleCount: 8, xyPointLength: 100 };
|
||||
let dist = new GenericDist(
|
||||
{ tag: "SampleSet", value: [3, 4, 5, 6, 6, 7, 10, 15, 30] },
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
resultFloat,
|
||||
resultString,
|
||||
} from "../rescript/TypescriptInterface.gen";
|
||||
export {makeSampleSetDist} from "../rescript/TypescriptInterface.gen";
|
||||
import {
|
||||
Constructors_mean,
|
||||
Constructors_sample,
|
||||
|
@ -32,13 +33,13 @@ import {
|
|||
Constructors_algebraicDivide,
|
||||
Constructors_algebraicSubtract,
|
||||
Constructors_algebraicLogarithm,
|
||||
Constructors_algebraicExponentiate,
|
||||
Constructors_algebraicPower,
|
||||
Constructors_pointwiseAdd,
|
||||
Constructors_pointwiseMultiply,
|
||||
Constructors_pointwiseDivide,
|
||||
Constructors_pointwiseSubtract,
|
||||
Constructors_pointwiseLogarithm,
|
||||
Constructors_pointwiseExponentiate,
|
||||
Constructors_pointwisePower,
|
||||
} from "../rescript/Distributions/DistributionOperation/DistributionOperation.gen";
|
||||
|
||||
export let defaultSamplingInputs: SamplingInputs = {
|
||||
|
@ -79,6 +80,10 @@ export function resultMap(r: result, mapFn: any): result {
|
|||
}
|
||||
}
|
||||
|
||||
export function resultExn(r: result): any {
|
||||
r.value
|
||||
}
|
||||
|
||||
export class GenericDist {
|
||||
t: genericDist;
|
||||
env: env;
|
||||
|
@ -179,9 +184,9 @@ export class GenericDist {
|
|||
);
|
||||
}
|
||||
|
||||
algebraicExponentiate(d2: GenericDist) {
|
||||
algebraicPower(d2: GenericDist) {
|
||||
return this.mapResultDist(
|
||||
Constructors_algebraicExponentiate({ env: this.env }, this.t, d2.t)
|
||||
Constructors_algebraicPower({ env: this.env }, this.t, d2.t)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -215,9 +220,9 @@ export class GenericDist {
|
|||
);
|
||||
}
|
||||
|
||||
pointwiseExponentiate(d2: GenericDist) {
|
||||
pointwisePower(d2: GenericDist) {
|
||||
return this.mapResultDist(
|
||||
Constructors_pointwiseExponentiate({ env: this.env }, this.t, d2.t)
|
||||
Constructors_pointwisePower({ env: this.env }, this.t, d2.t)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -128,7 +128,10 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => {
|
|||
->E.R2.fmap(r => Dist(r))
|
||||
->OutputLocal.fromResult
|
||||
| ToDist(ToSampleSet(n)) =>
|
||||
dist->GenericDist.sampleN(n)->E.R2.fmap(r => Dist(SampleSet(r)))->OutputLocal.fromResult
|
||||
dist
|
||||
->GenericDist.toSampleSetDist(n)
|
||||
->E.R2.fmap(r => Dist(SampleSet(r)))
|
||||
->OutputLocal.fromResult
|
||||
| ToDist(ToPointSet) =>
|
||||
dist
|
||||
->GenericDist.toPointSet(~xyPointLength, ~sampleCount, ())
|
||||
|
@ -204,7 +207,8 @@ module Constructors = {
|
|||
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 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
|
||||
|
@ -213,8 +217,7 @@ module Constructors = {
|
|||
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 algebraicPower = (~env, dist1, dist2) => C.algebraicPower(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
|
||||
|
@ -223,6 +226,5 @@ module Constructors = {
|
|||
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
|
||||
let pointwisePower = (~env, dist1, dist2) => C.pointwisePower(dist1, dist2)->run(~env)->toDistR
|
||||
}
|
||||
|
|
|
@ -79,7 +79,7 @@ module Constructors: {
|
|||
@genType
|
||||
let algebraicLogarithm: (~env: env, genericDist, genericDist) => result<genericDist, error>
|
||||
@genType
|
||||
let algebraicExponentiate: (~env: env, genericDist, genericDist) => result<genericDist, error>
|
||||
let algebraicPower: (~env: env, genericDist, genericDist) => result<genericDist, error>
|
||||
@genType
|
||||
let pointwiseAdd: (~env: env, genericDist, genericDist) => result<genericDist, error>
|
||||
@genType
|
||||
|
@ -91,5 +91,5 @@ module Constructors: {
|
|||
@genType
|
||||
let pointwiseLogarithm: (~env: env, genericDist, genericDist) => result<genericDist, error>
|
||||
@genType
|
||||
let pointwiseExponentiate: (~env: env, genericDist, genericDist) => result<genericDist, error>
|
||||
let pointwisePower: (~env: env, genericDist, genericDist) => result<genericDist, error>
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ module Operation = {
|
|||
| #Multiply
|
||||
| #Subtract
|
||||
| #Divide
|
||||
| #Exponentiate
|
||||
| #Power
|
||||
| #Logarithm
|
||||
]
|
||||
|
||||
|
@ -28,7 +28,7 @@ module Operation = {
|
|||
| #Add => \"+."
|
||||
| #Multiply => \"*."
|
||||
| #Subtract => \"-."
|
||||
| #Exponentiate => \"**"
|
||||
| #Power => \"**"
|
||||
| #Divide => \"/."
|
||||
| #Logarithm => (a, b) => log(a) /. log(b)
|
||||
}
|
||||
|
|
|
@ -2,17 +2,20 @@
|
|||
type t = GenericDist_Types.genericDist
|
||||
type error = GenericDist_Types.error
|
||||
type toPointSetFn = t => result<PointSetTypes.pointSetDist, error>
|
||||
type toSampleSetFn = t => result<array<float>, error>
|
||||
type toSampleSetFn = t => result<SampleSetDist.t, error>
|
||||
type scaleMultiplyFn = (t, float) => result<t, error>
|
||||
type pointwiseAddFn = (t, t) => result<t, error>
|
||||
|
||||
let sampleN = (t: t, n) =>
|
||||
switch t {
|
||||
| PointSet(r) => Ok(PointSetDist.sampleNRendered(n, r))
|
||||
| Symbolic(r) => Ok(SymbolicDist.T.sampleN(n, r))
|
||||
| SampleSet(r) => Ok(SampleSet.sampleN(r, n))
|
||||
| PointSet(r) => PointSetDist.sampleNRendered(n, r)
|
||||
| Symbolic(r) => SymbolicDist.T.sampleN(n, r)
|
||||
| SampleSet(r) => SampleSetDist.sampleN(r, n)
|
||||
}
|
||||
|
||||
let toSampleSetDist = (t: t, n) =>
|
||||
SampleSetDist.make(sampleN(t, n))->GenericDist_Types.Error.resultStringToResultError
|
||||
|
||||
let fromFloat = (f: float): t => Symbolic(SymbolicDist.Float.make(f))
|
||||
|
||||
let toString = (t: t) =>
|
||||
|
@ -62,22 +65,16 @@ let toPointSet = (
|
|||
switch (t: t) {
|
||||
| PointSet(pointSet) => Ok(pointSet)
|
||||
| Symbolic(r) => Ok(SymbolicDist.T.toPointSetDist(~xSelection, xyPointLength, r))
|
||||
| SampleSet(r) => {
|
||||
let response = SampleSet.toPointSetDist(
|
||||
~samples=r,
|
||||
~samplingInputs={
|
||||
sampleCount: sampleCount,
|
||||
outputXYPoints: xyPointLength,
|
||||
pointSetDistLength: xyPointLength,
|
||||
kernelWidth: None,
|
||||
},
|
||||
(),
|
||||
).pointSetDist
|
||||
switch response {
|
||||
| Some(r) => Ok(r)
|
||||
| None => Error(Other("Converting sampleSet to pointSet failed"))
|
||||
}
|
||||
}
|
||||
| SampleSet(r) =>
|
||||
SampleSetDist.toPointSetDist(
|
||||
~samples=r,
|
||||
~samplingInputs={
|
||||
sampleCount: sampleCount,
|
||||
outputXYPoints: xyPointLength,
|
||||
pointSetDistLength: xyPointLength,
|
||||
kernelWidth: None,
|
||||
},
|
||||
)->GenericDist_Types.Error.resultStringToResultError
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -91,7 +88,7 @@ let toSparkline = (t: t, ~sampleCount: int, ~bucketCount: int=20, unit): result<
|
|||
t
|
||||
->toPointSet(~xSelection=#Linear, ~xyPointLength=bucketCount * 3, ~sampleCount, ())
|
||||
->E.R.bind(r =>
|
||||
r->PointSetDist.toSparkline(bucketCount)->E.R2.errMap(r => Error(GenericDist_Types.Other(r)))
|
||||
r->PointSetDist.toSparkline(bucketCount)->GenericDist_Types.Error.resultStringToResultError
|
||||
)
|
||||
|
||||
module Truncate = {
|
||||
|
@ -166,10 +163,12 @@ module AlgebraicCombination = {
|
|||
t1: t,
|
||||
t2: t,
|
||||
) => {
|
||||
let arithmeticOperation = Operation.Algebraic.toFn(arithmeticOperation)
|
||||
E.R.merge(toSampleSet(t1), toSampleSet(t2))->E.R2.fmap(((a, b)) => {
|
||||
Belt.Array.zip(a, b)->E.A2.fmap(((a, b)) => arithmeticOperation(a, b))
|
||||
let fn = Operation.Algebraic.toFn(arithmeticOperation)
|
||||
E.R.merge(toSampleSet(t1), toSampleSet(t2))
|
||||
->E.R.bind(((t1, t2)) => {
|
||||
SampleSetDist.map2(~fn, ~t1, ~t2)->GenericDist_Types.Error.resultStringToResultError
|
||||
})
|
||||
->E.R2.fmap(r => GenericDist_Types.SampleSet(r))
|
||||
}
|
||||
|
||||
//I'm (Ozzie) really just guessing here, very little idea what's best
|
||||
|
@ -200,13 +199,7 @@ module AlgebraicCombination = {
|
|||
| Some(Error(e)) => Error(Other(e))
|
||||
| None =>
|
||||
switch chooseConvolutionOrMonteCarlo(t1, t2) {
|
||||
| #CalculateWithMonteCarlo =>
|
||||
runMonteCarlo(
|
||||
toSampleSetFn,
|
||||
arithmeticOperation,
|
||||
t1,
|
||||
t2,
|
||||
)->E.R2.fmap(r => GenericDist_Types.SampleSet(r))
|
||||
| #CalculateWithMonteCarlo => runMonteCarlo(toSampleSetFn, arithmeticOperation, t1, t2)
|
||||
| #CalculateWithConvolution =>
|
||||
runConvolution(
|
||||
toPointSetFn,
|
||||
|
@ -247,7 +240,7 @@ let pointwiseCombinationFloat = (
|
|||
): result<t, error> => {
|
||||
let m = switch arithmeticOperation {
|
||||
| #Add | #Subtract => Error(GenericDist_Types.DistributionVerticalShiftIsInvalid)
|
||||
| (#Multiply | #Divide | #Exponentiate | #Logarithm) as arithmeticOperation =>
|
||||
| (#Multiply | #Divide | #Power | #Logarithm) as arithmeticOperation =>
|
||||
toPointSetFn(t)->E.R2.fmap(t => {
|
||||
//TODO: Move to PointSet codebase
|
||||
let fn = (secondary, main) => Operation.Scale.toFn(arithmeticOperation, main, secondary)
|
||||
|
@ -272,7 +265,7 @@ let mixture = (
|
|||
~pointwiseAddFn: pointwiseAddFn,
|
||||
) => {
|
||||
if E.A.length(values) == 0 {
|
||||
Error(GenericDist_Types.Other("mixture must have at least 1 element"))
|
||||
Error(GenericDist_Types.Other("Mixture error: mixture must have at least 1 element"))
|
||||
} else {
|
||||
let totalWeight = values->E.A2.fmap(E.Tuple2.second)->E.A.Floats.sum
|
||||
let properlyWeightedValues =
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
type t = GenericDist_Types.genericDist
|
||||
type error = GenericDist_Types.error
|
||||
type toPointSetFn = t => result<PointSetTypes.pointSetDist, error>
|
||||
type toSampleSetFn = t => result<array<float>, error>
|
||||
type toSampleSetFn = t => result<SampleSetDist.t, error>
|
||||
type scaleMultiplyFn = (t, float) => result<t, error>
|
||||
type pointwiseAddFn = (t, t) => result<t, error>
|
||||
|
||||
let sampleN: (t, int) => result<array<float>, error>
|
||||
let sampleN: (t, int) => array<float>
|
||||
|
||||
let toSampleSetDist: (t, int) => Belt.Result.t<QuriSquiggleLang.SampleSetDist.t, error>
|
||||
|
||||
let fromFloat: float => t
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
type genericDist =
|
||||
| PointSet(PointSetTypes.pointSetDist)
|
||||
| SampleSet(SampleSet.t)
|
||||
| SampleSet(SampleSetDist.t)
|
||||
| Symbolic(SymbolicDistTypes.symbolicDist)
|
||||
|
||||
@genType
|
||||
|
@ -10,6 +10,15 @@ type error =
|
|||
| DistributionVerticalShiftIsInvalid
|
||||
| Other(string)
|
||||
|
||||
module Error = {
|
||||
type t = error
|
||||
|
||||
let fromString = (s: string): t => Other(s)
|
||||
|
||||
let resultStringToResultError: result<'a, string> => result<'a, error> = n =>
|
||||
n->E.R2.errMap(r => r->fromString->Error)
|
||||
}
|
||||
|
||||
module Operation = {
|
||||
type direction =
|
||||
| Algebraic
|
||||
|
@ -20,7 +29,7 @@ module Operation = {
|
|||
| #Multiply
|
||||
| #Subtract
|
||||
| #Divide
|
||||
| #Exponentiate
|
||||
| #Power
|
||||
| #Logarithm
|
||||
]
|
||||
|
||||
|
@ -29,7 +38,7 @@ module Operation = {
|
|||
| #Add => \"+."
|
||||
| #Multiply => \"*."
|
||||
| #Subtract => \"-."
|
||||
| #Exponentiate => \"**"
|
||||
| #Power => \"**"
|
||||
| #Divide => \"/."
|
||||
| #Logarithm => (a, b) => log(a) /. log(b)
|
||||
}
|
||||
|
@ -143,8 +152,8 @@ module Constructors = {
|
|||
ToDistCombination(Algebraic, #Logarithm, #Dist(dist2)),
|
||||
dist1,
|
||||
)
|
||||
let algebraicExponentiate = (dist1, dist2): t => FromDist(
|
||||
ToDistCombination(Algebraic, #Exponentiate, #Dist(dist2)),
|
||||
let algebraicPower = (dist1, dist2): t => FromDist(
|
||||
ToDistCombination(Algebraic, #Power, #Dist(dist2)),
|
||||
dist1,
|
||||
)
|
||||
let pointwiseAdd = (dist1, dist2): t => FromDist(
|
||||
|
@ -167,8 +176,8 @@ module Constructors = {
|
|||
ToDistCombination(Pointwise, #Logarithm, #Dist(dist2)),
|
||||
dist1,
|
||||
)
|
||||
let pointwiseExponentiate = (dist1, dist2): t => FromDist(
|
||||
ToDistCombination(Pointwise, #Exponentiate, #Dist(dist2)),
|
||||
let pointwisePower = (dist1, dist2): t => FromDist(
|
||||
ToDistCombination(Pointwise, #Power, #Dist(dist2)),
|
||||
dist1,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -114,7 +114,7 @@ let combineShapesContinuousContinuous = (
|
|||
| #Subtract => (m1, m2) => m1 -. m2
|
||||
| #Multiply => (m1, m2) => m1 *. m2
|
||||
| #Divide => (m1, mInv2) => m1 *. mInv2
|
||||
| #Exponentiate => (m1, mInv2) => m1 ** mInv2
|
||||
| #Power => (m1, mInv2) => m1 ** mInv2
|
||||
| #Logarithm => (m1, m2) => log(m1) /. log(m2)
|
||||
} // note: here, mInv2 = mean(1 / t2) ~= 1 / mean(t2)
|
||||
|
||||
|
@ -124,7 +124,7 @@ let combineShapesContinuousContinuous = (
|
|||
| #Add => (v1, v2, _, _) => v1 +. v2
|
||||
| #Subtract => (v1, v2, _, _) => v1 +. v2
|
||||
| #Multiply => (v1, v2, m1, m2) => v1 *. v2 +. v1 *. m2 ** 2. +. v2 *. m1 ** 2.
|
||||
| #Exponentiate => (v1, v2, m1, m2) => v1 *. v2 +. v1 *. m2 ** 2. +. v2 *. m1 ** 2.
|
||||
| #Power => (v1, v2, m1, m2) => v1 *. v2 +. v1 *. m2 ** 2. +. v2 *. m1 ** 2.
|
||||
| #Logarithm => (v1, v2, m1, m2) => v1 *. v2 +. v1 *. m2 ** 2. +. v2 *. m1 ** 2.
|
||||
| #Divide => (v1, vInv2, m1, mInv2) => v1 *. vInv2 +. v1 *. mInv2 ** 2. +. vInv2 *. m1 ** 2.
|
||||
}
|
||||
|
@ -233,7 +233,7 @@ let combineShapesContinuousDiscrete = (
|
|||
()
|
||||
}
|
||||
| #Multiply
|
||||
| #Exponentiate
|
||||
| #Power
|
||||
| #Logarithm
|
||||
| #Divide =>
|
||||
for j in 0 to t2n - 1 {
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
This is used as a smart constructor. The only way to create a SampleSetDist.t is to call
|
||||
this constructor.
|
||||
https://stackoverflow.com/questions/66909578/how-to-make-a-type-constructor-private-in-rescript-except-in-current-module
|
||||
*/
|
||||
module T: {
|
||||
//This really should be hidden (remove the array<float>). The reason it isn't is to act as an escape hatch in JS__Test.ts.
|
||||
//When we get a good functional library in TS, we could refactor that out.
|
||||
@genType
|
||||
type t = array<float>
|
||||
let make: array<float> => result<t, string>
|
||||
let get: t => array<float>
|
||||
} = {
|
||||
type t = array<float>
|
||||
let make = (a: array<float>) =>
|
||||
if E.A.length(a) > 5 {
|
||||
Ok(a)
|
||||
} else {
|
||||
Error("too small")
|
||||
}
|
||||
let get = (a: t) => a
|
||||
}
|
||||
|
||||
include T
|
||||
|
||||
let length = (t: t) => get(t)->E.A.length
|
||||
|
||||
/*
|
||||
TODO: Refactor to get a more precise estimate. Also, this code is just fairly messy, could use
|
||||
some refactoring.
|
||||
*/
|
||||
let toPointSetDist = (~samples: t, ~samplingInputs: SamplingInputs.samplingInputs): result<
|
||||
PointSetTypes.pointSetDist,
|
||||
string,
|
||||
> =>
|
||||
SampleSetDist_ToPointSet.toPointSetDist(
|
||||
~samples=get(samples),
|
||||
~samplingInputs,
|
||||
(),
|
||||
).pointSetDist->E.O2.toResult("Failed to convert to PointSetDist")
|
||||
|
||||
//Randomly get one sample from the distribution
|
||||
let sample = (t: t): float => {
|
||||
let i = E.Int.random(~min=0, ~max=E.A.length(get(t)) - 1)
|
||||
E.A.unsafe_get(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(get(t)) {
|
||||
E.A.slice(get(t), ~offset=0, ~len=n)
|
||||
} else {
|
||||
Belt.Array.makeBy(n, _ => sample(t))
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Figure out what to do if distributions are different lengths. ``zip`` is kind of inelegant for this.
|
||||
let map2 = (~fn: (float, float) => float, ~t1: t, ~t2: t) => {
|
||||
let samples = Belt.Array.zip(get(t1), get(t2))->E.A2.fmap(((a, b)) => fn(a, b))
|
||||
make(samples)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
//The math here was taken from https://github.com/jasondavies/science.js/blob/master/src/stats/bandwidth.js
|
||||
//The math here was taken from https://github.com/jasondavies/science.js/blob/master/src/stats/SampleSetDist_Bandwidth.js
|
||||
|
||||
let len = x => E.A.length(x) |> float_of_int
|
||||
|
|
@ -1,8 +1,3 @@
|
|||
@genType
|
||||
type t = array<float>
|
||||
|
||||
// TODO: Refactor to raise correct error when not enough samples
|
||||
|
||||
module Internals = {
|
||||
module Types = {
|
||||
type samplingStats = {
|
||||
|
@ -75,7 +70,7 @@ module Internals = {
|
|||
let formatUnitWidth = w => Jstat.max([w, 1.0]) |> int_of_float
|
||||
|
||||
let suggestedUnitWidth = (samples, outputXYPoints) => {
|
||||
let suggestedXWidth = Bandwidth.nrd0(samples)
|
||||
let suggestedXWidth = SampleSetDist_Bandwidth.nrd0(samples)
|
||||
xWidthToUnitWidth(samples, outputXYPoints, suggestedXWidth)
|
||||
}
|
||||
|
||||
|
@ -102,7 +97,7 @@ let toPointSetDist = (
|
|||
let pdf =
|
||||
continuousPart |> E.A.length > 5
|
||||
? {
|
||||
let _suggestedXWidth = Bandwidth.nrd0(continuousPart)
|
||||
let _suggestedXWidth = SampleSetDist_Bandwidth.nrd0(continuousPart)
|
||||
// todo: This does some recalculating from the last step.
|
||||
let _suggestedUnitWidth = Internals.T.suggestedUnitWidth(
|
||||
continuousPart,
|
||||
|
@ -145,25 +140,3 @@ let toPointSetDist = (
|
|||
|
||||
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))
|
||||
}
|
||||
}
|
|
@ -118,7 +118,7 @@ module PointwiseCombination = {
|
|||
switch pointwiseOp {
|
||||
| #Add => pointwiseAdd(evaluationParams, t1, t2)
|
||||
| #Multiply => pointwiseCombine(\"*.", evaluationParams, t1, t2)
|
||||
| #Exponentiate => pointwiseCombine(\"**", evaluationParams, t1, t2)
|
||||
| #Power => pointwiseCombine(\"**", evaluationParams, t1, t2)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -218,15 +218,14 @@ module SamplingDistribution = {
|
|||
algebraicOp,
|
||||
a,
|
||||
b,
|
||||
)
|
||||
) |> E.O.toResult("Could not get samples")
|
||||
|
||||
let pointSetDist =
|
||||
samples
|
||||
|> E.O.fmap(r =>
|
||||
SampleSet.toPointSetDist(~samplingInputs=evaluationParams.samplingInputs, ~samples=r, ())
|
||||
)
|
||||
|> E.O.bind(_, r => r.pointSetDist)
|
||||
|> E.O.toResult("No response")
|
||||
let sampleSetDist = samples -> E.R.bind(SampleSetDist.make)
|
||||
|
||||
let pointSetDist =
|
||||
sampleSetDist
|
||||
-> E.R.bind(r =>
|
||||
SampleSetDist.toPointSetDist(~samplingInputs=evaluationParams.samplingInputs, ~samples=r));
|
||||
pointSetDist |> E.R.fmap(r => #Normalize(#RenderedDist(r)))
|
||||
})
|
||||
}
|
||||
|
|
|
@ -227,7 +227,7 @@ let all = [
|
|||
},
|
||||
(),
|
||||
),
|
||||
makeRenderedDistFloat("scaleExp", (dist, float) => verticalScaling(#Exponentiate, dist, float)),
|
||||
makeRenderedDistFloat("scaleExp", (dist, float) => verticalScaling(#Power, dist, float)),
|
||||
makeRenderedDistFloat("scaleMultiply", (dist, float) => verticalScaling(#Multiply, dist, float)),
|
||||
makeRenderedDistFloat("scaleLog", (dist, float) => verticalScaling(#Logarithm, dist, float)),
|
||||
Multimodal._function,
|
||||
|
|
|
@ -144,11 +144,11 @@ module MathAdtToDistDst = {
|
|||
| ("subtract", _) => Error("Subtraction needs two operands")
|
||||
| ("multiply", [l, r]) => toOkAlgebraic((#Multiply, l, r))
|
||||
| ("multiply", _) => Error("Multiplication needs two operands")
|
||||
| ("pow", [l, r]) => toOkAlgebraic((#Exponentiate, l, r))
|
||||
| ("pow", [l, r]) => toOkAlgebraic((#Power, l, r))
|
||||
| ("pow", _) => Error("Exponentiation needs two operands")
|
||||
| ("dotMultiply", [l, r]) => toOkPointwise((#Multiply, l, r))
|
||||
| ("dotMultiply", _) => Error("Dotwise multiplication needs two operands")
|
||||
| ("dotPow", [l, r]) => toOkPointwise((#Exponentiate, l, r))
|
||||
| ("dotPow", [l, r]) => toOkPointwise((#Power, l, r))
|
||||
| ("dotPow", _) => Error("Dotwise exponentiation needs two operands")
|
||||
| ("rightLogShift", [l, r]) => toOkPointwise((#Add, l, r))
|
||||
| ("rightLogShift", _) => Error("Dotwise addition needs two operands")
|
||||
|
|
|
@ -18,8 +18,8 @@ module Helpers = {
|
|||
| "divide" => #Divide
|
||||
| "log" => #Logarithm
|
||||
| "dotDivide" => #Divide
|
||||
| "pow" => #Exponentiate
|
||||
| "dotPow" => #Exponentiate
|
||||
| "pow" => #Power
|
||||
| "dotPow" => #Power
|
||||
| "multiply" => #Multiply
|
||||
| "dotMultiply" => #Multiply
|
||||
| "dotLog" => #Logarithm
|
||||
|
|
|
@ -22,3 +22,6 @@ type resultDist = result<genericDist, error>
|
|||
type resultFloat = result<float, error>
|
||||
@genType
|
||||
type resultString = result<string, error>
|
||||
|
||||
@genType
|
||||
let makeSampleSetDist = SampleSetDist.make
|
|
@ -6,12 +6,12 @@ type algebraicOperation = [
|
|||
| #Multiply
|
||||
| #Subtract
|
||||
| #Divide
|
||||
| #Exponentiate
|
||||
| #Power
|
||||
| #Logarithm
|
||||
]
|
||||
@genType
|
||||
type pointwiseOperation = [#Add | #Multiply | #Exponentiate]
|
||||
type scaleOperation = [#Multiply | #Exponentiate | #Logarithm | #Divide]
|
||||
type pointwiseOperation = [#Add | #Multiply | #Power]
|
||||
type scaleOperation = [#Multiply | #Power | #Logarithm | #Divide]
|
||||
type distToFloatOperation = [
|
||||
| #Pdf(float)
|
||||
| #Cdf(float)
|
||||
|
@ -27,7 +27,7 @@ module Algebraic = {
|
|||
| #Add => \"+."
|
||||
| #Subtract => \"-."
|
||||
| #Multiply => \"*."
|
||||
| #Exponentiate => \"**"
|
||||
| #Power => \"**"
|
||||
| #Divide => \"/."
|
||||
| #Logarithm => (a, b) => log(a) /. log(b)
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ module Algebraic = {
|
|||
| #Add => "+"
|
||||
| #Subtract => "-"
|
||||
| #Multiply => "*"
|
||||
| #Exponentiate => "**"
|
||||
| #Power => "**"
|
||||
| #Divide => "/"
|
||||
| #Logarithm => "log"
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ module Pointwise = {
|
|||
let toString = x =>
|
||||
switch x {
|
||||
| #Add => "+"
|
||||
| #Exponentiate => "^"
|
||||
| #Power => "**"
|
||||
| #Multiply => "*"
|
||||
}
|
||||
|
||||
|
@ -83,7 +83,7 @@ module Scale = {
|
|||
switch x {
|
||||
| #Multiply => \"*."
|
||||
| #Divide => \"/."
|
||||
| #Exponentiate => \"**"
|
||||
| #Power => \"**"
|
||||
| #Logarithm => (a, b) => log(a) /. log(b)
|
||||
}
|
||||
|
||||
|
@ -91,7 +91,7 @@ module Scale = {
|
|||
switch operation {
|
||||
| #Multiply => j`verticalMultiply($value, $scaleBy) `
|
||||
| #Divide => j`verticalDivide($value, $scaleBy) `
|
||||
| #Exponentiate => j`verticalExponentiate($value, $scaleBy) `
|
||||
| #Power => j`verticalPower($value, $scaleBy) `
|
||||
| #Logarithm => j`verticalLog($value, $scaleBy) `
|
||||
}
|
||||
|
||||
|
@ -99,7 +99,7 @@ module Scale = {
|
|||
switch x {
|
||||
| #Multiply => (a, b) => Some(a *. b)
|
||||
| #Divide => (a, b) => Some(a /. b)
|
||||
| #Exponentiate => (_, _) => None
|
||||
| #Power => (_, _) => None
|
||||
| #Logarithm => (_, _) => None
|
||||
}
|
||||
|
||||
|
@ -107,7 +107,7 @@ module Scale = {
|
|||
switch x {
|
||||
| #Multiply => (_, _) => None // TODO: this could probably just be multiplied out (using Continuous.scaleBy)
|
||||
| #Divide => (_, _) => None
|
||||
| #Exponentiate => (_, _) => None
|
||||
| #Power => (_, _) => None
|
||||
| #Logarithm => (_, _) => None
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user