From ea5dd219b600549209a127f36bbbe51310494ac8 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Mon, 21 Mar 2022 16:39:33 -0400 Subject: [PATCH 01/41] First steps of refactor --- packages/squiggle-lang/bsconfig.json | 1 - packages/squiggle-lang/src/rescript/sci.res | 62 +++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 packages/squiggle-lang/src/rescript/sci.res diff --git a/packages/squiggle-lang/bsconfig.json b/packages/squiggle-lang/bsconfig.json index 03842597..4a738069 100644 --- a/packages/squiggle-lang/bsconfig.json +++ b/packages/squiggle-lang/bsconfig.json @@ -32,7 +32,6 @@ ], "gentypeconfig": { "language": "typescript", - "generatedFileExtension": ".gen.ts", "module": "commonjs", "shims": {}, "debug": { diff --git a/packages/squiggle-lang/src/rescript/sci.res b/packages/squiggle-lang/src/rescript/sci.res new file mode 100644 index 00000000..83b13ab6 --- /dev/null +++ b/packages/squiggle-lang/src/rescript/sci.res @@ -0,0 +1,62 @@ +type symboliDist = SymbolicDistTypes.symbolicDist; + +type genericDist = [ + | #XYContinuous(PointSetTypes.continuousShape) + | #XYDiscrete(Discrete.t) + | #SampleSet(array) + | #Symbolic(symboliDist) + | #Error(string) +] + +let isSymbolic = (r: genericDist) => + switch r { + | #Symbolic(_) => true + | _ => false + } + +type params = { + sampleCount: int, + xyPointLength: int, +} + +let genericParams = { + sampleCount: 1000, + xyPointLength: 1000, +} + +type wrapped = (genericDist, params) + +let wrapWithParams = (g: genericDist, f: params): wrapped => (g, f) + +let exampleDist: genericDist = #XYDiscrete( + Discrete.make(~integralSumCache=Some(1.0), {xs: [3.0], ys: [1.0]}), +) + +let rec isFunctionPossible = (wrapped: wrapped, fnName): bool => { + let (v, _) = wrapped + switch (fnName, v) { + | (#truncateLeft(_), #XYContinuous(_)) => true + | (#truncateRight(_), #XYContinuous(_)) => true + | _ => false + } +} + +let rec doFunction = (wrapped: wrapped, fnName): wrapped => { + let (v, extra) = wrapped + let newVal = switch (fnName, v) { + | (#truncateLeft(f), #XYContinuous(r)) => #XYContinuous(Continuous.T.truncate(Some(f), None, r)) + | (#truncateRight(f), #XYContinuous(r)) => #XYContinuous(Continuous.T.truncate(None, Some(f), r)) + | (#toPointSet, #XYContinuous(r)) => v + | (#toPointSet, #XYDiscrete(r)) => v + | (#toPointSet, #Symbolic(#Float(f))) => #XYDiscrete(Discrete.make(~integralSumCache=Some(1.0), {xs: [f], ys: [1.0]})); + | (#toPointSet, #Symbolic(r)) => { + let xs = SymbolicDist.T.interpolateXs(~xSelection=#ByWeight, r, 1000) + let ys = xs |> E.A.fmap(x => SymbolicDist.T.pdf(x, r)) + #XYContinuous(Continuous.make(~integralSumCache=Some(1.0), {xs: xs, ys: ys})) + } + | _ => #Error("No Match") + } + (newVal, extra) +} + +let foo = exampleDist->wrapWithParams(genericParams)->doFunction(#truncateLeft(3.0)) \ No newline at end of file From 0ded4a404fd8fdce0cc36d0985da77b2de9d7e06 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Mon, 21 Mar 2022 20:08:16 -0400 Subject: [PATCH 02/41] distToDist and distToFloat --- .../rescript/pointSetDist/PointSetDist.res | 2 +- packages/squiggle-lang/src/rescript/sci.res | 75 ++++++++++++------- 2 files changed, 47 insertions(+), 30 deletions(-) diff --git a/packages/squiggle-lang/src/rescript/pointSetDist/PointSetDist.res b/packages/squiggle-lang/src/rescript/pointSetDist/PointSetDist.res index e50470ac..4a9eda9e 100644 --- a/packages/squiggle-lang/src/rescript/pointSetDist/PointSetDist.res +++ b/packages/squiggle-lang/src/rescript/pointSetDist/PointSetDist.res @@ -196,7 +196,7 @@ let sampleNRendered = (n, dist) => { let operate = (distToFloatOp: Operation.distToFloatOperation, s): float => switch distToFloatOp { | #Pdf(f) => pdf(f, s) - | #Cdf(f) => pdf(f, s) + | #Cdf(f) => cdf(f, s) | #Inv(f) => inv(f, s) | #Sample => sample(s) | #Mean => T.mean(s) diff --git a/packages/squiggle-lang/src/rescript/sci.res b/packages/squiggle-lang/src/rescript/sci.res index 83b13ab6..c31c22f8 100644 --- a/packages/squiggle-lang/src/rescript/sci.res +++ b/packages/squiggle-lang/src/rescript/sci.res @@ -1,19 +1,13 @@ -type symboliDist = SymbolicDistTypes.symbolicDist; +type symboliDist = SymbolicDistTypes.symbolicDist type genericDist = [ - | #XYContinuous(PointSetTypes.continuousShape) - | #XYDiscrete(Discrete.t) + | #XYShape(PointSetTypes.pointSetDist) | #SampleSet(array) | #Symbolic(symboliDist) | #Error(string) + | #Float(float) ] -let isSymbolic = (r: genericDist) => - switch r { - | #Symbolic(_) => true - | _ => false - } - type params = { sampleCount: int, xyPointLength: int, @@ -28,35 +22,58 @@ type wrapped = (genericDist, params) let wrapWithParams = (g: genericDist, f: params): wrapped => (g, f) -let exampleDist: genericDist = #XYDiscrete( - Discrete.make(~integralSumCache=Some(1.0), {xs: [3.0], ys: [1.0]}), +let exampleDist: genericDist = #XYShape( + Discrete(Discrete.make(~integralSumCache=Some(1.0), {xs: [3.0], ys: [1.0]})), ) -let rec isFunctionPossible = (wrapped: wrapped, fnName): bool => { - let (v, _) = wrapped - switch (fnName, v) { - | (#truncateLeft(_), #XYContinuous(_)) => true - | (#truncateRight(_), #XYContinuous(_)) => true - | _ => false - } +let defaultSamplingInputs: SamplingInputs.samplingInputs = { + sampleCount: 10000, + outputXYPoints: 10000, + pointSetDistLength: 1000, + kernelWidth: None, } -let rec doFunction = (wrapped: wrapped, fnName): wrapped => { +let distToFloat = (wrapped: wrapped, fnName) => { let (v, extra) = wrapped let newVal = switch (fnName, v) { - | (#truncateLeft(f), #XYContinuous(r)) => #XYContinuous(Continuous.T.truncate(Some(f), None, r)) - | (#truncateRight(f), #XYContinuous(r)) => #XYContinuous(Continuous.T.truncate(None, Some(f), r)) - | (#toPointSet, #XYContinuous(r)) => v - | (#toPointSet, #XYDiscrete(r)) => v - | (#toPointSet, #Symbolic(#Float(f))) => #XYDiscrete(Discrete.make(~integralSumCache=Some(1.0), {xs: [f], ys: [1.0]})); - | (#toPointSet, #Symbolic(r)) => { - let xs = SymbolicDist.T.interpolateXs(~xSelection=#ByWeight, r, 1000) - let ys = xs |> E.A.fmap(x => SymbolicDist.T.pdf(x, r)) - #XYContinuous(Continuous.make(~integralSumCache=Some(1.0), {xs: xs, ys: ys})) + | (operation, #XYShape(r)) => #Float(PointSetDist.operate(operation, r)) + | (operation, #Symbolic(r)) => switch(SymbolicDist.T.operate(operation, r)){ + | Ok(r) => #SymbolicDist(r) + | Error(r) => #Error(r) } | _ => #Error("No Match") } (newVal, extra) } -let foo = exampleDist->wrapWithParams(genericParams)->doFunction(#truncateLeft(3.0)) \ No newline at end of file +let distToDist = (wrapped: wrapped, fnName): wrapped => { + let (v, extra) = wrapped + let newVal = switch (fnName, v) { + | (#normalize, #XYShape(r)) => #XYShape(PointSetDist.T.normalize(r)) + | (#normalize, #Symbolic(_)) => v + | (#normalize, #SampleSet(_)) => v + | (#toPointSet, #XYShape(_)) => v + | (#toPointSet, #Symbolic(r)) => #XYShape(SymbolicDist.T.toPointSetDist(1000, r)) + | (#toPointSet, #SampleSet(r)) => { + let response = SampleSet.toPointSetDist( + ~samples=r, + ~samplingInputs=defaultSamplingInputs, + (), + ).pointSetDist + switch response { + | Some(r) => #XYShape(r) + | None => #Error("Failed to convert sample into shape") + } + } + | _ => #Error("No Match") + } + (newVal, extra) +} +// | (#truncateLeft(f), #XYContinuous(r)) => #XYContinuous(Continuous.T.truncate(Some(f), None, r)) +// | (#truncateRight(f), #XYContinuous(r)) => #XYContinuous(Continuous.T.truncate(None, Some(f), r)) + +let foo = + exampleDist + ->wrapWithParams(genericParams) + ->distToDist(#truncateLeft(3.0)) + ->distToDist(#trunctateRight(5.0)) From 540d035b900db77b2a8db30da5bd8a6ec5bd6f04 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Wed, 23 Mar 2022 17:29:20 -0400 Subject: [PATCH 03/41] Refactored applyFnInternal --- packages/squiggle-lang/src/rescript/sci.res | 92 ++++++++++++++------- 1 file changed, 61 insertions(+), 31 deletions(-) diff --git a/packages/squiggle-lang/src/rescript/sci.res b/packages/squiggle-lang/src/rescript/sci.res index c31c22f8..82b33e5f 100644 --- a/packages/squiggle-lang/src/rescript/sci.res +++ b/packages/squiggle-lang/src/rescript/sci.res @@ -1,13 +1,44 @@ type symboliDist = SymbolicDistTypes.symbolicDist +type error = + | NeedsPointSetConversion + | Other(string) + type genericDist = [ | #XYShape(PointSetTypes.pointSetDist) | #SampleSet(array) | #Symbolic(symboliDist) - | #Error(string) + | #Error(error) | #Float(float) ] +type combination = [ + | #Add + | #Multiply + | #Subtract + | #Divide + | #Exponentiate +] + +type toFloat = [ + | #Cdf(float) + | #Inv(float) + | #Mean + | #Pdf(float) + | #Sample +] + +type toDist = [ + | #normalize + | #toPointSet +] + +type operation = [ + | #toFloat(toFloat) + | #toDist(toDist) + | #toDistCombination(combination, genericDist) +] + type params = { sampleCount: int, xyPointLength: int, @@ -33,28 +64,22 @@ let defaultSamplingInputs: SamplingInputs.samplingInputs = { kernelWidth: None, } -let distToFloat = (wrapped: wrapped, fnName) => { +let applyFnInternal = (wrapped: wrapped, fnName: operation): wrapped => { let (v, extra) = wrapped - let newVal = switch (fnName, v) { - | (operation, #XYShape(r)) => #Float(PointSetDist.operate(operation, r)) - | (operation, #Symbolic(r)) => switch(SymbolicDist.T.operate(operation, r)){ - | Ok(r) => #SymbolicDist(r) - | Error(r) => #Error(r) - } - | _ => #Error("No Match") - } - (newVal, extra) -} - -let distToDist = (wrapped: wrapped, fnName): wrapped => { - let (v, extra) = wrapped - let newVal = switch (fnName, v) { - | (#normalize, #XYShape(r)) => #XYShape(PointSetDist.T.normalize(r)) - | (#normalize, #Symbolic(_)) => v - | (#normalize, #SampleSet(_)) => v - | (#toPointSet, #XYShape(_)) => v - | (#toPointSet, #Symbolic(r)) => #XYShape(SymbolicDist.T.toPointSetDist(1000, r)) - | (#toPointSet, #SampleSet(r)) => { + let newVal: genericDist = switch (fnName, v) { + | (#toFloat(n), #XYShape(r)) => #Float(PointSetDist.operate(n, r)) + | (#toFloat(n), #Symbolic(r)) => + switch SymbolicDist.T.operate(n, r) { + | Ok(float) => #Float(float) + | Error(e) => #Error(Other(e)) + } + | (#toFloat(n), #SampleSet(_)) => #Error(NeedsPointSetConversion) + | (#toDist(#normalize), #XYShape(r)) => #XYShape(PointSetDist.T.normalize(r)) + | (#toDist(#normalize), #Symbolic(_)) => v + | (#toDist(#normalize), #SampleSet(_)) => v + | (#toDist(#toPointSet), #XYShape(_)) => v + | (#toDist(#toPointSet), #Symbolic(r)) => #XYShape(SymbolicDist.T.toPointSetDist(1000, r)) + | (#toDist(#toPointSet), #SampleSet(r)) => { let response = SampleSet.toPointSetDist( ~samples=r, ~samplingInputs=defaultSamplingInputs, @@ -62,18 +87,23 @@ let distToDist = (wrapped: wrapped, fnName): wrapped => { ).pointSetDist switch response { | Some(r) => #XYShape(r) - | None => #Error("Failed to convert sample into shape") + | None => #Error(Other("Failed to convert sample into shape")) } } - | _ => #Error("No Match") + | _ => #Error(Other("No Match or not supported")) } (newVal, extra) } -// | (#truncateLeft(f), #XYContinuous(r)) => #XYContinuous(Continuous.T.truncate(Some(f), None, r)) -// | (#truncateRight(f), #XYContinuous(r)) => #XYContinuous(Continuous.T.truncate(None, Some(f), r)) -let foo = - exampleDist - ->wrapWithParams(genericParams) - ->distToDist(#truncateLeft(3.0)) - ->distToDist(#trunctateRight(5.0)) +let applyFn = (wrapped, fnName): wrapped => { + let (v, extra) as result = applyFnInternal(wrapped, fnName) + switch v { + | #Error(NeedsPointSetConversion) => { + let convertedToPointSet = applyFnInternal(wrapped, #toDist(#toPointSet)) + applyFnInternal(convertedToPointSet, fnName) + } + | _ => result + } +} + +let foo = exampleDist->wrapWithParams(genericParams)->applyFn(#toDist(#normalize)) From 1aae479aa31e54657ddce8ee8f0cfdcb8bd07800 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Thu, 24 Mar 2022 14:46:36 -0400 Subject: [PATCH 04/41] Added combinations to sci.res --- packages/squiggle-lang/src/rescript/sci.res | 96 +++++++++++++++++++-- 1 file changed, 91 insertions(+), 5 deletions(-) diff --git a/packages/squiggle-lang/src/rescript/sci.res b/packages/squiggle-lang/src/rescript/sci.res index 82b33e5f..dac5e5d6 100644 --- a/packages/squiggle-lang/src/rescript/sci.res +++ b/packages/squiggle-lang/src/rescript/sci.res @@ -2,6 +2,8 @@ type symboliDist = SymbolicDistTypes.symbolicDist type error = | NeedsPointSetConversion + | InputsNeedPointSetConversion + | NotYetImplemented | Other(string) type genericDist = [ @@ -12,6 +14,11 @@ type genericDist = [ | #Float(float) ] +type direction = [ + | #Algebraic + | #Pointwise +] + type combination = [ | #Add | #Multiply @@ -20,6 +27,15 @@ type combination = [ | #Exponentiate ] +let combinationToFn = (combination: combination) => + switch combination { + | #Add => \"+." + | #Multiply => \"*." + | #Subtract => \"-." + | #Exponentiate => \"**" + | #Divide => \"/." + } + type toFloat = [ | #Cdf(float) | #Inv(float) @@ -31,12 +47,17 @@ type toFloat = [ type toDist = [ | #normalize | #toPointSet + | #toSampleSet(int) +] + +type toFloatArray = [ + | #Sample(int) ] type operation = [ | #toFloat(toFloat) | #toDist(toDist) - | #toDistCombination(combination, genericDist) + | #toDistCombination(direction, combination, genericDist) ] type params = { @@ -64,8 +85,37 @@ let defaultSamplingInputs: SamplingInputs.samplingInputs = { kernelWidth: None, } -let applyFnInternal = (wrapped: wrapped, fnName: operation): wrapped => { - let (v, extra) = wrapped +/* Given two random variables A and B, this returns the distribution + of a new variable that is the result of the operation on A and B. + For instance, normal(0, 1) + normal(1, 1) -> normal(1, 2). + In general, this is implemented via convolution. */ +module AlgebraicCombination = { + let tryAnalyticalSimplification = (operation, t1: genericDist, t2: genericDist) => + switch (operation, t1, t2) { + | (operation, #Symbolic(d1), #Symbolic(d2)) => + switch SymbolicDist.T.tryAnalyticalSimplification(d1, d2, operation) { + | #AnalyticalSolution(symbolicDist) => Ok(#Symbolic(symbolicDist)) + | #Error(er) => Error(er) + | #NoSolution => Ok(#NoSolution) + } + | _ => Ok(#NoSolution) + } +} + +// let toSampleSet = (r) + +let sampleN = (n, genericDist) => { + switch genericDist { + | #XYShape(r) => Ok(PointSetDist.sampleNRendered(n, r)) + | #Symbolic(r) => Ok(SymbolicDist.T.sampleN(n, r)) + | #SampleSet(r) => Error(NotYetImplemented) + | #Error(r) => Error(r) + | _ => Error(NotYetImplemented) + } +} + +let rec applyFnInternal = (wrapped: wrapped, fnName: operation): wrapped => { + let (v, {sampleCount, xyPointLength} as extra) = wrapped let newVal: genericDist = switch (fnName, v) { | (#toFloat(n), #XYShape(r)) => #Float(PointSetDist.operate(n, r)) | (#toFloat(n), #Symbolic(r)) => @@ -78,7 +128,7 @@ let applyFnInternal = (wrapped: wrapped, fnName: operation): wrapped => { | (#toDist(#normalize), #Symbolic(_)) => v | (#toDist(#normalize), #SampleSet(_)) => v | (#toDist(#toPointSet), #XYShape(_)) => v - | (#toDist(#toPointSet), #Symbolic(r)) => #XYShape(SymbolicDist.T.toPointSetDist(1000, r)) + | (#toDist(#toPointSet), #Symbolic(r)) => #XYShape(SymbolicDist.T.toPointSetDist(sampleCount, r)) | (#toDist(#toPointSet), #SampleSet(r)) => { let response = SampleSet.toPointSetDist( ~samples=r, @@ -90,9 +140,35 @@ let applyFnInternal = (wrapped: wrapped, fnName: operation): wrapped => { | None => #Error(Other("Failed to convert sample into shape")) } } + | (#toDist(#toSampleSet(n)), r) => + switch sampleN(n, r) { + | Ok(r) => #SampleSet(r) + | Error(r) => #Error(r) + } + | (#toDistCombination(#Algebraic, operation, p2), p1) => { + // TODO: This could be more complex, to get possible simplification and similar. + let dist1 = sampleN(sampleCount, p1) + let dist2 = sampleN(sampleCount, p2) + let samples = E.R.merge(dist1, dist2) |> E.R.fmap(((d1, d2)) => { + Belt.Array.zip(d1, d2) |> E.A.fmap(((a, b)) => Operation.Algebraic.toFn(operation, a, b)) + }) + switch samples { + | Ok(r) => #SampleSet(r) + | Error(e) => #Error(e) + } + } + | (#toDistCombination(#Pointwise, operation, p2), p1) => + switch ( + applyFnInternal((p1, extra), #toDist(#toPointSet)), + applyFnInternal((p2, extra), #toDist(#toPointSet)), + ) { + | ((#XYShape(p1), _), (#XYShape(p2), _)) => + #XYShape(PointSetDist.combinePointwise(combinationToFn(operation), p1, p2)) + | _ => #Error(Other("No Match or not supported")) + } | _ => #Error(Other("No Match or not supported")) } - (newVal, extra) + (newVal, {sampleCount: sampleCount, xyPointLength: xyPointLength}) } let applyFn = (wrapped, fnName): wrapped => { @@ -102,6 +178,16 @@ let applyFn = (wrapped, fnName): wrapped => { let convertedToPointSet = applyFnInternal(wrapped, #toDist(#toPointSet)) applyFnInternal(convertedToPointSet, fnName) } + | #Error(InputsNeedPointSetConversion) => { + let altDist = switch fnName { + | #toDistCombination(p1, p2, dist) => { + let (newDist, _) = applyFnInternal((dist, extra), #toDist(#toPointSet)) + applyFnInternal(wrapped, #toDistCombination(p1, p2, newDist)) + } + | _ => (#Error(Other("Not needed")), extra) + } + altDist + } | _ => result } } From 1a2ce5bfa0db9d737ea91508d1d6472b550f2399 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Fri, 25 Mar 2022 22:11:27 -0400 Subject: [PATCH 05/41] Minor refactors --- packages/squiggle-lang/src/rescript/sci.res | 159 +++++++++++++------- 1 file changed, 102 insertions(+), 57 deletions(-) diff --git a/packages/squiggle-lang/src/rescript/sci.res b/packages/squiggle-lang/src/rescript/sci.res index dac5e5d6..15a57f44 100644 --- a/packages/squiggle-lang/src/rescript/sci.res +++ b/packages/squiggle-lang/src/rescript/sci.res @@ -1,19 +1,28 @@ -type symboliDist = SymbolicDistTypes.symbolicDist - type error = | NeedsPointSetConversion | InputsNeedPointSetConversion | NotYetImplemented + | ImpossiblePath | Other(string) type genericDist = [ - | #XYShape(PointSetTypes.pointSetDist) + | #PointSet(PointSetTypes.pointSetDist) | #SampleSet(array) - | #Symbolic(symboliDist) + | #Symbolic(SymbolicDistTypes.symbolicDist) +] + +type outputType = [ + | #Dist(genericDist) | #Error(error) | #Float(float) ] +let fromResult = (r: result): outputType => + switch r { + | Ok(o) => o + | Error(e) => #Error(e) + } + type direction = [ | #Algebraic | #Pointwise @@ -71,10 +80,11 @@ let genericParams = { } type wrapped = (genericDist, params) +type wrappedOutput = (outputType, params) let wrapWithParams = (g: genericDist, f: params): wrapped => (g, f) -let exampleDist: genericDist = #XYShape( +let exampleDist: genericDist = #PointSet( Discrete(Discrete.make(~integralSumCache=Some(1.0), {xs: [3.0], ys: [1.0]})), ) @@ -106,45 +116,79 @@ module AlgebraicCombination = { let sampleN = (n, genericDist) => { switch genericDist { - | #XYShape(r) => Ok(PointSetDist.sampleNRendered(n, r)) + | #PointSet(r) => Ok(PointSetDist.sampleNRendered(n, r)) | #Symbolic(r) => Ok(SymbolicDist.T.sampleN(n, r)) - | #SampleSet(r) => Error(NotYetImplemented) - | #Error(r) => Error(r) - | _ => Error(NotYetImplemented) + | #SampleSet(_) => Error(NotYetImplemented) } } -let rec applyFnInternal = (wrapped: wrapped, fnName: operation): wrapped => { - let (v, {sampleCount, xyPointLength} as extra) = wrapped - let newVal: genericDist = switch (fnName, v) { - | (#toFloat(n), #XYShape(r)) => #Float(PointSetDist.operate(n, r)) - | (#toFloat(n), #Symbolic(r)) => - switch SymbolicDist.T.operate(n, r) { - | Ok(float) => #Float(float) - | Error(e) => #Error(Other(e)) +let toFloat = ( + toPointSet: genericDist => result, + fnName, + value, +) => { + switch value { + | #Symbolic(r) if Belt.Result.isOk(SymbolicDist.T.operate(fnName, r)) => + switch SymbolicDist.T.operate(fnName, r) { + | Ok(float) => Ok(float) + | Error(_) => Error(ImpossiblePath) } - | (#toFloat(n), #SampleSet(_)) => #Error(NeedsPointSetConversion) - | (#toDist(#normalize), #XYShape(r)) => #XYShape(PointSetDist.T.normalize(r)) - | (#toDist(#normalize), #Symbolic(_)) => v - | (#toDist(#normalize), #SampleSet(_)) => v - | (#toDist(#toPointSet), #XYShape(_)) => v - | (#toDist(#toPointSet), #Symbolic(r)) => #XYShape(SymbolicDist.T.toPointSetDist(sampleCount, r)) - | (#toDist(#toPointSet), #SampleSet(r)) => { + | #PointSet(r) => Ok(PointSetDist.operate(fnName, r)) + | _ => + switch toPointSet(value) { + | Ok(r) => Ok(PointSetDist.operate(fnName, r)) + | Error(r) => Error(r) + } + } +} + +let distToPointSet = (sampleCount, dist: genericDist) => { + switch dist { + | #PointSet(pointSet) => Ok(pointSet) + | #Symbolic(r) => Ok(SymbolicDist.T.toPointSetDist(sampleCount, r)) + | #SampleSet(r) => { let response = SampleSet.toPointSetDist( ~samples=r, ~samplingInputs=defaultSamplingInputs, (), ).pointSetDist switch response { - | Some(r) => #XYShape(r) - | None => #Error(Other("Failed to convert sample into shape")) + | Some(r) => Ok(r) + | None => Error(Other("Converting sampleSet to pointSet failed")) } } - | (#toDist(#toSampleSet(n)), r) => - switch sampleN(n, r) { - | Ok(r) => #SampleSet(r) - | Error(r) => #Error(r) + } +} + +let rec applyFnInternal = (wrapped: wrapped, fnName: operation): wrappedOutput => { + let (value, {sampleCount, xyPointLength} as extra) = wrapped + let reCall = (~value=value, ~extra=extra, ~fnName=fnName, ()) => { + applyFnInternal((value, extra), fnName) + } + let reCallUnwrapped = (~value=value, ~extra=extra, ~fnName=fnName, ()) => { + let (value, _) = applyFnInternal((value, extra), fnName) + value + } + let toPointSet = r => { + switch reCallUnwrapped(~value=r, ~fnName=#toDist(#toPointSet), ()) { + | #Dist(#PointSet(p)) => Ok(p) + | #Error(r) => Error(r) + | _ => Error(Other("Impossible error")) } + } + let toPointSetAndReCall = v => + toPointSet(v) |> E.R.fmap(r => reCallUnwrapped(~value=#PointSet(r), ())) + let newVal: outputType = switch (fnName, value) { + // | (#toFloat(n), v) => toFloat(toPointSet, v, n) + | (#toFloat(fnName), _) => + toFloat(toPointSet, fnName, value) |> E.R.fmap(r => #Float(r)) |> fromResult + | (#toDist(#normalize), #PointSet(r)) => #Dist(#PointSet(PointSetDist.T.normalize(r))) + | (#toDist(#normalize), #Symbolic(_)) => #Dist(value) + | (#toDist(#normalize), #SampleSet(_)) => #Dist(value) + | (#toDist(#toPointSet), _) => + value |> distToPointSet(sampleCount) |> E.R.fmap(r => #Dist(#PointSet(r))) |> fromResult + | (#toDist(#toSampleSet(n)), _) => + value |> sampleN(n) |> E.R.fmap(r => #Dist(#SampleSet(r))) |> fromResult | (#toDistCombination(#Algebraic, operation, p2), p1) => { // TODO: This could be more complex, to get possible simplification and similar. let dist1 = sampleN(sampleCount, p1) @@ -153,43 +197,44 @@ let rec applyFnInternal = (wrapped: wrapped, fnName: operation): wrapped => { Belt.Array.zip(d1, d2) |> E.A.fmap(((a, b)) => Operation.Algebraic.toFn(operation, a, b)) }) switch samples { - | Ok(r) => #SampleSet(r) + | Ok(r) => #Dist(#SampleSet(r)) | Error(e) => #Error(e) } } | (#toDistCombination(#Pointwise, operation, p2), p1) => switch ( - applyFnInternal((p1, extra), #toDist(#toPointSet)), - applyFnInternal((p2, extra), #toDist(#toPointSet)), + toPointSet(p1), + toPointSet(p2) ) { - | ((#XYShape(p1), _), (#XYShape(p2), _)) => - #XYShape(PointSetDist.combinePointwise(combinationToFn(operation), p1, p2)) - | _ => #Error(Other("No Match or not supported")) + | (Ok(p1), Ok(p2)) => + // TODO: If the dist is symbolic, then it doesn't need to be converted into a pointSet + #Dist(#PointSet(PointSetDist.combinePointwise(combinationToFn(operation), p1, p2))) + | (_, _) => #Error(Other("No Match or not supported")) } | _ => #Error(Other("No Match or not supported")) } (newVal, {sampleCount: sampleCount, xyPointLength: xyPointLength}) } -let applyFn = (wrapped, fnName): wrapped => { - let (v, extra) as result = applyFnInternal(wrapped, fnName) - switch v { - | #Error(NeedsPointSetConversion) => { - let convertedToPointSet = applyFnInternal(wrapped, #toDist(#toPointSet)) - applyFnInternal(convertedToPointSet, fnName) - } - | #Error(InputsNeedPointSetConversion) => { - let altDist = switch fnName { - | #toDistCombination(p1, p2, dist) => { - let (newDist, _) = applyFnInternal((dist, extra), #toDist(#toPointSet)) - applyFnInternal(wrapped, #toDistCombination(p1, p2, newDist)) - } - | _ => (#Error(Other("Not needed")), extra) - } - altDist - } - | _ => result - } -} +// let applyFn = (wrapped, fnName): wrapped => { +// let (v, extra) as result = applyFnInternal(wrapped, fnName) +// switch v { +// | #Error(NeedsPointSetConversion) => { +// let convertedToPointSet = applyFnInternal(wrapped, #toDist(#toPointSet)) +// applyFnInternal(convertedToPointSet, fnName) +// } +// | #Error(InputsNeedPointSetConversion) => { +// let altDist = switch fnName { +// | #toDistCombination(p1, p2, dist) => { +// let (newDist, _) = applyFnInternal((dist, extra), #toDist(#toPointSet)) +// applyFnInternal(wrapped, #toDistCombination(p1, p2, newDist)) +// } +// | _ => (#Error(Other("Not needed")), extra) +// } +// altDist +// } +// | _ => result +// } +// } -let foo = exampleDist->wrapWithParams(genericParams)->applyFn(#toDist(#normalize)) +// let foo = exampleDist->wrapWithParams(genericParams)->applyFn(#toDist(#normalize)) From d490af38f027151a2fa6d666cab24e03f043e292 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Sat, 26 Mar 2022 13:25:47 -0400 Subject: [PATCH 06/41] Added pointwiseCombinationFloat to sci.res --- packages/squiggle-lang/src/rescript/sci.res | 390 +++++++++--------- .../src/rescript/utility/Operation.res | 9 +- 2 files changed, 211 insertions(+), 188 deletions(-) diff --git a/packages/squiggle-lang/src/rescript/sci.res b/packages/squiggle-lang/src/rescript/sci.res index 15a57f44..66a5605a 100644 --- a/packages/squiggle-lang/src/rescript/sci.res +++ b/packages/squiggle-lang/src/rescript/sci.res @@ -3,6 +3,7 @@ type error = | InputsNeedPointSetConversion | NotYetImplemented | ImpossiblePath + | DistributionVerticalShiftIsInvalid | Other(string) type genericDist = [ @@ -11,209 +12,220 @@ type genericDist = [ | #Symbolic(SymbolicDistTypes.symbolicDist) ] -type outputType = [ - | #Dist(genericDist) - | #Error(error) - | #Float(float) -] +module OperationType = { + type direction = [ + | #Algebraic + | #Pointwise + ] -let fromResult = (r: result): outputType => - switch r { - | Ok(o) => o - | Error(e) => #Error(e) + type combination = [ + | #Add + | #Multiply + | #Subtract + | #Divide + | #Exponentiate + | #Log + ] + + let combinationToFn = (combination: combination) => + switch combination { + | #Add => \"+." + | #Multiply => \"*." + | #Subtract => \"-." + | #Exponentiate => \"**" + | #Divide => \"/." + | #Log => (a, b) => log(a) /. log(b) + } + + type toFloat = [ + | #Cdf(float) + | #Inv(float) + | #Mean + | #Pdf(float) + | #Sample + ] + + type toDist = [ + | #normalize + | #toPointSet + | #toSampleSet(int) + ] + + type toFloatArray = [ + | #Sample(int) + ] + + type scale = [ + | #Multiply + | #Exponentiate + | #Log + ] + + type t = [ + | #toFloat(toFloat) + | #toDist(toDist) + | #toDistCombination(direction, combination, [#Dist(genericDist) | #Float(float)]) + ] +} + +type operation = OperationType.t + +module T = { + type t = genericDist + type toPointSetFn = genericDist => result + let sampleN = (n, t: t) => { + switch t { + | #PointSet(r) => Ok(PointSetDist.sampleNRendered(n, r)) + | #Symbolic(r) => Ok(SymbolicDist.T.sampleN(n, r)) + | #SampleSet(_) => Error(NotYetImplemented) + } } -type direction = [ - | #Algebraic - | #Pointwise -] - -type combination = [ - | #Add - | #Multiply - | #Subtract - | #Divide - | #Exponentiate -] - -let combinationToFn = (combination: combination) => - switch combination { - | #Add => \"+." - | #Multiply => \"*." - | #Subtract => \"-." - | #Exponentiate => \"**" - | #Divide => \"/." - } - -type toFloat = [ - | #Cdf(float) - | #Inv(float) - | #Mean - | #Pdf(float) - | #Sample -] - -type toDist = [ - | #normalize - | #toPointSet - | #toSampleSet(int) -] - -type toFloatArray = [ - | #Sample(int) -] - -type operation = [ - | #toFloat(toFloat) - | #toDist(toDist) - | #toDistCombination(direction, combination, genericDist) -] - -type params = { - sampleCount: int, - xyPointLength: int, -} - -let genericParams = { - sampleCount: 1000, - xyPointLength: 1000, -} - -type wrapped = (genericDist, params) -type wrappedOutput = (outputType, params) - -let wrapWithParams = (g: genericDist, f: params): wrapped => (g, f) - -let exampleDist: genericDist = #PointSet( - Discrete(Discrete.make(~integralSumCache=Some(1.0), {xs: [3.0], ys: [1.0]})), -) - -let defaultSamplingInputs: SamplingInputs.samplingInputs = { - sampleCount: 10000, - outputXYPoints: 10000, - pointSetDistLength: 1000, - kernelWidth: None, -} - -/* Given two random variables A and B, this returns the distribution - of a new variable that is the result of the operation on A and B. - For instance, normal(0, 1) + normal(1, 1) -> normal(1, 2). - In general, this is implemented via convolution. */ -module AlgebraicCombination = { - let tryAnalyticalSimplification = (operation, t1: genericDist, t2: genericDist) => - switch (operation, t1, t2) { - | (operation, #Symbolic(d1), #Symbolic(d2)) => - switch SymbolicDist.T.tryAnalyticalSimplification(d1, d2, operation) { - | #AnalyticalSolution(symbolicDist) => Ok(#Symbolic(symbolicDist)) - | #Error(er) => Error(er) - | #NoSolution => Ok(#NoSolution) + let toFloat = (toPointSet: toPointSetFn, fnName, t: genericDist) => { + switch t { + | #Symbolic(r) if Belt.Result.isOk(SymbolicDist.T.operate(fnName, r)) => + switch SymbolicDist.T.operate(fnName, r) { + | Ok(float) => Ok(float) + | Error(_) => Error(ImpossiblePath) } - | _ => Ok(#NoSolution) - } -} - -// let toSampleSet = (r) - -let sampleN = (n, genericDist) => { - switch genericDist { - | #PointSet(r) => Ok(PointSetDist.sampleNRendered(n, r)) - | #Symbolic(r) => Ok(SymbolicDist.T.sampleN(n, r)) - | #SampleSet(_) => Error(NotYetImplemented) - } -} - -let toFloat = ( - toPointSet: genericDist => result, - fnName, - value, -) => { - switch value { - | #Symbolic(r) if Belt.Result.isOk(SymbolicDist.T.operate(fnName, r)) => - switch SymbolicDist.T.operate(fnName, r) { - | Ok(float) => Ok(float) - | Error(_) => Error(ImpossiblePath) - } - | #PointSet(r) => Ok(PointSetDist.operate(fnName, r)) - | _ => - switch toPointSet(value) { - | Ok(r) => Ok(PointSetDist.operate(fnName, r)) - | Error(r) => Error(r) - } - } -} - -let distToPointSet = (sampleCount, dist: genericDist) => { - switch dist { - | #PointSet(pointSet) => Ok(pointSet) - | #Symbolic(r) => Ok(SymbolicDist.T.toPointSetDist(sampleCount, r)) - | #SampleSet(r) => { - let response = SampleSet.toPointSetDist( - ~samples=r, - ~samplingInputs=defaultSamplingInputs, - (), - ).pointSetDist - switch response { - | Some(r) => Ok(r) - | None => Error(Other("Converting sampleSet to pointSet failed")) + | _ => + switch toPointSet(t) { + | Ok(r) => Ok(PointSetDist.operate(fnName, r)) + | Error(r) => Error(r) } } } -} -let rec applyFnInternal = (wrapped: wrapped, fnName: operation): wrappedOutput => { - let (value, {sampleCount, xyPointLength} as extra) = wrapped - let reCall = (~value=value, ~extra=extra, ~fnName=fnName, ()) => { - applyFnInternal((value, extra), fnName) + //TODO: Refactor this bit. + let defaultSamplingInputs: SamplingInputs.samplingInputs = { + sampleCount: 10000, + outputXYPoints: 10000, + pointSetDistLength: 1000, + kernelWidth: None, } - let reCallUnwrapped = (~value=value, ~extra=extra, ~fnName=fnName, ()) => { - let (value, _) = applyFnInternal((value, extra), fnName) - value - } - let toPointSet = r => { - switch reCallUnwrapped(~value=r, ~fnName=#toDist(#toPointSet), ()) { - | #Dist(#PointSet(p)) => Ok(p) - | #Error(r) => Error(r) - | _ => Error(Other("Impossible error")) + + let toPointSet = (xyPointLength, t: t) => { + switch t { + | #PointSet(pointSet) => Ok(pointSet) + | #Symbolic(r) => Ok(SymbolicDist.T.toPointSetDist(xyPointLength, r)) + | #SampleSet(r) => { + let response = SampleSet.toPointSetDist( + ~samples=r, + ~samplingInputs=defaultSamplingInputs, + (), + ).pointSetDist + switch response { + | Some(r) => Ok(r) + | None => Error(Other("Converting sampleSet to pointSet failed")) + } + } } } - let toPointSetAndReCall = v => - toPointSet(v) |> E.R.fmap(r => reCallUnwrapped(~value=#PointSet(r), ())) - let newVal: outputType = switch (fnName, value) { - // | (#toFloat(n), v) => toFloat(toPointSet, v, n) - | (#toFloat(fnName), _) => - toFloat(toPointSet, fnName, value) |> E.R.fmap(r => #Float(r)) |> fromResult - | (#toDist(#normalize), #PointSet(r)) => #Dist(#PointSet(PointSetDist.T.normalize(r))) - | (#toDist(#normalize), #Symbolic(_)) => #Dist(value) - | (#toDist(#normalize), #SampleSet(_)) => #Dist(value) - | (#toDist(#toPointSet), _) => - value |> distToPointSet(sampleCount) |> E.R.fmap(r => #Dist(#PointSet(r))) |> fromResult - | (#toDist(#toSampleSet(n)), _) => - value |> sampleN(n) |> E.R.fmap(r => #Dist(#SampleSet(r))) |> fromResult - | (#toDistCombination(#Algebraic, operation, p2), p1) => { - // TODO: This could be more complex, to get possible simplification and similar. - let dist1 = sampleN(sampleCount, p1) - let dist2 = sampleN(sampleCount, p2) - let samples = E.R.merge(dist1, dist2) |> E.R.fmap(((d1, d2)) => { - Belt.Array.zip(d1, d2) |> E.A.fmap(((a, b)) => Operation.Algebraic.toFn(operation, a, b)) + + let algebraicCombination = (operation, sampleCount, dist1: t, dist2: t) => { + let dist1 = sampleN(sampleCount, dist1) + let dist2 = sampleN(sampleCount, dist2) + let samples = E.R.merge(dist1, dist2) |> E.R.fmap(((d1, d2)) => { + Belt.Array.zip(d1, d2) |> E.A.fmap(((a, b)) => Operation.Algebraic.toFn(operation, a, b)) + }) + samples |> E.R.fmap(r => #SampleSet(r)) + } + + let pointwiseCombination = (toPointSet: toPointSetFn, operation, t1: t, t2: t) => { + E.R.merge(toPointSet(t1), toPointSet(t2)) + |> E.R.fmap(((t1, t2)) => + PointSetDist.combinePointwise(OperationType.combinationToFn(operation), t1, t2) + ) + |> E.R.fmap(r => #PointSet(r)) + } + + let pointwiseCombinationFloat = ( + toPointSet: toPointSetFn, + operation: OperationType.combination, + t: t, + f: float, + ) => { + switch operation { + | #Add | #Subtract => Error(DistributionVerticalShiftIsInvalid) + | (#Multiply | #Divide | #Exponentiate | #Log) as operation => + toPointSet(t) |> E.R.fmap(t => { + let fn = (secondary, main) => Operation.Scale.toFn(operation, main, secondary) + let integralSumCacheFn = Operation.Scale.toIntegralSumCacheFn(operation) + let integralCacheFn = Operation.Scale.toIntegralCacheFn(operation) + PointSetDist.T.mapY( + ~integralSumCacheFn=integralSumCacheFn(f), + ~integralCacheFn=integralCacheFn(f), + ~fn=fn(f), + t, + ) }) - switch samples { - | Ok(r) => #Dist(#SampleSet(r)) - | Error(e) => #Error(e) + } + } +} + +module OmniRunner = { + type params = { + sampleCount: int, + xyPointLength: int, + } + + let genericParams = { + sampleCount: 1000, + xyPointLength: 1000, + } + type wrapped = (genericDist, params) + + let wrapWithParams = (g: genericDist, f: params): wrapped => (g, f) + type outputType = [ + | #Dist(genericDist) + | #Error(error) + | #Float(float) + ] + + let fromResult = (r: result): outputType => + switch r { + | Ok(o) => o + | Error(e) => #Error(e) + } + + let rec applyFnInternal = (wrapped: wrapped, fnName: operation): outputType => { + let (value, {sampleCount, xyPointLength} as extra) = wrapped + let reCall = (~value=value, ~extra=extra, ~fnName=fnName, ()) => { + applyFnInternal((value, extra), fnName) + } + let toPointSet = r => { + switch reCall(~value=r, ~fnName=#toDist(#toPointSet), ()) { + | #Dist(#PointSet(p)) => Ok(p) + | #Error(r) => Error(r) + | _ => Error(Other("Impossible error")) } } - | (#toDistCombination(#Pointwise, operation, p2), p1) => - switch ( - toPointSet(p1), - toPointSet(p2) - ) { - | (Ok(p1), Ok(p2)) => - // TODO: If the dist is symbolic, then it doesn't need to be converted into a pointSet - #Dist(#PointSet(PointSetDist.combinePointwise(combinationToFn(operation), p1, p2))) - | (_, _) => #Error(Other("No Match or not supported")) + let toPointSetAndReCall = v => toPointSet(v) |> E.R.fmap(r => reCall(~value=#PointSet(r), ())) + let newVal: outputType = switch (fnName, value) { + // | (#toFloat(n), v) => toFloat(toPointSet, v, n) + | (#toFloat(fnName), _) => + T.toFloat(toPointSet, fnName, value) |> E.R.fmap(r => #Float(r)) |> fromResult + | (#toDist(#normalize), #PointSet(r)) => #Dist(#PointSet(PointSetDist.T.normalize(r))) + | (#toDist(#normalize), #Symbolic(_)) => #Dist(value) + | (#toDist(#normalize), #SampleSet(_)) => #Dist(value) + | (#toDist(#toPointSet), _) => + value |> T.toPointSet(xyPointLength) |> E.R.fmap(r => #Dist(#PointSet(r))) |> fromResult + | (#toDist(#toSampleSet(n)), _) => + value |> T.sampleN(n) |> E.R.fmap(r => #Dist(#SampleSet(r))) |> fromResult + | (#toDistCombination(#Algebraic, _, #Float(_)), _) => #Error(NotYetImplemented) + | (#toDistCombination(#Algebraic, operation, #Dist(p2)), p1) => + T.algebraicCombination(operation, sampleCount, p1, p2) + |> E.R.fmap(r => #Dist(r)) + |> fromResult + | (#toDistCombination(#Pointwise, operation, #Dist(p2)), p1) => + T.pointwiseCombination(toPointSet, operation, p1, p2) |> E.R.fmap(r => #Dist(r)) |> fromResult + | (#toDistCombination(#Pointwise, operation, #Float(f)), _) => + T.pointwiseCombinationFloat(toPointSet, operation, value, f) + |> E.R.fmap(r => #Dist(#PointSet(r))) + |> fromResult } - | _ => #Error(Other("No Match or not supported")) + newVal } - (newVal, {sampleCount: sampleCount, xyPointLength: xyPointLength}) } // let applyFn = (wrapped, fnName): wrapped => { @@ -237,4 +249,8 @@ let rec applyFnInternal = (wrapped: wrapped, fnName: operation): wrappedOutput = // } // } +// let exampleDist: genericDist = #PointSet( +// Discrete(Discrete.make(~integralSumCache=Some(1.0), {xs: [3.0], ys: [1.0]})), +// ) + // let foo = exampleDist->wrapWithParams(genericParams)->applyFn(#toDist(#normalize)) diff --git a/packages/squiggle-lang/src/rescript/utility/Operation.res b/packages/squiggle-lang/src/rescript/utility/Operation.res index 4eb2c3cd..540bd08c 100644 --- a/packages/squiggle-lang/src/rescript/utility/Operation.res +++ b/packages/squiggle-lang/src/rescript/utility/Operation.res @@ -7,10 +7,11 @@ type algebraicOperation = [ | #Subtract | #Divide | #Exponentiate + | #Log ] @genType type pointwiseOperation = [#Add | #Multiply | #Exponentiate] -type scaleOperation = [#Multiply | #Exponentiate | #Log] +type scaleOperation = [#Multiply | #Exponentiate | #Log | #Divide] type distToFloatOperation = [ | #Pdf(float) | #Cdf(float) @@ -28,6 +29,7 @@ module Algebraic = { | #Multiply => \"*." | #Exponentiate => \"**" | #Divide => \"/." + | #Log => (a, b) => log(a) /. log(b) } let applyFn = (t, f1, f2) => @@ -43,6 +45,7 @@ module Algebraic = { | #Multiply => "*" | #Exponentiate => "**" | #Divide => "/" + | #Log => "log" } let format = (a, b, c) => b ++ (" " ++ (toString(a) ++ (" " ++ c))) @@ -79,6 +82,7 @@ module Scale = { let toFn = x => switch x { | #Multiply => \"*." + | #Divide => \"/." | #Exponentiate => \"**" | #Log => (a, b) => log(a) /. log(b) } @@ -86,6 +90,7 @@ module Scale = { let format = (operation: t, value, scaleBy) => switch operation { | #Multiply => j`verticalMultiply($value, $scaleBy) ` + | #Divide => j`verticalDivide($value, $scaleBy) ` | #Exponentiate => j`verticalExponentiate($value, $scaleBy) ` | #Log => j`verticalLog($value, $scaleBy) ` } @@ -93,6 +98,7 @@ module Scale = { let toIntegralSumCacheFn = x => switch x { | #Multiply => (a, b) => Some(a *. b) + | #Divide => (a, b) => Some(a /. b) | #Exponentiate => (_, _) => None | #Log => (_, _) => None } @@ -100,6 +106,7 @@ module Scale = { let toIntegralCacheFn = x => switch x { | #Multiply => (_, _) => None // TODO: this could probably just be multiplied out (using Continuous.scaleBy) + | #Divide => (_, _) => None | #Exponentiate => (_, _) => None | #Log => (_, _) => None } From c5afb2d867ab6c440c636e600f6665198defa173 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Sat, 26 Mar 2022 16:56:56 -0400 Subject: [PATCH 07/41] Fleshed out AlgebraicCombination --- .../AlgebraicShapeCombination.res | 2 + .../rescript/pointSetDist/PointSetDist.res | 3 +- packages/squiggle-lang/src/rescript/sci.res | 117 ++++++++++++++---- 3 files changed, 96 insertions(+), 26 deletions(-) diff --git a/packages/squiggle-lang/src/rescript/pointSetDist/AlgebraicShapeCombination.res b/packages/squiggle-lang/src/rescript/pointSetDist/AlgebraicShapeCombination.res index e08d8887..3c298b18 100644 --- a/packages/squiggle-lang/src/rescript/pointSetDist/AlgebraicShapeCombination.res +++ b/packages/squiggle-lang/src/rescript/pointSetDist/AlgebraicShapeCombination.res @@ -115,6 +115,7 @@ let combineShapesContinuousContinuous = ( | #Multiply => (m1, m2) => m1 *. m2 | #Divide => (m1, mInv2) => m1 *. mInv2 | #Exponentiate => (m1, mInv2) => m1 ** mInv2 + | #Log => (m1, m2) => log(m1) /. log(m2) } // note: here, mInv2 = mean(1 / t2) ~= 1 / mean(t2) // TODO: I don't know what the variances are for exponentatiation @@ -232,6 +233,7 @@ let combineShapesContinuousDiscrete = ( } | #Multiply | #Exponentiate + | #Log | #Divide => for j in 0 to t2n - 1 { // creates a new continuous shape for each one of the discrete points, and collects them in outXYShapes. diff --git a/packages/squiggle-lang/src/rescript/pointSetDist/PointSetDist.res b/packages/squiggle-lang/src/rescript/pointSetDist/PointSetDist.res index 4a9eda9e..59bead6b 100644 --- a/packages/squiggle-lang/src/rescript/pointSetDist/PointSetDist.res +++ b/packages/squiggle-lang/src/rescript/pointSetDist/PointSetDist.res @@ -41,7 +41,8 @@ let combineAlgebraically = (op: Operation.algebraicOperation, t1: t, t2: t): t = | (Continuous(m1), Discrete(m2)) | (Discrete(m2), Continuous(m1)) => Continuous.combineAlgebraicallyWithDiscrete(op, m1, m2) |> Continuous.T.toPointSetDist - | (Discrete(m1), Discrete(m2)) => Discrete.combineAlgebraically(op, m1, m2) |> Discrete.T.toPointSetDist + | (Discrete(m1), Discrete(m2)) => + Discrete.combineAlgebraically(op, m1, m2) |> Discrete.T.toPointSetDist | (m1, m2) => Mixed.combineAlgebraically(op, toMixed(m1), toMixed(m2)) |> Mixed.T.toPointSetDist } diff --git a/packages/squiggle-lang/src/rescript/sci.res b/packages/squiggle-lang/src/rescript/sci.res index 66a5605a..ddcbf182 100644 --- a/packages/squiggle-lang/src/rescript/sci.res +++ b/packages/squiggle-lang/src/rescript/sci.res @@ -55,12 +55,6 @@ module OperationType = { | #Sample(int) ] - type scale = [ - | #Multiply - | #Exponentiate - | #Log - ] - type t = [ | #toFloat(toFloat) | #toDist(toDist) @@ -73,6 +67,7 @@ type operation = OperationType.t module T = { type t = genericDist type toPointSetFn = genericDist => result + type toSampleSetFn = genericDist => result, error> let sampleN = (n, t: t) => { switch t { | #PointSet(r) => Ok(PointSetDist.sampleNRendered(n, r)) @@ -81,7 +76,7 @@ module T = { } } - let toFloat = (toPointSet: toPointSetFn, fnName, t: genericDist) => { + let toFloat = (toPointSet: toPointSetFn, fnName, t: genericDist): result => { switch t { | #Symbolic(r) if Belt.Result.isOk(SymbolicDist.T.operate(fnName, r)) => switch SymbolicDist.T.operate(fnName, r) { @@ -104,7 +99,7 @@ module T = { kernelWidth: None, } - let toPointSet = (xyPointLength, t: t) => { + let toPointSet = (xyPointLength, t: t): result => { switch t { | #PointSet(pointSet) => Ok(pointSet) | #Symbolic(r) => Ok(SymbolicDist.T.toPointSetDist(xyPointLength, r)) @@ -122,16 +117,82 @@ module T = { } } - let algebraicCombination = (operation, sampleCount, dist1: t, dist2: t) => { - let dist1 = sampleN(sampleCount, dist1) - let dist2 = sampleN(sampleCount, dist2) - let samples = E.R.merge(dist1, dist2) |> E.R.fmap(((d1, d2)) => { - Belt.Array.zip(d1, d2) |> E.A.fmap(((a, b)) => Operation.Algebraic.toFn(operation, a, b)) - }) - samples |> E.R.fmap(r => #SampleSet(r)) + module AlgebraicCombination = { + let tryAnalyticalSimplification = (operation: OperationType.combination, t1: t, t2: t): option< + result, + > => + switch (operation, t1, t2) { + | (operation, #Symbolic(d1), #Symbolic(d2)) => + switch SymbolicDist.T.tryAnalyticalSimplification(d1, d2, operation) { + | #AnalyticalSolution(symbolicDist) => Some(Ok(symbolicDist)) + | #Error(er) => Some(Error(er)) + | #NoSolution => None + } + | _ => None + } + + let runConvolution = ( + toPointSet: toPointSetFn, + operation: OperationType.combination, + t1: t, + t2: t, + ) => + E.R.merge(toPointSet(t1), toPointSet(t2)) |> E.R.fmap(((a, b)) => + PointSetDist.combineAlgebraically(operation, a, b) + ) + + let runMonteCarlo = ( + toSampleSet: toSampleSetFn, + operation: OperationType.combination, + t1: t, + t2: t, + ) => { + E.R.merge(toSampleSet(t1), toSampleSet(t2)) |> E.R.fmap(((a, b)) => { + Belt.Array.zip(a, b) |> E.A.fmap(((a, b)) => Operation.Algebraic.toFn(operation, a, b)) + }) + } + + //I'm (Ozzie) really just guessing here, very little idea what's best + let expectedConvolutionCost: t => int = x => + switch x { + | #Symbolic(#Float(_)) => 1 + | #Symbolic(_) => 1000 + | #PointSet(Discrete(m)) => m.xyShape |> XYShape.T.length + | #PointSet(Mixed(_)) => 1000 + | #PointSet(Continuous(_)) => 1000 + | _ => 1000 + } + + let chooseConvolutionOrMonteCarlo = (t1: t, t2: t) => + expectedConvolutionCost(t1) * expectedConvolutionCost(t2) > 10000 + ? #CalculateWithMonteCarlo + : #CalculateWithConvolution + + let run = ( + toPointSet: toPointSetFn, + toSampleSet: toSampleSetFn, + algebraicOp, + t1: t, + t2: t, + ): result => { + switch tryAnalyticalSimplification(algebraicOp, t1, t2) { + | Some(Ok(symbolicDist)) => Ok(#Symbolic(symbolicDist)) + | Some(Error(e)) => Error(Other(e)) + | None => + switch chooseConvolutionOrMonteCarlo(t1, t2) { + | #CalculateWithMonteCarlo => + runMonteCarlo(toSampleSet, algebraicOp, t1, t2) |> E.R.fmap(r => #SampleSet(r)) + | #CalculateWithConvolution => + runConvolution(toPointSet, algebraicOp, t1, t2) |> E.R.fmap(r => #PointSet(r)) + } + } + } } - let pointwiseCombination = (toPointSet: toPointSetFn, operation, t1: t, t2: t) => { + let pointwiseCombination = (toPointSet: toPointSetFn, operation, t1: t, t2: t): result< + t, + error, + > => { E.R.merge(toPointSet(t1), toPointSet(t2)) |> E.R.fmap(((t1, t2)) => PointSetDist.combinePointwise(OperationType.combinationToFn(operation), t1, t2) @@ -144,11 +205,12 @@ module T = { operation: OperationType.combination, t: t, f: float, - ) => { + ): result => { switch operation { | #Add | #Subtract => Error(DistributionVerticalShiftIsInvalid) | (#Multiply | #Divide | #Exponentiate | #Log) as operation => toPointSet(t) |> E.R.fmap(t => { + //TODO: Move to PointSet codebase let fn = (secondary, main) => Operation.Scale.toFn(operation, main, secondary) let integralSumCacheFn = Operation.Scale.toIntegralSumCacheFn(operation) let integralCacheFn = Operation.Scale.toIntegralCacheFn(operation) @@ -159,7 +221,7 @@ module T = { t, ) }) - } + } |> E.R.fmap(r => #PointSet(r)) } } @@ -188,10 +250,10 @@ module OmniRunner = { | Error(e) => #Error(e) } - let rec applyFnInternal = (wrapped: wrapped, fnName: operation): outputType => { + let rec run = (wrapped: wrapped, fnName: operation): outputType => { let (value, {sampleCount, xyPointLength} as extra) = wrapped let reCall = (~value=value, ~extra=extra, ~fnName=fnName, ()) => { - applyFnInternal((value, extra), fnName) + run((value, extra), fnName) } let toPointSet = r => { switch reCall(~value=r, ~fnName=#toDist(#toPointSet), ()) { @@ -200,8 +262,14 @@ module OmniRunner = { | _ => Error(Other("Impossible error")) } } - let toPointSetAndReCall = v => toPointSet(v) |> E.R.fmap(r => reCall(~value=#PointSet(r), ())) - let newVal: outputType = switch (fnName, value) { + let toSampleSet = r => { + switch reCall(~value=r, ~fnName=#toDist(#toSampleSet(sampleCount)), ()) { + | #Dist(#SampleSet(p)) => Ok(p) + | #Error(r) => Error(r) + | _ => Error(Other("Impossible error")) + } + } + switch (fnName, value) { // | (#toFloat(n), v) => toFloat(toPointSet, v, n) | (#toFloat(fnName), _) => T.toFloat(toPointSet, fnName, value) |> E.R.fmap(r => #Float(r)) |> fromResult @@ -214,17 +282,16 @@ module OmniRunner = { value |> T.sampleN(n) |> E.R.fmap(r => #Dist(#SampleSet(r))) |> fromResult | (#toDistCombination(#Algebraic, _, #Float(_)), _) => #Error(NotYetImplemented) | (#toDistCombination(#Algebraic, operation, #Dist(p2)), p1) => - T.algebraicCombination(operation, sampleCount, p1, p2) + T.AlgebraicCombination.run(toPointSet, toSampleSet, operation, p1, p2) |> E.R.fmap(r => #Dist(r)) |> fromResult | (#toDistCombination(#Pointwise, operation, #Dist(p2)), p1) => T.pointwiseCombination(toPointSet, operation, p1, p2) |> E.R.fmap(r => #Dist(r)) |> fromResult | (#toDistCombination(#Pointwise, operation, #Float(f)), _) => T.pointwiseCombinationFloat(toPointSet, operation, value, f) - |> E.R.fmap(r => #Dist(#PointSet(r))) + |> E.R.fmap(r => #Dist(r)) |> fromResult } - newVal } } From 2ec1bfd068acf8d7d29562cb6420890d219e5458 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Sat, 26 Mar 2022 22:06:19 -0400 Subject: [PATCH 08/41] Added Truncate to sci.res --- packages/squiggle-lang/src/rescript/sci.res | 113 ++++++++++++++------ 1 file changed, 83 insertions(+), 30 deletions(-) diff --git a/packages/squiggle-lang/src/rescript/sci.res b/packages/squiggle-lang/src/rescript/sci.res index ddcbf182..bf6cc03e 100644 --- a/packages/squiggle-lang/src/rescript/sci.res +++ b/packages/squiggle-lang/src/rescript/sci.res @@ -1,3 +1,5 @@ +//TODO: multimodal, add interface, split up a little bit, test somehow, track performance, refactor sampleSet, refactor ASTEvaluator.res. + type error = | NeedsPointSetConversion | InputsNeedPointSetConversion @@ -18,7 +20,7 @@ module OperationType = { | #Pointwise ] - type combination = [ + type arithmeticOperation = [ | #Add | #Multiply | #Subtract @@ -27,8 +29,8 @@ module OperationType = { | #Log ] - let combinationToFn = (combination: combination) => - switch combination { + let arithmeticToFn = (arithmetic: arithmeticOperation) => + switch arithmetic { | #Add => \"+." | #Multiply => \"*." | #Subtract => \"-." @@ -49,6 +51,7 @@ module OperationType = { | #normalize | #toPointSet | #toSampleSet(int) + | #truncate(option, option) ] type toFloatArray = [ @@ -58,7 +61,7 @@ module OperationType = { type t = [ | #toFloat(toFloat) | #toDist(toDist) - | #toDistCombination(direction, combination, [#Dist(genericDist) | #Float(float)]) + | #toDistCombination(direction, arithmeticOperation, [#Dist(genericDist) | #Float(float)]) ] } @@ -76,6 +79,14 @@ module T = { } } + let normalize = (t: t) => { + switch t { + | #PointSet(r) => #PointSet(PointSetDist.T.normalize(r)) + | #Symbolic(_) => t + | #SampleSet(_) => t + } + } + let toFloat = (toPointSet: toPointSetFn, fnName, t: genericDist): result => { switch t { | #Symbolic(r) if Belt.Result.isOk(SymbolicDist.T.operate(fnName, r)) => @@ -117,10 +128,46 @@ module T = { } } + module Truncate = { + let trySymbolicSimplification = (leftCutoff, rightCutoff, t): option => + switch (leftCutoff, rightCutoff, t) { + | (None, None, _) => None + | (lc, rc, #Symbolic(#Uniform(u))) if lc < rc => + Some(#Symbolic(#Uniform(SymbolicDist.Uniform.truncate(lc, rc, u)))) + | _ => None + } + + let run = ( + toPointSet: toPointSetFn, + leftCutoff: option, + rightCutoff: option, + t: t, + ): result => { + let doesNotNeedCutoff = E.O.isNone(leftCutoff) && E.O.isNone(rightCutoff) + if doesNotNeedCutoff { + Ok(t) + } else { + switch trySymbolicSimplification(leftCutoff, rightCutoff, t) { + | Some(r) => Ok(r) + | None => + toPointSet(t) |> E.R.fmap(t => + #PointSet(PointSetDist.T.truncate(leftCutoff, rightCutoff, t)) + ) + } + } + } + } + + /* Given two random variables A and B, this returns the distribution + of a new variable that is the result of the operation on A and B. + For instance, normal(0, 1) + normal(1, 1) -> normal(1, 2). + In general, this is implemented via convolution. */ module AlgebraicCombination = { - let tryAnalyticalSimplification = (operation: OperationType.combination, t1: t, t2: t): option< - result, - > => + let tryAnalyticalSimplification = ( + operation: OperationType.arithmeticOperation, + t1: t, + t2: t, + ): option> => switch (operation, t1, t2) { | (operation, #Symbolic(d1), #Symbolic(d2)) => switch SymbolicDist.T.tryAnalyticalSimplification(d1, d2, operation) { @@ -133,7 +180,7 @@ module T = { let runConvolution = ( toPointSet: toPointSetFn, - operation: OperationType.combination, + operation: OperationType.arithmeticOperation, t1: t, t2: t, ) => @@ -143,7 +190,7 @@ module T = { let runMonteCarlo = ( toSampleSet: toSampleSetFn, - operation: OperationType.combination, + operation: OperationType.arithmeticOperation, t1: t, t2: t, ) => { @@ -163,7 +210,7 @@ module T = { | _ => 1000 } - let chooseConvolutionOrMonteCarlo = (t1: t, t2: t) => + let chooseConvolutionOrMonteCarlo = (t2: t, t1: t) => expectedConvolutionCost(t1) * expectedConvolutionCost(t2) > 10000 ? #CalculateWithMonteCarlo : #CalculateWithConvolution @@ -189,22 +236,23 @@ module T = { } } - let pointwiseCombination = (toPointSet: toPointSetFn, operation, t1: t, t2: t): result< + //TODO: Add faster pointwiseCombine fn + let pointwiseCombination = (toPointSet: toPointSetFn, operation, t2: t, t1: t): result< t, error, > => { E.R.merge(toPointSet(t1), toPointSet(t2)) |> E.R.fmap(((t1, t2)) => - PointSetDist.combinePointwise(OperationType.combinationToFn(operation), t1, t2) + PointSetDist.combinePointwise(OperationType.arithmeticToFn(operation), t1, t2) ) |> E.R.fmap(r => #PointSet(r)) } let pointwiseCombinationFloat = ( toPointSet: toPointSetFn, - operation: OperationType.combination, - t: t, + operation: OperationType.arithmeticOperation, f: float, + t: t, ): result => { switch operation { | #Add | #Subtract => Error(DistributionVerticalShiftIsInvalid) @@ -259,36 +307,41 @@ module OmniRunner = { switch reCall(~value=r, ~fnName=#toDist(#toPointSet), ()) { | #Dist(#PointSet(p)) => Ok(p) | #Error(r) => Error(r) - | _ => Error(Other("Impossible error")) + | _ => Error(ImpossiblePath) } } let toSampleSet = r => { switch reCall(~value=r, ~fnName=#toDist(#toSampleSet(sampleCount)), ()) { | #Dist(#SampleSet(p)) => Ok(p) | #Error(r) => Error(r) - | _ => Error(Other("Impossible error")) + | _ => Error(ImpossiblePath) } } - switch (fnName, value) { + switch fnName { // | (#toFloat(n), v) => toFloat(toPointSet, v, n) - | (#toFloat(fnName), _) => + | #toFloat(fnName) => T.toFloat(toPointSet, fnName, value) |> E.R.fmap(r => #Float(r)) |> fromResult - | (#toDist(#normalize), #PointSet(r)) => #Dist(#PointSet(PointSetDist.T.normalize(r))) - | (#toDist(#normalize), #Symbolic(_)) => #Dist(value) - | (#toDist(#normalize), #SampleSet(_)) => #Dist(value) - | (#toDist(#toPointSet), _) => + | #toDist(#normalize) => value |> T.normalize |> (r => #Dist(r)) + | #toDist(#truncate(left, right)) => + value |> T.Truncate.run(toPointSet, left, right) |> E.R.fmap(r => #Dist(r)) |> fromResult + | #toDist(#toPointSet) => value |> T.toPointSet(xyPointLength) |> E.R.fmap(r => #Dist(#PointSet(r))) |> fromResult - | (#toDist(#toSampleSet(n)), _) => + | #toDist(#toSampleSet(n)) => value |> T.sampleN(n) |> E.R.fmap(r => #Dist(#SampleSet(r))) |> fromResult - | (#toDistCombination(#Algebraic, _, #Float(_)), _) => #Error(NotYetImplemented) - | (#toDistCombination(#Algebraic, operation, #Dist(p2)), p1) => - T.AlgebraicCombination.run(toPointSet, toSampleSet, operation, p1, p2) + | #toDistCombination(#Algebraic, _, #Float(_)) => #Error(NotYetImplemented) + | #toDistCombination(#Algebraic, operation, #Dist(value2)) => + value + |> T.AlgebraicCombination.run(toPointSet, toSampleSet, operation, value2) |> E.R.fmap(r => #Dist(r)) |> fromResult - | (#toDistCombination(#Pointwise, operation, #Dist(p2)), p1) => - T.pointwiseCombination(toPointSet, operation, p1, p2) |> E.R.fmap(r => #Dist(r)) |> fromResult - | (#toDistCombination(#Pointwise, operation, #Float(f)), _) => - T.pointwiseCombinationFloat(toPointSet, operation, value, f) + | #toDistCombination(#Pointwise, operation, #Dist(value2)) => + value + |> T.pointwiseCombination(toPointSet, operation, value2) + |> E.R.fmap(r => #Dist(r)) + |> fromResult + | #toDistCombination(#Pointwise, operation, #Float(f)) => + value + |> T.pointwiseCombinationFloat(toPointSet, operation, f) |> E.R.fmap(r => #Dist(r)) |> fromResult } From c2ac9614d0921646e119943db6fced7b6a75aab0 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Sun, 27 Mar 2022 14:22:26 -0400 Subject: [PATCH 09/41] Refactored sci.res into multiple files --- .../src/rescript/GenericDist/GenericDist.res | 213 ++++++++++ .../GenericDist_GenericOperation.res | 78 ++++ .../GenericDist/GenericDist_Types.res | 63 +++ packages/squiggle-lang/src/rescript/sci.res | 376 ------------------ 4 files changed, 354 insertions(+), 376 deletions(-) create mode 100644 packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res create mode 100644 packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res create mode 100644 packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res delete mode 100644 packages/squiggle-lang/src/rescript/sci.res diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res new file mode 100644 index 00000000..a160e6a7 --- /dev/null +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res @@ -0,0 +1,213 @@ +//TODO: multimodal, add interface, split up a little bit, test somehow, track performance, refactor sampleSet, refactor ASTEvaluator.res. +type genericDist = GenericDist_Types.genericDist +type error = GenericDist_Types.error +type toPointSetFn = genericDist => result +type toSampleSetFn = genericDist => result, error> +type t = genericDist + +let sampleN = (n, t: t) => + switch t { + | #PointSet(r) => Ok(PointSetDist.sampleNRendered(n, r)) + | #Symbolic(r) => Ok(SymbolicDist.T.sampleN(n, r)) + | #SampleSet(_) => Error(GenericDist_Types.NotYetImplemented) + } + +let toString = (t: t) => + switch t { + | #PointSet(_) => "Point Set Distribution" + | #Symbolic(r) => SymbolicDist.T.toString(r) + | #SampleSet(_) => "Sample Set Distribution" + } + +let normalize = (t: t) => + switch t { + | #PointSet(r) => #PointSet(PointSetDist.T.normalize(r)) + | #Symbolic(_) => t + | #SampleSet(_) => t + } + + +let operationToFloat = (toPointSet: toPointSetFn, fnName, t: genericDist): result => { + let symbolicSolution = switch t { + | #Symbolic(r) => + switch SymbolicDist.T.operate(fnName, r) { + | Ok(f) => Some(f) + | _ => None + } + | _ => None + } + switch symbolicSolution { + | Some(r) => Ok(r) + | None => toPointSet(t) |> E.R.fmap(PointSetDist.operate(fnName)) + } +} + +//TODO: Refactor this bit. +let defaultSamplingInputs: SamplingInputs.samplingInputs = { + sampleCount: 10000, + outputXYPoints: 10000, + pointSetDistLength: 1000, + kernelWidth: None, +} + +let toPointSet = (xyPointLength, t: t): result => { + switch t { + | #PointSet(pointSet) => Ok(pointSet) + | #Symbolic(r) => Ok(SymbolicDist.T.toPointSetDist(xyPointLength, r)) + | #SampleSet(r) => { + let response = SampleSet.toPointSetDist( + ~samples=r, + ~samplingInputs=defaultSamplingInputs, + (), + ).pointSetDist + switch response { + | Some(r) => Ok(r) + | None => Error(Other("Converting sampleSet to pointSet failed")) + } + } + } +} + +module Truncate = { + let trySymbolicSimplification = (leftCutoff, rightCutoff, t): option => + switch (leftCutoff, rightCutoff, t) { + | (None, None, _) => None + | (lc, rc, #Symbolic(#Uniform(u))) if lc < rc => + Some(#Symbolic(#Uniform(SymbolicDist.Uniform.truncate(lc, rc, u)))) + | _ => None + } + + let run = ( + toPointSet: toPointSetFn, + leftCutoff: option, + rightCutoff: option, + t: t, + ): result => { + let doesNotNeedCutoff = E.O.isNone(leftCutoff) && E.O.isNone(rightCutoff) + if doesNotNeedCutoff { + Ok(t) + } else { + switch trySymbolicSimplification(leftCutoff, rightCutoff, t) { + | Some(r) => Ok(r) + | None => + toPointSet(t) |> E.R.fmap(t => + #PointSet(PointSetDist.T.truncate(leftCutoff, rightCutoff, t)) + ) + } + } + } +} + +/* Given two random variables A and B, this returns the distribution + of a new variable that is the result of the operation on A and B. + For instance, normal(0, 1) + normal(1, 1) -> normal(1, 2). + In general, this is implemented via convolution. */ +module AlgebraicCombination = { + let tryAnalyticalSimplification = ( + operation: GenericDist_Types.Operation.arithmeticOperation, + t1: t, + t2: t, + ): option> => + switch (operation, t1, t2) { + | (operation, #Symbolic(d1), #Symbolic(d2)) => + switch SymbolicDist.T.tryAnalyticalSimplification(d1, d2, operation) { + | #AnalyticalSolution(symbolicDist) => Some(Ok(symbolicDist)) + | #Error(er) => Some(Error(er)) + | #NoSolution => None + } + | _ => None + } + + let runConvolution = ( + toPointSet: toPointSetFn, + operation: GenericDist_Types.Operation.arithmeticOperation, + t1: t, + t2: t, + ) => + E.R.merge(toPointSet(t1), toPointSet(t2)) |> E.R.fmap(((a, b)) => + PointSetDist.combineAlgebraically(operation, a, b) + ) + + let runMonteCarlo = ( + toSampleSet: toSampleSetFn, + operation: GenericDist_Types.Operation.arithmeticOperation, + t1: t, + t2: t, + ) => { + E.R.merge(toSampleSet(t1), toSampleSet(t2)) |> E.R.fmap(((a, b)) => { + Belt.Array.zip(a, b) |> E.A.fmap(((a, b)) => Operation.Algebraic.toFn(operation, a, b)) + }) + } + + //I'm (Ozzie) really just guessing here, very little idea what's best + let expectedConvolutionCost: t => int = x => + switch x { + | #Symbolic(#Float(_)) => 1 + | #Symbolic(_) => 1000 + | #PointSet(Discrete(m)) => m.xyShape |> XYShape.T.length + | #PointSet(Mixed(_)) => 1000 + | #PointSet(Continuous(_)) => 1000 + | _ => 1000 + } + + let chooseConvolutionOrMonteCarlo = (t2: t, t1: t) => + expectedConvolutionCost(t1) * expectedConvolutionCost(t2) > 10000 + ? #CalculateWithMonteCarlo + : #CalculateWithConvolution + + let run = ( + toPointSet: toPointSetFn, + toSampleSet: toSampleSetFn, + algebraicOp, + t1: t, + t2: t, + ): result => { + switch tryAnalyticalSimplification(algebraicOp, t1, t2) { + | Some(Ok(symbolicDist)) => Ok(#Symbolic(symbolicDist)) + | Some(Error(e)) => Error(Other(e)) + | None => + switch chooseConvolutionOrMonteCarlo(t1, t2) { + | #CalculateWithMonteCarlo => + runMonteCarlo(toSampleSet, algebraicOp, t1, t2) |> E.R.fmap(r => #SampleSet(r)) + | #CalculateWithConvolution => + runConvolution(toPointSet, algebraicOp, t1, t2) |> E.R.fmap(r => #PointSet(r)) + } + } + } +} + +//TODO: Add faster pointwiseCombine fn +let pointwiseCombination = (toPointSet: toPointSetFn, operation, t2: t, t1: t): result< + t, + error, +> => { + E.R.merge(toPointSet(t1), toPointSet(t2)) + |> E.R.fmap(((t1, t2)) => + PointSetDist.combinePointwise(GenericDist_Types.Operation.arithmeticToFn(operation), t1, t2) + ) + |> E.R.fmap(r => #PointSet(r)) +} + +let pointwiseCombinationFloat = ( + toPointSet: toPointSetFn, + operation: GenericDist_Types.Operation.arithmeticOperation, + f: float, + t: t, +): result => { + switch operation { + | #Add | #Subtract => Error(GenericDist_Types.DistributionVerticalShiftIsInvalid) + | (#Multiply | #Divide | #Exponentiate | #Log) as operation => + toPointSet(t) |> E.R.fmap(t => { + //TODO: Move to PointSet codebase + let fn = (secondary, main) => Operation.Scale.toFn(operation, main, secondary) + let integralSumCacheFn = Operation.Scale.toIntegralSumCacheFn(operation) + let integralCacheFn = Operation.Scale.toIntegralCacheFn(operation) + PointSetDist.T.mapY( + ~integralSumCacheFn=integralSumCacheFn(f), + ~integralCacheFn=integralCacheFn(f), + ~fn=fn(f), + t, + ) + }) + } |> E.R.fmap(r => #PointSet(r)) +} diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res new file mode 100644 index 00000000..e03f3230 --- /dev/null +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res @@ -0,0 +1,78 @@ +type operation = GenericDist_Types.Operation.t +type genericDist = GenericDist_Types.genericDist; +type error = GenericDist_Types.error; + +type params = { + sampleCount: int, + xyPointLength: int, +} + +let genericParams = { + sampleCount: 1000, + xyPointLength: 1000, +} + +type wrapped = (genericDist, params) + +let wrapWithParams = (g: genericDist, f: params): wrapped => (g, f) +type outputType = [ + | #Dist(genericDist) + | #Error(error) + | #Float(float) +] + +let fromResult = (r: result): outputType => + switch r { + | Ok(o) => o + | Error(e) => #Error(e) + } + +let rec run = (wrapped: wrapped, fnName: operation): outputType => { + let (value, {sampleCount, xyPointLength} as extra) = wrapped + let reCall = (~value=value, ~extra=extra, ~fnName=fnName, ()) => { + run((value, extra), fnName) + } + let toPointSet = r => { + switch reCall(~value=r, ~fnName=#toDist(#toPointSet), ()) { + | #Dist(#PointSet(p)) => Ok(p) + | #Error(r) => Error(r) + | _ => Error(ImpossiblePath) + } + } + let toSampleSet = r => { + switch reCall(~value=r, ~fnName=#toDist(#toSampleSet(sampleCount)), ()) { + | #Dist(#SampleSet(p)) => Ok(p) + | #Error(r) => Error(r) + | _ => Error(ImpossiblePath) + } + } + switch fnName { + | #toFloat(fnName) => + GenericDist.operationToFloat(toPointSet, fnName, value) |> E.R.fmap(r => #Float(r)) |> fromResult + | #toString => + #Error(GenericDist_Types.NotYetImplemented) + | #toDist(#normalize) => value |> GenericDist.normalize |> (r => #Dist(r)) + | #toDist(#truncate(left, right)) => + value |> GenericDist.Truncate.run(toPointSet, left, right) |> E.R.fmap(r => #Dist(r)) |> fromResult + | #toDist(#toPointSet) => + value |> GenericDist.toPointSet(xyPointLength) |> E.R.fmap(r => #Dist(#PointSet(r))) |> fromResult + | #toDist(#toSampleSet(n)) => + value |> GenericDist.sampleN(n) |> E.R.fmap(r => #Dist(#SampleSet(r))) |> fromResult + | #toDistCombination(#Algebraic, _, #Float(_)) => #Error(NotYetImplemented) + | #toDistCombination(#Algebraic, operation, #Dist(value2)) => + value + |> GenericDist.AlgebraicCombination.run(toPointSet, toSampleSet, operation, value2) + |> E.R.fmap(r => #Dist(r)) + |> fromResult + | #toDistCombination(#Pointwise, operation, #Dist(value2)) => + value + |> GenericDist.pointwiseCombination(toPointSet, operation, value2) + |> E.R.fmap(r => #Dist(r)) + |> fromResult + | #toDistCombination(#Pointwise, operation, #Float(f)) => + value + |> GenericDist.pointwiseCombinationFloat(toPointSet, operation, f) + |> E.R.fmap(r => #Dist(r)) + |> fromResult + } +} diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res new file mode 100644 index 00000000..f6eb723f --- /dev/null +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res @@ -0,0 +1,63 @@ +type genericDist = [ + | #PointSet(PointSetTypes.pointSetDist) + | #SampleSet(array) + | #Symbolic(SymbolicDistTypes.symbolicDist) +] + +type error = + | NotYetImplemented + | ImpossiblePath + | DistributionVerticalShiftIsInvalid + | Other(string) + +module Operation = { + type direction = [ + | #Algebraic + | #Pointwise + ] + + type arithmeticOperation = [ + | #Add + | #Multiply + | #Subtract + | #Divide + | #Exponentiate + | #Log + ] + + let arithmeticToFn = (arithmetic: arithmeticOperation) => + switch arithmetic { + | #Add => \"+." + | #Multiply => \"*." + | #Subtract => \"-." + | #Exponentiate => \"**" + | #Divide => \"/." + | #Log => (a, b) => log(a) /. log(b) + } + + type toFloat = [ + | #Cdf(float) + | #Inv(float) + | #Mean + | #Pdf(float) + | #Sample + ] + + type toDist = [ + | #normalize + | #toPointSet + | #toSampleSet(int) + | #truncate(option, option) + ] + + type toFloatArray = [ + | #Sample(int) + ] + + type t = [ + | #toFloat(toFloat) + | #toDist(toDist) + | #toDistCombination(direction, arithmeticOperation, [#Dist(genericDist) | #Float(float)]) + | #toString + ] +} \ No newline at end of file diff --git a/packages/squiggle-lang/src/rescript/sci.res b/packages/squiggle-lang/src/rescript/sci.res deleted file mode 100644 index bf6cc03e..00000000 --- a/packages/squiggle-lang/src/rescript/sci.res +++ /dev/null @@ -1,376 +0,0 @@ -//TODO: multimodal, add interface, split up a little bit, test somehow, track performance, refactor sampleSet, refactor ASTEvaluator.res. - -type error = - | NeedsPointSetConversion - | InputsNeedPointSetConversion - | NotYetImplemented - | ImpossiblePath - | DistributionVerticalShiftIsInvalid - | Other(string) - -type genericDist = [ - | #PointSet(PointSetTypes.pointSetDist) - | #SampleSet(array) - | #Symbolic(SymbolicDistTypes.symbolicDist) -] - -module OperationType = { - type direction = [ - | #Algebraic - | #Pointwise - ] - - type arithmeticOperation = [ - | #Add - | #Multiply - | #Subtract - | #Divide - | #Exponentiate - | #Log - ] - - let arithmeticToFn = (arithmetic: arithmeticOperation) => - switch arithmetic { - | #Add => \"+." - | #Multiply => \"*." - | #Subtract => \"-." - | #Exponentiate => \"**" - | #Divide => \"/." - | #Log => (a, b) => log(a) /. log(b) - } - - type toFloat = [ - | #Cdf(float) - | #Inv(float) - | #Mean - | #Pdf(float) - | #Sample - ] - - type toDist = [ - | #normalize - | #toPointSet - | #toSampleSet(int) - | #truncate(option, option) - ] - - type toFloatArray = [ - | #Sample(int) - ] - - type t = [ - | #toFloat(toFloat) - | #toDist(toDist) - | #toDistCombination(direction, arithmeticOperation, [#Dist(genericDist) | #Float(float)]) - ] -} - -type operation = OperationType.t - -module T = { - type t = genericDist - type toPointSetFn = genericDist => result - type toSampleSetFn = genericDist => result, error> - let sampleN = (n, t: t) => { - switch t { - | #PointSet(r) => Ok(PointSetDist.sampleNRendered(n, r)) - | #Symbolic(r) => Ok(SymbolicDist.T.sampleN(n, r)) - | #SampleSet(_) => Error(NotYetImplemented) - } - } - - let normalize = (t: t) => { - switch t { - | #PointSet(r) => #PointSet(PointSetDist.T.normalize(r)) - | #Symbolic(_) => t - | #SampleSet(_) => t - } - } - - let toFloat = (toPointSet: toPointSetFn, fnName, t: genericDist): result => { - switch t { - | #Symbolic(r) if Belt.Result.isOk(SymbolicDist.T.operate(fnName, r)) => - switch SymbolicDist.T.operate(fnName, r) { - | Ok(float) => Ok(float) - | Error(_) => Error(ImpossiblePath) - } - | _ => - switch toPointSet(t) { - | Ok(r) => Ok(PointSetDist.operate(fnName, r)) - | Error(r) => Error(r) - } - } - } - - //TODO: Refactor this bit. - let defaultSamplingInputs: SamplingInputs.samplingInputs = { - sampleCount: 10000, - outputXYPoints: 10000, - pointSetDistLength: 1000, - kernelWidth: None, - } - - let toPointSet = (xyPointLength, t: t): result => { - switch t { - | #PointSet(pointSet) => Ok(pointSet) - | #Symbolic(r) => Ok(SymbolicDist.T.toPointSetDist(xyPointLength, r)) - | #SampleSet(r) => { - let response = SampleSet.toPointSetDist( - ~samples=r, - ~samplingInputs=defaultSamplingInputs, - (), - ).pointSetDist - switch response { - | Some(r) => Ok(r) - | None => Error(Other("Converting sampleSet to pointSet failed")) - } - } - } - } - - module Truncate = { - let trySymbolicSimplification = (leftCutoff, rightCutoff, t): option => - switch (leftCutoff, rightCutoff, t) { - | (None, None, _) => None - | (lc, rc, #Symbolic(#Uniform(u))) if lc < rc => - Some(#Symbolic(#Uniform(SymbolicDist.Uniform.truncate(lc, rc, u)))) - | _ => None - } - - let run = ( - toPointSet: toPointSetFn, - leftCutoff: option, - rightCutoff: option, - t: t, - ): result => { - let doesNotNeedCutoff = E.O.isNone(leftCutoff) && E.O.isNone(rightCutoff) - if doesNotNeedCutoff { - Ok(t) - } else { - switch trySymbolicSimplification(leftCutoff, rightCutoff, t) { - | Some(r) => Ok(r) - | None => - toPointSet(t) |> E.R.fmap(t => - #PointSet(PointSetDist.T.truncate(leftCutoff, rightCutoff, t)) - ) - } - } - } - } - - /* Given two random variables A and B, this returns the distribution - of a new variable that is the result of the operation on A and B. - For instance, normal(0, 1) + normal(1, 1) -> normal(1, 2). - In general, this is implemented via convolution. */ - module AlgebraicCombination = { - let tryAnalyticalSimplification = ( - operation: OperationType.arithmeticOperation, - t1: t, - t2: t, - ): option> => - switch (operation, t1, t2) { - | (operation, #Symbolic(d1), #Symbolic(d2)) => - switch SymbolicDist.T.tryAnalyticalSimplification(d1, d2, operation) { - | #AnalyticalSolution(symbolicDist) => Some(Ok(symbolicDist)) - | #Error(er) => Some(Error(er)) - | #NoSolution => None - } - | _ => None - } - - let runConvolution = ( - toPointSet: toPointSetFn, - operation: OperationType.arithmeticOperation, - t1: t, - t2: t, - ) => - E.R.merge(toPointSet(t1), toPointSet(t2)) |> E.R.fmap(((a, b)) => - PointSetDist.combineAlgebraically(operation, a, b) - ) - - let runMonteCarlo = ( - toSampleSet: toSampleSetFn, - operation: OperationType.arithmeticOperation, - t1: t, - t2: t, - ) => { - E.R.merge(toSampleSet(t1), toSampleSet(t2)) |> E.R.fmap(((a, b)) => { - Belt.Array.zip(a, b) |> E.A.fmap(((a, b)) => Operation.Algebraic.toFn(operation, a, b)) - }) - } - - //I'm (Ozzie) really just guessing here, very little idea what's best - let expectedConvolutionCost: t => int = x => - switch x { - | #Symbolic(#Float(_)) => 1 - | #Symbolic(_) => 1000 - | #PointSet(Discrete(m)) => m.xyShape |> XYShape.T.length - | #PointSet(Mixed(_)) => 1000 - | #PointSet(Continuous(_)) => 1000 - | _ => 1000 - } - - let chooseConvolutionOrMonteCarlo = (t2: t, t1: t) => - expectedConvolutionCost(t1) * expectedConvolutionCost(t2) > 10000 - ? #CalculateWithMonteCarlo - : #CalculateWithConvolution - - let run = ( - toPointSet: toPointSetFn, - toSampleSet: toSampleSetFn, - algebraicOp, - t1: t, - t2: t, - ): result => { - switch tryAnalyticalSimplification(algebraicOp, t1, t2) { - | Some(Ok(symbolicDist)) => Ok(#Symbolic(symbolicDist)) - | Some(Error(e)) => Error(Other(e)) - | None => - switch chooseConvolutionOrMonteCarlo(t1, t2) { - | #CalculateWithMonteCarlo => - runMonteCarlo(toSampleSet, algebraicOp, t1, t2) |> E.R.fmap(r => #SampleSet(r)) - | #CalculateWithConvolution => - runConvolution(toPointSet, algebraicOp, t1, t2) |> E.R.fmap(r => #PointSet(r)) - } - } - } - } - - //TODO: Add faster pointwiseCombine fn - let pointwiseCombination = (toPointSet: toPointSetFn, operation, t2: t, t1: t): result< - t, - error, - > => { - E.R.merge(toPointSet(t1), toPointSet(t2)) - |> E.R.fmap(((t1, t2)) => - PointSetDist.combinePointwise(OperationType.arithmeticToFn(operation), t1, t2) - ) - |> E.R.fmap(r => #PointSet(r)) - } - - let pointwiseCombinationFloat = ( - toPointSet: toPointSetFn, - operation: OperationType.arithmeticOperation, - f: float, - t: t, - ): result => { - switch operation { - | #Add | #Subtract => Error(DistributionVerticalShiftIsInvalid) - | (#Multiply | #Divide | #Exponentiate | #Log) as operation => - toPointSet(t) |> E.R.fmap(t => { - //TODO: Move to PointSet codebase - let fn = (secondary, main) => Operation.Scale.toFn(operation, main, secondary) - let integralSumCacheFn = Operation.Scale.toIntegralSumCacheFn(operation) - let integralCacheFn = Operation.Scale.toIntegralCacheFn(operation) - PointSetDist.T.mapY( - ~integralSumCacheFn=integralSumCacheFn(f), - ~integralCacheFn=integralCacheFn(f), - ~fn=fn(f), - t, - ) - }) - } |> E.R.fmap(r => #PointSet(r)) - } -} - -module OmniRunner = { - type params = { - sampleCount: int, - xyPointLength: int, - } - - let genericParams = { - sampleCount: 1000, - xyPointLength: 1000, - } - type wrapped = (genericDist, params) - - let wrapWithParams = (g: genericDist, f: params): wrapped => (g, f) - type outputType = [ - | #Dist(genericDist) - | #Error(error) - | #Float(float) - ] - - let fromResult = (r: result): outputType => - switch r { - | Ok(o) => o - | Error(e) => #Error(e) - } - - let rec run = (wrapped: wrapped, fnName: operation): outputType => { - let (value, {sampleCount, xyPointLength} as extra) = wrapped - let reCall = (~value=value, ~extra=extra, ~fnName=fnName, ()) => { - run((value, extra), fnName) - } - let toPointSet = r => { - switch reCall(~value=r, ~fnName=#toDist(#toPointSet), ()) { - | #Dist(#PointSet(p)) => Ok(p) - | #Error(r) => Error(r) - | _ => Error(ImpossiblePath) - } - } - let toSampleSet = r => { - switch reCall(~value=r, ~fnName=#toDist(#toSampleSet(sampleCount)), ()) { - | #Dist(#SampleSet(p)) => Ok(p) - | #Error(r) => Error(r) - | _ => Error(ImpossiblePath) - } - } - switch fnName { - // | (#toFloat(n), v) => toFloat(toPointSet, v, n) - | #toFloat(fnName) => - T.toFloat(toPointSet, fnName, value) |> E.R.fmap(r => #Float(r)) |> fromResult - | #toDist(#normalize) => value |> T.normalize |> (r => #Dist(r)) - | #toDist(#truncate(left, right)) => - value |> T.Truncate.run(toPointSet, left, right) |> E.R.fmap(r => #Dist(r)) |> fromResult - | #toDist(#toPointSet) => - value |> T.toPointSet(xyPointLength) |> E.R.fmap(r => #Dist(#PointSet(r))) |> fromResult - | #toDist(#toSampleSet(n)) => - value |> T.sampleN(n) |> E.R.fmap(r => #Dist(#SampleSet(r))) |> fromResult - | #toDistCombination(#Algebraic, _, #Float(_)) => #Error(NotYetImplemented) - | #toDistCombination(#Algebraic, operation, #Dist(value2)) => - value - |> T.AlgebraicCombination.run(toPointSet, toSampleSet, operation, value2) - |> E.R.fmap(r => #Dist(r)) - |> fromResult - | #toDistCombination(#Pointwise, operation, #Dist(value2)) => - value - |> T.pointwiseCombination(toPointSet, operation, value2) - |> E.R.fmap(r => #Dist(r)) - |> fromResult - | #toDistCombination(#Pointwise, operation, #Float(f)) => - value - |> T.pointwiseCombinationFloat(toPointSet, operation, f) - |> E.R.fmap(r => #Dist(r)) - |> fromResult - } - } -} - -// let applyFn = (wrapped, fnName): wrapped => { -// let (v, extra) as result = applyFnInternal(wrapped, fnName) -// switch v { -// | #Error(NeedsPointSetConversion) => { -// let convertedToPointSet = applyFnInternal(wrapped, #toDist(#toPointSet)) -// applyFnInternal(convertedToPointSet, fnName) -// } -// | #Error(InputsNeedPointSetConversion) => { -// let altDist = switch fnName { -// | #toDistCombination(p1, p2, dist) => { -// let (newDist, _) = applyFnInternal((dist, extra), #toDist(#toPointSet)) -// applyFnInternal(wrapped, #toDistCombination(p1, p2, newDist)) -// } -// | _ => (#Error(Other("Not needed")), extra) -// } -// altDist -// } -// | _ => result -// } -// } - -// let exampleDist: genericDist = #PointSet( -// Discrete(Discrete.make(~integralSumCache=Some(1.0), {xs: [3.0], ys: [1.0]})), -// ) - -// let foo = exampleDist->wrapWithParams(genericParams)->applyFn(#toDist(#normalize)) From b70e8e02e1010485022e8d701c415f4a77b10b07 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Sun, 27 Mar 2022 16:59:46 -0400 Subject: [PATCH 10/41] Refactor GenericOperation to allow for operations other than toDist operations --- .../src/rescript/GenericDist/GenericDist.res | 5 +- .../GenericDist_GenericOperation.res | 92 +++++++++++-------- .../GenericDist/GenericDist_Types.res | 26 +++++- 3 files changed, 84 insertions(+), 39 deletions(-) diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res index a160e6a7..0dc2dd7c 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res @@ -1,4 +1,4 @@ -//TODO: multimodal, add interface, split up a little bit, test somehow, track performance, refactor sampleSet, refactor ASTEvaluator.res. +//TODO: multimodal, add interface, test somehow, track performance, refactor sampleSet, refactor ASTEvaluator.res. type genericDist = GenericDist_Types.genericDist type error = GenericDist_Types.error type toPointSetFn = genericDist => result @@ -26,6 +26,8 @@ let normalize = (t: t) => | #SampleSet(_) => t } +// let isNormalized = (t:t) => + let operationToFloat = (toPointSet: toPointSetFn, fnName, t: genericDist): result => { let symbolicSolution = switch t { @@ -36,6 +38,7 @@ let operationToFloat = (toPointSet: toPointSetFn, fnName, t: genericDist): resul } | _ => None } + switch symbolicSolution { | Some(r) => Ok(r) | None => toPointSet(t) |> E.R.fmap(PointSetDist.operate(fnName)) diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res index e03f3230..17e5ace6 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res @@ -1,6 +1,8 @@ -type operation = GenericDist_Types.Operation.t -type genericDist = GenericDist_Types.genericDist; -type error = GenericDist_Types.error; +type operation = GenericDist_Types.Operation.genericFunction +type genericDist = GenericDist_Types.genericDist +type error = GenericDist_Types.error + +// TODO: It could be great to use a cache for some calculations (basically, do memoization). Also, better analytics/tracking could go a long way. type params = { sampleCount: int, @@ -27,52 +29,70 @@ let fromResult = (r: result): outputType => | Error(e) => #Error(e) } -let rec run = (wrapped: wrapped, fnName: operation): outputType => { - let (value, {sampleCount, xyPointLength} as extra) = wrapped - let reCall = (~value=value, ~extra=extra, ~fnName=fnName, ()) => { - run((value, extra), fnName) +let rec run = (extra, fnName: operation): outputType => { + let {sampleCount, xyPointLength} = extra + let reCall = (~extra=extra, ~fnName=fnName, ()) => { + run(extra, fnName) } let toPointSet = r => { - switch reCall(~value=r, ~fnName=#toDist(#toPointSet), ()) { + switch reCall(~fnName=#fromDist(#toDist(#toPointSet), r), ()) { | #Dist(#PointSet(p)) => Ok(p) | #Error(r) => Error(r) | _ => Error(ImpossiblePath) } } let toSampleSet = r => { - switch reCall(~value=r, ~fnName=#toDist(#toSampleSet(sampleCount)), ()) { + switch reCall(~fnName=#fromDist(#toDist(#toSampleSet(sampleCount)), r), ()) { | #Dist(#SampleSet(p)) => Ok(p) | #Error(r) => Error(r) | _ => Error(ImpossiblePath) } } + + let fromDistFn = (subFn: GenericDist_Types.Operation.fromDist, dist: genericDist) => + switch subFn { + | #toFloat(fnName) => + GenericDist.operationToFloat(toPointSet, fnName, dist) + |> E.R.fmap(r => #Float(r)) + |> fromResult + | #toString => #Error(GenericDist_Types.NotYetImplemented) + | #toDist(#normalize) => dist |> GenericDist.normalize |> (r => #Dist(r)) + | #toDist(#truncate(left, right)) => + dist + |> GenericDist.Truncate.run(toPointSet, left, right) + |> E.R.fmap(r => #Dist(r)) + |> fromResult + | #toDist(#toPointSet) => + dist + |> GenericDist.toPointSet(xyPointLength) + |> E.R.fmap(r => #Dist(#PointSet(r))) + |> fromResult + | #toDist(#toSampleSet(n)) => + dist |> GenericDist.sampleN(n) |> E.R.fmap(r => #Dist(#SampleSet(r))) |> fromResult + | #toDistCombination(#Algebraic, _, #Float(_)) => #Error(NotYetImplemented) + | #toDistCombination(#Algebraic, operation, #Dist(dist2)) => + dist + |> GenericDist.AlgebraicCombination.run(toPointSet, toSampleSet, operation, dist2) + |> E.R.fmap(r => #Dist(r)) + |> fromResult + | #toDistCombination(#Pointwise, operation, #Dist(dist2)) => + dist + |> GenericDist.pointwiseCombination(toPointSet, operation, dist2) + |> E.R.fmap(r => #Dist(r)) + |> fromResult + | #toDistCombination(#Pointwise, operation, #Float(f)) => + dist + |> GenericDist.pointwiseCombinationFloat(toPointSet, operation, f) + |> E.R.fmap(r => #Dist(r)) + |> fromResult + } + switch fnName { - | #toFloat(fnName) => - GenericDist.operationToFloat(toPointSet, fnName, value) |> E.R.fmap(r => #Float(r)) |> fromResult - | #toString => - #Error(GenericDist_Types.NotYetImplemented) - | #toDist(#normalize) => value |> GenericDist.normalize |> (r => #Dist(r)) - | #toDist(#truncate(left, right)) => - value |> GenericDist.Truncate.run(toPointSet, left, right) |> E.R.fmap(r => #Dist(r)) |> fromResult - | #toDist(#toPointSet) => - value |> GenericDist.toPointSet(xyPointLength) |> E.R.fmap(r => #Dist(#PointSet(r))) |> fromResult - | #toDist(#toSampleSet(n)) => - value |> GenericDist.sampleN(n) |> E.R.fmap(r => #Dist(#SampleSet(r))) |> fromResult - | #toDistCombination(#Algebraic, _, #Float(_)) => #Error(NotYetImplemented) - | #toDistCombination(#Algebraic, operation, #Dist(value2)) => - value - |> GenericDist.AlgebraicCombination.run(toPointSet, toSampleSet, operation, value2) - |> E.R.fmap(r => #Dist(r)) - |> fromResult - | #toDistCombination(#Pointwise, operation, #Dist(value2)) => - value - |> GenericDist.pointwiseCombination(toPointSet, operation, value2) - |> E.R.fmap(r => #Dist(r)) - |> fromResult - | #toDistCombination(#Pointwise, operation, #Float(f)) => - value - |> GenericDist.pointwiseCombinationFloat(toPointSet, operation, f) - |> E.R.fmap(r => #Dist(r)) - |> fromResult + | #fromDist(subFn, dist) => fromDistFn(subFn, dist) + | #fromFloat(subFn, float) => reCall( + ~fnName=#fromDist(subFn, #Symbolic(SymbolicDist.Float.make(float))), + (), + ) + | _ => #Error(NotYetImplemented) } } diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res index f6eb723f..a870d557 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res @@ -54,10 +54,32 @@ module Operation = { | #Sample(int) ] - type t = [ + type fromDist = [ | #toFloat(toFloat) | #toDist(toDist) | #toDistCombination(direction, arithmeticOperation, [#Dist(genericDist) | #Float(float)]) | #toString ] -} \ No newline at end of file + + type genericFunction = [ + | #fromDist(fromDist, genericDist) + | #fromFloat(fromDist, float) + | #mixture(array<(genericDist, float)>) + ] + + let toString = (distFunction: fromDist): string => + switch distFunction { + | #toFloat(#Cdf(r)) => `cdf(${E.Float.toFixed(r)})` + | #toFloat(#Inv(r)) => `inv(${E.Float.toFixed(r)})` + | #toFloat(#Mean) => `mean` + | #toFloat(#Pdf(r)) => `pdf${E.Float.toFixed(r)}` + | #toFloat(#Sample) => `sample` + | #toDist(#normalize) => `normalize` + | #toDist(#toPointSet) => `toPointSet` + | #toDist(#toSampleSet(r)) => `toSampleSet${E.I.toString(r)}` + | #toDist(#truncate(_, _)) => `truncate` + | #toString => `toString` + | #toDistCombination(#Algebraic, _, _) => `algebraic` + | #toDistCombination(#Pointwise, _, _) => `pointwise` + } +} From 3f678e24a18c02a11a377cab27d1ffa53a362928 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Sun, 27 Mar 2022 17:37:27 -0400 Subject: [PATCH 11/41] Added mixture fn for generic distributions --- .../src/rescript/GenericDist/GenericDist.res | 26 +++++++++++++--- .../GenericDist_GenericOperation.res | 30 +++++++++++++++---- 2 files changed, 47 insertions(+), 9 deletions(-) diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res index 0dc2dd7c..0bdf004d 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res @@ -5,13 +5,15 @@ type toPointSetFn = genericDist => result type toSampleSetFn = genericDist => result, error> type t = genericDist -let sampleN = (n, t: t) => +let sampleN = (n, t: t) => switch t { | #PointSet(r) => Ok(PointSetDist.sampleNRendered(n, r)) | #Symbolic(r) => Ok(SymbolicDist.T.sampleN(n, r)) | #SampleSet(_) => Error(GenericDist_Types.NotYetImplemented) } +let fromFloat = (f: float) => #Symbolic(SymbolicDist.Float.make(f)) + let toString = (t: t) => switch t { | #PointSet(_) => "Point Set Distribution" @@ -19,15 +21,14 @@ let toString = (t: t) => | #SampleSet(_) => "Sample Set Distribution" } -let normalize = (t: t) => +let normalize = (t: t) => switch t { | #PointSet(r) => #PointSet(PointSetDist.T.normalize(r)) | #Symbolic(_) => t | #SampleSet(_) => t } -// let isNormalized = (t:t) => - +// let isNormalized = (t:t) => let operationToFloat = (toPointSet: toPointSetFn, fnName, t: genericDist): result => { let symbolicSolution = switch t { @@ -214,3 +215,20 @@ let pointwiseCombinationFloat = ( }) } |> E.R.fmap(r => #PointSet(r)) } + +let mixture = ( + scaleMultiply: (genericDist, float) => result, + pointwiseAdd: (genericDist, genericDist) => result, + values: array<(genericDist, float)>, +) => { + let properlyWeightedValues = + values |> E.A.fmap(((dist, weight)) => scaleMultiply(dist, weight)) |> E.A.R.firstErrorOrOpen + properlyWeightedValues |> E.R.bind(_, values => { + values + |> Js.Array.sliceFrom(1) + |> E.A.fold_left( + (acc, x) => E.R.bind(acc, acc => pointwiseAdd(acc, x)), + Ok(E.A.unsafe_get(values, 0)), + ) + }) +} diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res index 17e5ace6..9958051c 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res @@ -29,11 +29,20 @@ let fromResult = (r: result): outputType => | Error(e) => #Error(e) } +let outputToDistResult = (b: outputType): result => + switch b { + | #Dist(r) => Ok(r) + | #Error(r) => Error(r) + | _ => Error(ImpossiblePath) + } + let rec run = (extra, fnName: operation): outputType => { let {sampleCount, xyPointLength} = extra + let reCall = (~extra=extra, ~fnName=fnName, ()) => { run(extra, fnName) } + let toPointSet = r => { switch reCall(~fnName=#fromDist(#toDist(#toPointSet), r), ()) { | #Dist(#PointSet(p)) => Ok(p) @@ -41,6 +50,7 @@ let rec run = (extra, fnName: operation): outputType => { | _ => Error(ImpossiblePath) } } + let toSampleSet = r => { switch reCall(~fnName=#fromDist(#toDist(#toSampleSet(sampleCount)), r), ()) { | #Dist(#SampleSet(p)) => Ok(p) @@ -49,6 +59,18 @@ let rec run = (extra, fnName: operation): outputType => { } } + let scaleMultiply = (r, weight) => + reCall( + ~fnName=#fromDist(#toDistCombination(#Pointwise, #Multiply, #Float(weight)), r), + (), + ) |> outputToDistResult + + let pointwiseAdd = (r1, r2) => + reCall( + ~fnName=#fromDist(#toDistCombination(#Pointwise, #Add, #Dist(r2)), r1), + (), + ) |> outputToDistResult + let fromDistFn = (subFn: GenericDist_Types.Operation.fromDist, dist: genericDist) => switch subFn { | #toFloat(fnName) => @@ -89,10 +111,8 @@ let rec run = (extra, fnName: operation): outputType => { switch fnName { | #fromDist(subFn, dist) => fromDistFn(subFn, dist) - | #fromFloat(subFn, float) => reCall( - ~fnName=#fromDist(subFn, #Symbolic(SymbolicDist.Float.make(float))), - (), - ) - | _ => #Error(NotYetImplemented) + | #fromFloat(subFn, float) => reCall(~fnName=#fromDist(subFn, GenericDist.fromFloat(float)), ()) + | #mixture(dists) => + GenericDist.mixture(scaleMultiply, pointwiseAdd, dists) |> E.R.fmap(r => #Dist(r)) |> fromResult } } From 80b33fcd84a70c2aa5d41689fc368d01c2462445 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Sun, 27 Mar 2022 21:07:41 -0400 Subject: [PATCH 12/41] Added README to GenericDist library --- .../src/rescript/GenericDist/GenericDist.res | 2 -- .../GenericDist_GenericOperation.res | 3 +- .../GenericDist/GenericDist_Types.res | 2 +- .../src/rescript/GenericDist/README.md | 32 +++++++++++++++++++ 4 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 packages/squiggle-lang/src/rescript/GenericDist/README.md diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res index 0bdf004d..84259de3 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res @@ -28,8 +28,6 @@ let normalize = (t: t) => | #SampleSet(_) => t } -// let isNormalized = (t:t) => - let operationToFloat = (toPointSet: toPointSetFn, fnName, t: genericDist): result => { let symbolicSolution = switch t { | #Symbolic(r) => diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res index 9958051c..dd4bcd5d 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res @@ -21,6 +21,7 @@ type outputType = [ | #Dist(genericDist) | #Error(error) | #Float(float) + | #String(string) ] let fromResult = (r: result): outputType => @@ -77,7 +78,7 @@ let rec run = (extra, fnName: operation): outputType => { GenericDist.operationToFloat(toPointSet, fnName, dist) |> E.R.fmap(r => #Float(r)) |> fromResult - | #toString => #Error(GenericDist_Types.NotYetImplemented) + | #toString => dist |> GenericDist.toString |> (r => #String(r)) | #toDist(#normalize) => dist |> GenericDist.normalize |> (r => #Dist(r)) | #toDist(#truncate(left, right)) => dist diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res index a870d557..321b3a6c 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res @@ -82,4 +82,4 @@ module Operation = { | #toDistCombination(#Algebraic, _, _) => `algebraic` | #toDistCombination(#Pointwise, _, _) => `pointwise` } -} +} \ No newline at end of file diff --git a/packages/squiggle-lang/src/rescript/GenericDist/README.md b/packages/squiggle-lang/src/rescript/GenericDist/README.md new file mode 100644 index 00000000..9a3ca6ce --- /dev/null +++ b/packages/squiggle-lang/src/rescript/GenericDist/README.md @@ -0,0 +1,32 @@ +# Generic Distribution Library + +This library provides one interface to generic distributions. These distributions can either be symbolic, point set (xy-coordinates of the shape), or sample set (arrays of random samples). + +Different internal formats (symbolic, point set, sample set) allow for benefits and features. It's common for distributions to be converted into either point sets or sample sets to enable certain functions. + +In addition to this interface, there's a second, generic function, for calling functions on this generic distribution type. This ``genericOperation`` standardizes the inputs and outputs for these various function calls. See it's ``run()`` function. + +Performance is very important. Some operations can take a long time to run, and even then, be inaccurate. Because of this, we plan to have a lot of logging and stack tracing functionality eventually built in. + +## Diagram of Distribution Types +```mermaid +graph TD + A[Generic Distribution] -->B{Point Set} + A --> C{Sample Set} + A --> D{Symbolic} + B ---> continuous(Continuous) + B ---> discrete(Discrete) + B --> mixed(Mixed) + continuous -.-> XYshape(XYshape) + discrete -.-> XYshape(XYshape) + mixed -.-> continuous + mixed -.-> discrete + D --> Normal(Normal) + D --> Lognormal(Lognormal) + D --> Triangular(Triangular) + D --> Beta(Beta) + D --> Uniform(Uniform) + D --> Float(Float) + D --> Exponential(Exponential) + D --> Cauchy(Cauchy) +``` \ No newline at end of file From 04abeb7cd10d5081d72d93de0a7b97cbae4908b7 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Mon, 28 Mar 2022 07:56:20 -0400 Subject: [PATCH 13/41] Added todo list to GenericDist repo --- .../src/rescript/GenericDist/GenericDist.res | 31 ++++++++++++------- .../src/rescript/GenericDist/README.md | 15 ++++++++- .../rescript/pointSetDist/PointSetDist.res | 1 + .../rescript/symbolicDist/SymbolicDist.res | 1 + 4 files changed, 36 insertions(+), 12 deletions(-) diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res index 84259de3..7cb714e6 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res @@ -100,6 +100,8 @@ module Truncate = { } } +let truncate = Truncate.run + /* Given two random variables A and B, this returns the distribution of a new variable that is the result of the operation on A and B. For instance, normal(0, 1) + normal(1, 1) -> normal(1, 2). @@ -136,8 +138,9 @@ module AlgebraicCombination = { t1: t, t2: t, ) => { + let operation = Operation.Algebraic.toFn(operation) E.R.merge(toSampleSet(t1), toSampleSet(t2)) |> E.R.fmap(((a, b)) => { - Belt.Array.zip(a, b) |> E.A.fmap(((a, b)) => Operation.Algebraic.toFn(operation, a, b)) + Belt.Array.zip(a, b) |> E.A.fmap(((a, b)) => operation(a, b)) }) } @@ -178,6 +181,8 @@ module AlgebraicCombination = { } } +let algebraicCombination = AlgebraicCombination.run + //TODO: Add faster pointwiseCombine fn let pointwiseCombination = (toPointSet: toPointSetFn, operation, t2: t, t1: t): result< t, @@ -219,14 +224,18 @@ let mixture = ( pointwiseAdd: (genericDist, genericDist) => result, values: array<(genericDist, float)>, ) => { - let properlyWeightedValues = - values |> E.A.fmap(((dist, weight)) => scaleMultiply(dist, weight)) |> E.A.R.firstErrorOrOpen - properlyWeightedValues |> E.R.bind(_, values => { - values - |> Js.Array.sliceFrom(1) - |> E.A.fold_left( - (acc, x) => E.R.bind(acc, acc => pointwiseAdd(acc, x)), - Ok(E.A.unsafe_get(values, 0)), - ) - }) + if E.A.length(values) == 0 { + Error(GenericDist_Types.Other("mixture must have at least 1 element")) + } else { + let properlyWeightedValues = + values |> E.A.fmap(((dist, weight)) => scaleMultiply(dist, weight)) |> E.A.R.firstErrorOrOpen + properlyWeightedValues |> E.R.bind(_, values => { + values + |> Js.Array.sliceFrom(1) + |> E.A.fold_left( + (acc, x) => E.R.bind(acc, acc => pointwiseAdd(acc, x)), + Ok(E.A.unsafe_get(values, 0)), + ) + }) + } } diff --git a/packages/squiggle-lang/src/rescript/GenericDist/README.md b/packages/squiggle-lang/src/rescript/GenericDist/README.md index 9a3ca6ce..382de2b9 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/README.md +++ b/packages/squiggle-lang/src/rescript/GenericDist/README.md @@ -29,4 +29,17 @@ graph TD D --> Float(Float) D --> Exponential(Exponential) D --> Cauchy(Cauchy) -``` \ No newline at end of file +``` + +## Todo +- [ ] Lots of cleanup +- [ ] Add interface files +- [ ] Simple test story +- [ ] Provide decent stack traces for key calls in GenericOperation. This could be very useful for debugging. +- [ ] Cleanup Sample Set library +- [ ] Add memoization for calculations +- [ ] Performance bechmarking reports +- [ ] Remove most of DistPlus, much of which is not needed anymore +- [ ] More functions for Sample Set, which is very minimal now +- [ ] Allow these functions to be run on web workers +- [ ] Refactor interpreter to use GenericDist. This might not be necessary, as the new reducer-inspired interpreter is integrated. \ No newline at end of file diff --git a/packages/squiggle-lang/src/rescript/pointSetDist/PointSetDist.res b/packages/squiggle-lang/src/rescript/pointSetDist/PointSetDist.res index 59bead6b..834b244f 100644 --- a/packages/squiggle-lang/src/rescript/pointSetDist/PointSetDist.res +++ b/packages/squiggle-lang/src/rescript/pointSetDist/PointSetDist.res @@ -34,6 +34,7 @@ let toMixed = mapToAll(( ), )) +//TODO WARNING: The combineAlgebraicallyWithDiscrete will break for subtraction and division, like, discrete - continous let combineAlgebraically = (op: Operation.algebraicOperation, t1: t, t2: t): t => switch (t1, t2) { | (Continuous(m1), Continuous(m2)) => diff --git a/packages/squiggle-lang/src/rescript/symbolicDist/SymbolicDist.res b/packages/squiggle-lang/src/rescript/symbolicDist/SymbolicDist.res index 3da6bd02..d31a8393 100644 --- a/packages/squiggle-lang/src/rescript/symbolicDist/SymbolicDist.res +++ b/packages/squiggle-lang/src/rescript/symbolicDist/SymbolicDist.res @@ -145,6 +145,7 @@ module Uniform = { let mean = (t: t) => Ok(Jstat.Uniform.mean(t.low, t.high)) let toString = ({low, high}: t) => j`Uniform($low,$high)` let truncate = (low, high, t: t): t => { +//todo: add check let newLow = max(E.O.default(neg_infinity, low), t.low) let newHigh = min(E.O.default(infinity, high), t.high) {low: newLow, high: newHigh} From 6f11e87c0a4e2ca549868e419b681041dc4c625e Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Mon, 28 Mar 2022 08:39:07 -0400 Subject: [PATCH 14/41] Added simple interface files --- .../src/rescript/GenericDist/GenericDist.res | 17 +++---- .../src/rescript/GenericDist/GenericDist.resi | 44 +++++++++++++++++++ .../GenericDist_GenericOperation.res | 14 ++---- .../GenericDist_GenericOperation.resi | 13 ++++++ .../GenericDist/GenericDist_Types.res | 2 +- .../src/rescript/GenericDist/README.md | 1 - 6 files changed, 70 insertions(+), 21 deletions(-) create mode 100644 packages/squiggle-lang/src/rescript/GenericDist/GenericDist.resi create mode 100644 packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res index 7cb714e6..0b8d7717 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res @@ -1,9 +1,10 @@ //TODO: multimodal, add interface, test somehow, track performance, refactor sampleSet, refactor ASTEvaluator.res. -type genericDist = GenericDist_Types.genericDist +type t = GenericDist_Types.genericDist type error = GenericDist_Types.error -type toPointSetFn = genericDist => result -type toSampleSetFn = genericDist => result, error> -type t = genericDist +type toPointSetFn = t => result +type toSampleSetFn = t => result, error> +type scaleMultiplyFn = (t, float) => result +type pointwiseAddFn = (t, t) => result let sampleN = (n, t: t) => switch t { @@ -28,7 +29,7 @@ let normalize = (t: t) => | #SampleSet(_) => t } -let operationToFloat = (toPointSet: toPointSetFn, fnName, t: genericDist): result => { +let operationToFloat = (toPointSet: toPointSetFn, fnName, t) => { let symbolicSolution = switch t { | #Symbolic(r) => switch SymbolicDist.T.operate(fnName, r) { @@ -220,9 +221,9 @@ let pointwiseCombinationFloat = ( } let mixture = ( - scaleMultiply: (genericDist, float) => result, - pointwiseAdd: (genericDist, genericDist) => result, - values: array<(genericDist, float)>, + scaleMultiply: scaleMultiplyFn, + pointwiseAdd: pointwiseAddFn, + values: array<(t, float)>, ) => { if E.A.length(values) == 0 { Error(GenericDist_Types.Other("mixture must have at least 1 element")) diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.resi b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.resi new file mode 100644 index 00000000..0f8734e0 --- /dev/null +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.resi @@ -0,0 +1,44 @@ +type t = GenericDist_Types.genericDist +type error = GenericDist_Types.error +type toPointSetFn = t => result +type toSampleSetFn = t => result, error> +type scaleMultiplyFn = (t, float) => result +type pointwiseAddFn = (t, t) => result + +let sampleN: (int, t) => result, error> + +let fromFloat: float => t + +let toString: t => string + +let normalize: t => t + +let operationToFloat: (toPointSetFn, Operation.distToFloatOperation, t) => result + +let toPointSet: (int, t) => result + +let truncate: (toPointSetFn, option, option, t) => result + +let algebraicCombination: ( + toPointSetFn, + toSampleSetFn, + GenericDist_Types.Operation.arithmeticOperation, + t, + t, +) => result + +let pointwiseCombination: ( + toPointSetFn, + GenericDist_Types.Operation.arithmeticOperation, + t, + t, +) => result + +let pointwiseCombinationFloat: ( + toPointSetFn, + GenericDist_Types.Operation.arithmeticOperation, + float, + t, +) => result + +let mixture: (scaleMultiplyFn, pointwiseAddFn, array<(t, float)>) => result diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res index dd4bcd5d..cc542ef3 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res @@ -1,4 +1,4 @@ -type operation = GenericDist_Types.Operation.genericFunction +type operation = GenericDist_Types.Operation.genericFunctionCall type genericDist = GenericDist_Types.genericDist type error = GenericDist_Types.error @@ -9,14 +9,6 @@ type params = { xyPointLength: int, } -let genericParams = { - sampleCount: 1000, - xyPointLength: 1000, -} - -type wrapped = (genericDist, params) - -let wrapWithParams = (g: genericDist, f: params): wrapped => (g, f) type outputType = [ | #Dist(genericDist) | #Error(error) @@ -82,7 +74,7 @@ let rec run = (extra, fnName: operation): outputType => { | #toDist(#normalize) => dist |> GenericDist.normalize |> (r => #Dist(r)) | #toDist(#truncate(left, right)) => dist - |> GenericDist.Truncate.run(toPointSet, left, right) + |> GenericDist.truncate(toPointSet, left, right) |> E.R.fmap(r => #Dist(r)) |> fromResult | #toDist(#toPointSet) => @@ -95,7 +87,7 @@ let rec run = (extra, fnName: operation): outputType => { | #toDistCombination(#Algebraic, _, #Float(_)) => #Error(NotYetImplemented) | #toDistCombination(#Algebraic, operation, #Dist(dist2)) => dist - |> GenericDist.AlgebraicCombination.run(toPointSet, toSampleSet, operation, dist2) + |> GenericDist.algebraicCombination(toPointSet, toSampleSet, operation, dist2) |> E.R.fmap(r => #Dist(r)) |> fromResult | #toDistCombination(#Pointwise, operation, #Dist(dist2)) => diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi new file mode 100644 index 00000000..84822876 --- /dev/null +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi @@ -0,0 +1,13 @@ +type params = { + sampleCount: int, + xyPointLength: int, +} + +type outputType = [ + | #Dist(GenericDist_Types.genericDist) + | #Error(GenericDist_Types.error) + | #Float(float) + | #String(string) +] + +let run: (params, GenericDist_Types.Operation.genericFunctionCall) => outputType \ No newline at end of file diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res index 321b3a6c..ecf8398f 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res @@ -61,7 +61,7 @@ module Operation = { | #toString ] - type genericFunction = [ + type genericFunctionCall = [ | #fromDist(fromDist, genericDist) | #fromFloat(fromDist, float) | #mixture(array<(genericDist, float)>) diff --git a/packages/squiggle-lang/src/rescript/GenericDist/README.md b/packages/squiggle-lang/src/rescript/GenericDist/README.md index 382de2b9..f37009de 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/README.md +++ b/packages/squiggle-lang/src/rescript/GenericDist/README.md @@ -33,7 +33,6 @@ graph TD ## Todo - [ ] Lots of cleanup -- [ ] Add interface files - [ ] Simple test story - [ ] Provide decent stack traces for key calls in GenericOperation. This could be very useful for debugging. - [ ] Cleanup Sample Set library From 3cf336d7203e9440f60a88a22912ebf96a611b60 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Mon, 28 Mar 2022 15:14:39 -0400 Subject: [PATCH 15/41] Starting to add tests to rescript --- .../GenericDist/GenericOperation__Test.res | 104 ++++++++++++++++++ .../GenericDist_GenericOperation.res | 53 ++++++++- .../GenericDist_GenericOperation.resi | 16 ++- .../GenericDist/GenericDist_Types.res | 7 ++ .../src/rescript/pointSetDist/XYShape.res | 2 +- .../src/rescript/sampleSet/SampleSet.res | 2 + 6 files changed, 178 insertions(+), 6 deletions(-) create mode 100644 packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res diff --git a/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res b/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res new file mode 100644 index 00000000..2c1a44c4 --- /dev/null +++ b/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res @@ -0,0 +1,104 @@ +open Jest +open Expect + +let params: GenericDist_GenericOperation.params = { + sampleCount: 100, + xyPointLength: 100, +} + +let normalDist: GenericDist_Types.genericDist = #Symbolic(#Normal({mean: 5.0, stdev: 2.0})) +let normalDist10: GenericDist_Types.genericDist = #Symbolic(#Normal({mean: 10.0, stdev: 2.0})) +let normalDist20: GenericDist_Types.genericDist = #Symbolic(#Normal({mean: 20.0, stdev: 2.0})) +let uniformDist: GenericDist_Types.genericDist = #Symbolic(#Uniform({low: 9.0, high: 10.0})) + +let {toFloat, toDist, toString, toError} = module(GenericDist_GenericOperation.Output) +let {run, fmap} = module(GenericDist_GenericOperation) +let run = run(params) +let fmap = fmap(params) +let toExt: option<'a> => 'a = E.O.toExt( + "Should be impossible to reach (This error is in test file)", +) + +describe("normalize", () => { + test("has no impact on normal dist", () => { + let result = run(#fromDist(#toDist(#normalize), normalDist)) + expect(result)->toEqual(#Dist(normalDist)) + }) +}) + +describe("mean", () => { + test("for a normal distribution", () => { + let result = GenericDist_GenericOperation.run(params, #fromDist(#toFloat(#Mean), normalDist)) + expect(result)->toEqual(#Float(5.0)) + }) +}) + +describe("mixture", () => { + test("on two normal distributions", () => { + let result = + run(#mixture([(normalDist10, 0.5), (normalDist20, 0.5)])) + |> fmap(#fromDist(#toFloat(#Mean))) + |> toFloat + |> toExt + expect(result)->toBeCloseTo(15.28) + }) +}) + +describe("toPointSet", () => { + test("on symbolic normal distribution", () => { + let result = + run(#fromDist(#toDist(#toPointSet), normalDist)) + |> fmap(#fromDist(#toFloat(#Mean))) + |> toFloat + |> toExt + expect(result)->toBeCloseTo(5.09) + }) + + test("on sample set distribution with under 4 points", () => { + let result = + run(#fromDist(#toDist(#toPointSet), #SampleSet([0.0, 1.0, 2.0, 3.0]))) |> fmap( + #fromDist(#toFloat(#Mean)), + ) + expect(result)->toEqual(#Error(Other("Converting sampleSet to pointSet failed"))) + }) + + test("back and forth", () => { + let result = + run(#fromDist(#toDist(#toPointSet), normalDist)) + |> fmap(#fromDist(#toDist(#toSampleSet(1000)))) + |> fmap(#fromDist(#toDist(#consoleLog))) + |> fmap(#fromDist(#toDist(#toPointSet))) + |> fmap(#fromDist(#toDist(#consoleLog))) + |> fmap(#fromDist(#toFloat(#Mean))) + |> toFloat + |> toExt + expect(result)->toBeCloseTo(5.09) + }) + + test("on sample set distribution", () => { + let result = + run( + #fromDist( + #toDist(#toPointSet), + #SampleSet([ + 0.0, + 1.0, + 2.0, + 3.0, + 4.0, + 5.0, + 6.0, + 7.0, + 8.0, + 9.0, + 10.0, + ]), + ), + ) + |> fmap(#fromDist(#toFloat(#Mean))) + |> toFloat + |> toExt + Js.log(result) + expect(result)->toBeCloseTo(5.09) + }) +}) diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res index cc542ef3..9993ef20 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res @@ -16,6 +16,32 @@ type outputType = [ | #String(string) ] +module Output = { + let toDist = (o: outputType) => + switch o { + | #Dist(d) => Some(d) + | _ => None + } + + let toFloat = (o: outputType) => + switch o { + | #Float(d) => Some(d) + | _ => None + } + + let toString = (o: outputType) => + switch o { + | #String(d) => Some(d) + | _ => None + } + + let toError = (o: outputType) => + switch o { + | #Error(d) => Some(d) + | _ => None + } +} + let fromResult = (r: result): outputType => switch r { | Ok(o) => o @@ -71,12 +97,13 @@ let rec run = (extra, fnName: operation): outputType => { |> E.R.fmap(r => #Float(r)) |> fromResult | #toString => dist |> GenericDist.toString |> (r => #String(r)) + | #toDist(#consoleLog) => { + Js.log2("Console log requested: ", dist) + #Dist(dist) + } | #toDist(#normalize) => dist |> GenericDist.normalize |> (r => #Dist(r)) | #toDist(#truncate(left, right)) => - dist - |> GenericDist.truncate(toPointSet, left, right) - |> E.R.fmap(r => #Dist(r)) - |> fromResult + dist |> GenericDist.truncate(toPointSet, left, right) |> E.R.fmap(r => #Dist(r)) |> fromResult | #toDist(#toPointSet) => dist |> GenericDist.toPointSet(xyPointLength) @@ -109,3 +136,21 @@ let rec run = (extra, fnName: operation): outputType => { GenericDist.mixture(scaleMultiply, pointwiseAdd, dists) |> E.R.fmap(r => #Dist(r)) |> fromResult } } + +let runFromDist = (extra, fnName, dist) => run(extra, #fromDist(fnName, dist)) +let runFromFloat = (extra, fnName, float) => run(extra, #fromFloat(fnName, float)) + +let fmap = ( + extra, + fn: GenericDist_Types.Operation.singleParamaterFunction, + input: outputType, +): outputType => { + let newFnCall: result = switch (fn, input) { + | (#fromDist(fromDist), #Dist(o)) => Ok(#fromDist(fromDist, o)) + | (#fromFloat(fromDist), #Float(o)) => Ok(#fromFloat(fromDist, o)) + | (_, #Error(r)) => Error(r) + | (#fromDist(_), _) => Error(Other("Expected dist, got something else")) + | (#fromFloat(_), _) => Error(Other("Expected float, got something else")) + } + newFnCall |> E.R.fmap(r => run(extra, r)) |> fromResult +} diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi index 84822876..d05f4ddc 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi @@ -10,4 +10,18 @@ type outputType = [ | #String(string) ] -let run: (params, GenericDist_Types.Operation.genericFunctionCall) => outputType \ No newline at end of file +let run: (params, GenericDist_Types.Operation.genericFunctionCall) => outputType +let runFromDist: ( + params, + GenericDist_Types.Operation.fromDist, + GenericDist_Types.genericDist, +) => outputType +let runFromFloat: (params, GenericDist_Types.Operation.fromDist, float) => outputType +let fmap: (params, GenericDist_Types.Operation.singleParamaterFunction, outputType) => outputType + +module Output: { + let toDist: outputType => option + let toFloat: outputType => option + let toString: outputType => option + let toError: outputType => option +} diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res index ecf8398f..7aad46b8 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res @@ -48,6 +48,7 @@ module Operation = { | #toPointSet | #toSampleSet(int) | #truncate(option, option) + | #consoleLog ] type toFloatArray = [ @@ -61,6 +62,11 @@ module Operation = { | #toString ] + type singleParamaterFunction = [ + | #fromDist(fromDist) + | #fromFloat(fromDist) + ] + type genericFunctionCall = [ | #fromDist(fromDist, genericDist) | #fromFloat(fromDist, float) @@ -78,6 +84,7 @@ module Operation = { | #toDist(#toPointSet) => `toPointSet` | #toDist(#toSampleSet(r)) => `toSampleSet${E.I.toString(r)}` | #toDist(#truncate(_, _)) => `truncate` + | #toDist(#consoleLog) => `consoleLog` | #toString => `toString` | #toDistCombination(#Algebraic, _, _) => `algebraic` | #toDistCombination(#Pointwise, _, _) => `pointwise` diff --git a/packages/squiggle-lang/src/rescript/pointSetDist/XYShape.res b/packages/squiggle-lang/src/rescript/pointSetDist/XYShape.res index 048b571f..6cadec60 100644 --- a/packages/squiggle-lang/src/rescript/pointSetDist/XYShape.res +++ b/packages/squiggle-lang/src/rescript/pointSetDist/XYShape.res @@ -254,7 +254,7 @@ module PointwiseCombination = { j = t2n; continue; } else { - console.log("Error!", i, j); + console.log("PointwiseCombination Error", i, j); } outX.push(x); diff --git a/packages/squiggle-lang/src/rescript/sampleSet/SampleSet.res b/packages/squiggle-lang/src/rescript/sampleSet/SampleSet.res index 6b20d946..746f13d7 100644 --- a/packages/squiggle-lang/src/rescript/sampleSet/SampleSet.res +++ b/packages/squiggle-lang/src/rescript/sampleSet/SampleSet.res @@ -1,3 +1,5 @@ +// TODO: Refactor to raise correct error when not enough samples + module Internals = { module Types = { type samplingStats = { From 01b80d73be1834882a11379a20718896f16d2d79 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Mon, 28 Mar 2022 16:52:04 -0400 Subject: [PATCH 16/41] Added simple genericOperation test --- .../GenericDist/GenericOperation__Test.res | 31 +------------------ 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res b/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res index 2c1a44c4..22121903 100644 --- a/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res +++ b/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res @@ -62,43 +62,14 @@ describe("toPointSet", () => { expect(result)->toEqual(#Error(Other("Converting sampleSet to pointSet failed"))) }) - test("back and forth", () => { + test("on sample set", () => { let result = run(#fromDist(#toDist(#toPointSet), normalDist)) |> fmap(#fromDist(#toDist(#toSampleSet(1000)))) - |> fmap(#fromDist(#toDist(#consoleLog))) |> fmap(#fromDist(#toDist(#toPointSet))) - |> fmap(#fromDist(#toDist(#consoleLog))) |> fmap(#fromDist(#toFloat(#Mean))) |> toFloat |> toExt expect(result)->toBeCloseTo(5.09) }) - - test("on sample set distribution", () => { - let result = - run( - #fromDist( - #toDist(#toPointSet), - #SampleSet([ - 0.0, - 1.0, - 2.0, - 3.0, - 4.0, - 5.0, - 6.0, - 7.0, - 8.0, - 9.0, - 10.0, - ]), - ), - ) - |> fmap(#fromDist(#toFloat(#Mean))) - |> toFloat - |> toExt - Js.log(result) - expect(result)->toBeCloseTo(5.09) - }) }) From b8159a88e07d4b81d43bece7cfdd7d909ddaa943 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Mon, 28 Mar 2022 22:39:59 -0400 Subject: [PATCH 17/41] Figured out patchelf hack for nixos users --- CONTRIBUTING.md | 9 ++++++++ flake.nix | 1 + .../squiggle-lang/__tests__/Symbolic_test.res | 23 +++++++++++++++++++ packages/squiggle-lang/package.json | 3 ++- .../squiggle-lang/src/rescript/utility/E.res | 4 ++++ 5 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 packages/squiggle-lang/__tests__/Symbolic_test.res diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3694f142..a6b645f8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -36,6 +36,15 @@ You need `yarn`. TODO: fill this out based on all the different packages scripts once they cool down. +## If you're on NixOS + +You'll need to run a command like this in order to get `yarn build` to run, especially in `packages/squiggle-lang`. +```sh +patchelf --set-interpreter $(patchelf --print-interpreter $(which mkdir)) ./node_modules/gentype/gentype.exe +``` + +See [here](https://github.com/NixOS/nixpkgs/issues/107375) + # Pull request protocol Please work against `staging` branch. **Do not** work against `master`. Please do not merge without approval from some subset of Quinn, Sam, and Ozzie; they will be auto-pinged. diff --git a/flake.nix b/flake.nix index fc4595b8..d39ba79a 100644 --- a/flake.nix +++ b/flake.nix @@ -44,6 +44,7 @@ yarn2nix nodePackages.npm nodejs + patchelf (pkgs.vscode-with-extensions.override { vscode = pkgs.vscodium; vscodeExtensions = pkgs.vscode-utils.extensionsFromVscodeMarketplace [ diff --git a/packages/squiggle-lang/__tests__/Symbolic_test.res b/packages/squiggle-lang/__tests__/Symbolic_test.res new file mode 100644 index 00000000..cdb178cd --- /dev/null +++ b/packages/squiggle-lang/__tests__/Symbolic_test.res @@ -0,0 +1,23 @@ +open Jest +open Expect +open Js.Array +open SymbolicDist +open E.Sp + +let makeTest = (~only=false, str, item1, item2) => + only + ? Only.test(str, () => expect(item1) -> toEqual(item2)) + : test(str, () => expect(item1) -> toEqual(item2)) + +let normalParams1: SymbolicDistTypes.normal = {mean: 5.0, stdev: 2.0} +let normalParams2: SymbolicDistTypes.normal = {mean: 10.0, stdev: 2.0} +let normalParams3: SymbolicDistTypes.normal = {mean: 20.0, stdev: 2.0} +let range20 = [0.0,1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0,16.0,17.0,18.0,19.0,20.0] +let forSparkline = (thisPdf, inps) => map(thisPdf, inps) + +describe("normal combine", () => { + let pdf1 = x => Normal.pdf(x, normalParams1) + let forSparkline1 = forSparkline(pdf1, range20) + let x = forSparkline1 -> toString -> sparkly -> Js.Console.log + makeTest("Spark1", 1, 0) +}) diff --git a/packages/squiggle-lang/package.json b/packages/squiggle-lang/package.json index 6755d2d2..edb6803b 100644 --- a/packages/squiggle-lang/package.json +++ b/packages/squiggle-lang/package.json @@ -24,7 +24,8 @@ "mathjs": "10.4.1", "pdfast": "^0.2.0", "rationale": "0.2.0", - "rescript": "^9.1.4" + "rescript": "^9.1.4", + "sparkly": "6.0.0" }, "devDependencies": { "@glennsl/rescript-jest": "^0.9.0", diff --git a/packages/squiggle-lang/src/rescript/utility/E.res b/packages/squiggle-lang/src/rescript/utility/E.res index d17850fd..01c87a28 100644 --- a/packages/squiggle-lang/src/rescript/utility/E.res +++ b/packages/squiggle-lang/src/rescript/utility/E.res @@ -441,3 +441,7 @@ module JsArray = { |> Js.Array.map(Rationale.Option.toExn("Warning: This should not have happened")) let filter = Js.Array.filter } + +module Sp = { + @module("sparkly") @scope("sparkly") external sparkly: string => string = "sparkly" +} From 03318ee2e12937dcc1ba097e3ed4c6aa110a4002 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Tue, 29 Mar 2022 02:13:52 -0400 Subject: [PATCH 18/41] translated sparkly to rescript, still debugging it tho --- .../squiggle-lang/__tests__/Symbolic_test.res | 3 +- packages/squiggle-lang/package.json | 3 +- .../squiggle-lang/src/rescript/utility/E.res | 4 -- .../src/rescript/utility/Sparklines.res | 51 +++++++++++++++++++ 4 files changed, 53 insertions(+), 8 deletions(-) create mode 100644 packages/squiggle-lang/src/rescript/utility/Sparklines.res diff --git a/packages/squiggle-lang/__tests__/Symbolic_test.res b/packages/squiggle-lang/__tests__/Symbolic_test.res index cdb178cd..8f9f88cd 100644 --- a/packages/squiggle-lang/__tests__/Symbolic_test.res +++ b/packages/squiggle-lang/__tests__/Symbolic_test.res @@ -2,7 +2,6 @@ open Jest open Expect open Js.Array open SymbolicDist -open E.Sp let makeTest = (~only=false, str, item1, item2) => only @@ -18,6 +17,6 @@ let forSparkline = (thisPdf, inps) => map(thisPdf, inps) describe("normal combine", () => { let pdf1 = x => Normal.pdf(x, normalParams1) let forSparkline1 = forSparkline(pdf1, range20) - let x = forSparkline1 -> toString -> sparkly -> Js.Console.log + let x = forSparkline1 -> toString -> Sparklines.sparkly -> Js.Console.log makeTest("Spark1", 1, 0) }) diff --git a/packages/squiggle-lang/package.json b/packages/squiggle-lang/package.json index edb6803b..6755d2d2 100644 --- a/packages/squiggle-lang/package.json +++ b/packages/squiggle-lang/package.json @@ -24,8 +24,7 @@ "mathjs": "10.4.1", "pdfast": "^0.2.0", "rationale": "0.2.0", - "rescript": "^9.1.4", - "sparkly": "6.0.0" + "rescript": "^9.1.4" }, "devDependencies": { "@glennsl/rescript-jest": "^0.9.0", diff --git a/packages/squiggle-lang/src/rescript/utility/E.res b/packages/squiggle-lang/src/rescript/utility/E.res index 01c87a28..d17850fd 100644 --- a/packages/squiggle-lang/src/rescript/utility/E.res +++ b/packages/squiggle-lang/src/rescript/utility/E.res @@ -441,7 +441,3 @@ module JsArray = { |> Js.Array.map(Rationale.Option.toExn("Warning: This should not have happened")) let filter = Js.Array.filter } - -module Sp = { - @module("sparkly") @scope("sparkly") external sparkly: string => string = "sparkly" -} diff --git a/packages/squiggle-lang/src/rescript/utility/Sparklines.res b/packages/squiggle-lang/src/rescript/utility/Sparklines.res new file mode 100644 index 00000000..97b25127 --- /dev/null +++ b/packages/squiggle-lang/src/rescript/utility/Sparklines.res @@ -0,0 +1,51 @@ +// Port of Sindre Sorhus' Sparkly to Rescript +// reference implementation: https://github.com/sindresorhus/sparkly +// Omitting rgb "fire" style, so no `chalk` dependency + +type sparklyConfig = { + minimum: option, + maximum: option +} + +let sparkly = ( + numbers: list, + ~options = {minimum: None, maximum: None} +) => { + // if not numbers is not an array, throw typeerror "Expected an array" + + // Unlike reference impl, we assume that all numbers are finite, i.e. no NaN. + let ticks = ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"] + let minimum = switch options.minimum { + | None => Js.Math.minimum(numbers) + | Some(x) => x + } + let maximum = switch options.maximum { + | None => Js.Math.maximum(numbers) + | Some(x) => x + } + + // Use a high tick if data is constant and max is not equal + let ticks = if minimum == maximum && maximum != 0.0 { + [ticks[4]] + } else { + ticks + } + + let toMapWith = number => { + let ret = if (! Js.Number.isFinite(number)) { + " " + } else { + let tickIndex = Js.Math.ceil((number / maximum) * ticks.length) - 1 + + let tickIndex = if maximum == 0.0 || tickIndex < 0 { + 0 + } else { + tickIndex + } + ticks[tickIndex] + } + ret + } + let ret = map(toMapWith, numbers) + Js.Array.joinWith("", ret) +} From 669fb95479a8eb76fa2e45f7377dd2f4eab6f965 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Tue, 29 Mar 2022 10:04:08 -0400 Subject: [PATCH 19/41] debugged sparklines implementation --- .../squiggle-lang/__tests__/Symbolic_test.res | 4 ++-- .../src/rescript/utility/Sparklines.res | 19 +++++++++---------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/packages/squiggle-lang/__tests__/Symbolic_test.res b/packages/squiggle-lang/__tests__/Symbolic_test.res index 8f9f88cd..f45e3374 100644 --- a/packages/squiggle-lang/__tests__/Symbolic_test.res +++ b/packages/squiggle-lang/__tests__/Symbolic_test.res @@ -14,9 +14,9 @@ let normalParams3: SymbolicDistTypes.normal = {mean: 20.0, stdev: 2.0} let range20 = [0.0,1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0,16.0,17.0,18.0,19.0,20.0] let forSparkline = (thisPdf, inps) => map(thisPdf, inps) -describe("normal combine", () => { +describe("Sparklines", () => { let pdf1 = x => Normal.pdf(x, normalParams1) let forSparkline1 = forSparkline(pdf1, range20) - let x = forSparkline1 -> toString -> Sparklines.sparkly -> Js.Console.log + Js.Console.log(Sparklines.sparkly(forSparkline1, ~options={minimum: None, maximum: None})) makeTest("Spark1", 1, 0) }) diff --git a/packages/squiggle-lang/src/rescript/utility/Sparklines.res b/packages/squiggle-lang/src/rescript/utility/Sparklines.res index 97b25127..9f2f780c 100644 --- a/packages/squiggle-lang/src/rescript/utility/Sparklines.res +++ b/packages/squiggle-lang/src/rescript/utility/Sparklines.res @@ -8,19 +8,19 @@ type sparklyConfig = { } let sparkly = ( - numbers: list, + numbers: array, ~options = {minimum: None, maximum: None} ) => { // if not numbers is not an array, throw typeerror "Expected an array" // Unlike reference impl, we assume that all numbers are finite, i.e. no NaN. - let ticks = ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"] + let ticks = ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"]// ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"] let minimum = switch options.minimum { - | None => Js.Math.minimum(numbers) + | None => Js.Math.minMany_float(numbers) | Some(x) => x } let maximum = switch options.maximum { - | None => Js.Math.maximum(numbers) + | None => Js.Math.maxMany_float(numbers) | Some(x) => x } @@ -31,11 +31,9 @@ let sparkly = ( ticks } - let toMapWith = number => { - let ret = if (! Js.Number.isFinite(number)) { - " " - } else { - let tickIndex = Js.Math.ceil((number / maximum) * ticks.length) - 1 + let toMapWith = (number: float) => { + let ret = { + let tickIndex = Js.Math.ceil_int((number /. maximum) *. Belt.Int.toFloat(Belt.Array.length(ticks))) - 1 let tickIndex = if maximum == 0.0 || tickIndex < 0 { 0 @@ -46,6 +44,7 @@ let sparkly = ( } ret } - let ret = map(toMapWith, numbers) + let ret = Belt.Array.map(numbers, toMapWith) + // Belt.Array.reduce(ret, "", (x, y) => x ++ y) Js.Array.joinWith("", ret) } From 49c091043a7559e305625e6b2bc19ef6bb149e0a Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Tue, 29 Mar 2022 10:40:38 -0400 Subject: [PATCH 20/41] second test case --- .../squiggle-lang/__tests__/Symbolic_test.res | 16 ++++++++++++---- .../src/rescript/utility/Sparklines.res | 5 +++-- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/packages/squiggle-lang/__tests__/Symbolic_test.res b/packages/squiggle-lang/__tests__/Symbolic_test.res index f45e3374..98032804 100644 --- a/packages/squiggle-lang/__tests__/Symbolic_test.res +++ b/packages/squiggle-lang/__tests__/Symbolic_test.res @@ -9,14 +9,22 @@ let makeTest = (~only=false, str, item1, item2) => : test(str, () => expect(item1) -> toEqual(item2)) let normalParams1: SymbolicDistTypes.normal = {mean: 5.0, stdev: 2.0} -let normalParams2: SymbolicDistTypes.normal = {mean: 10.0, stdev: 2.0} +// let normalParams2: SymbolicDistTypes.normal = {mean: 10.0, stdev: 2.0} let normalParams3: SymbolicDistTypes.normal = {mean: 20.0, stdev: 2.0} let range20 = [0.0,1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0,16.0,17.0,18.0,19.0,20.0] let forSparkline = (thisPdf, inps) => map(thisPdf, inps) -describe("Sparklines", () => { +describe("Normal with Sparklines", () => { let pdf1 = x => Normal.pdf(x, normalParams1) let forSparkline1 = forSparkline(pdf1, range20) - Js.Console.log(Sparklines.sparkly(forSparkline1, ~options={minimum: None, maximum: None})) - makeTest("Spark1", 1, 0) + makeTest("mean=5", Sparklines.sparkly(forSparkline1, ~options={minimum: None, maximum: None}), `▁▂▃▅███▅▃▂▁▁▁▁▁▁▁▁▁▁▁`) + + let normal4 = Normal.add(normalParams1, normalParams3) + let normalParams4 = switch normal4 { + | #Normal(params) => params + | _ => {mean: 0.0, stdev: 1.0} + } + let pdf4 = x => Normal.pdf(x, normalParams4) + let forSparkline4 = forSparkline(pdf4, range20) + makeTest("mixture of two normals", Sparklines.sparkly(forSparkline4, ~options={minimum: None, maximum: None}), `▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▂▅█`) }) diff --git a/packages/squiggle-lang/src/rescript/utility/Sparklines.res b/packages/squiggle-lang/src/rescript/utility/Sparklines.res index 9f2f780c..35679918 100644 --- a/packages/squiggle-lang/src/rescript/utility/Sparklines.res +++ b/packages/squiggle-lang/src/rescript/utility/Sparklines.res @@ -14,16 +14,17 @@ let sparkly = ( // if not numbers is not an array, throw typeerror "Expected an array" // Unlike reference impl, we assume that all numbers are finite, i.e. no NaN. - let ticks = ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"]// ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"] + let ticks = [`▁`, `▂`, `▃`, `▄`, `▅`, `▆`, `▇`, `█`] let minimum = switch options.minimum { | None => Js.Math.minMany_float(numbers) | Some(x) => x } + // let minimum = E.O.default(Js.Math.minMany_float(numbers)) let maximum = switch options.maximum { | None => Js.Math.maxMany_float(numbers) | Some(x) => x } - + // let maximum = E.O.default(Js.Math.maxMany_float(numbers)) // Use a high tick if data is constant and max is not equal let ticks = if minimum == maximum && maximum != 0.0 { [ticks[4]] From 517a9128e2836fce30c73692a9dc44e0fcea3223 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Tue, 29 Mar 2022 10:55:36 -0400 Subject: [PATCH 21/41] E.O.default pattern --- .../src/rescript/utility/Sparklines.res | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/packages/squiggle-lang/src/rescript/utility/Sparklines.res b/packages/squiggle-lang/src/rescript/utility/Sparklines.res index 35679918..222364b7 100644 --- a/packages/squiggle-lang/src/rescript/utility/Sparklines.res +++ b/packages/squiggle-lang/src/rescript/utility/Sparklines.res @@ -15,16 +15,9 @@ let sparkly = ( // Unlike reference impl, we assume that all numbers are finite, i.e. no NaN. let ticks = [`▁`, `▂`, `▃`, `▄`, `▅`, `▆`, `▇`, `█`] - let minimum = switch options.minimum { - | None => Js.Math.minMany_float(numbers) - | Some(x) => x - } - // let minimum = E.O.default(Js.Math.minMany_float(numbers)) - let maximum = switch options.maximum { - | None => Js.Math.maxMany_float(numbers) - | Some(x) => x - } - // let maximum = E.O.default(Js.Math.maxMany_float(numbers)) + + let minimum = E.O.default(Js.Math.minMany_float(numbers), options.minimum) + let maximum = E.O.default(Js.Math.maxMany_float(numbers), options.maximum) // Use a high tick if data is constant and max is not equal let ticks = if minimum == maximum && maximum != 0.0 { [ticks[4]] From bcff646e54c6ae42b1064cfc9d4a7a5b8d2f6848 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Tue, 29 Mar 2022 11:05:27 -0400 Subject: [PATCH 22/41] cleanup --- .../src/rescript/utility/Sparklines.res | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/packages/squiggle-lang/src/rescript/utility/Sparklines.res b/packages/squiggle-lang/src/rescript/utility/Sparklines.res index 222364b7..2e5c9cf0 100644 --- a/packages/squiggle-lang/src/rescript/utility/Sparklines.res +++ b/packages/squiggle-lang/src/rescript/utility/Sparklines.res @@ -11,13 +11,13 @@ let sparkly = ( numbers: array, ~options = {minimum: None, maximum: None} ) => { - // if not numbers is not an array, throw typeerror "Expected an array" - // Unlike reference impl, we assume that all numbers are finite, i.e. no NaN. + let ticks = [`▁`, `▂`, `▃`, `▄`, `▅`, `▆`, `▇`, `█`] let minimum = E.O.default(Js.Math.minMany_float(numbers), options.minimum) let maximum = E.O.default(Js.Math.maxMany_float(numbers), options.maximum) + // Use a high tick if data is constant and max is not equal let ticks = if minimum == maximum && maximum != 0.0 { [ticks[4]] @@ -26,19 +26,15 @@ let sparkly = ( } let toMapWith = (number: float) => { - let ret = { - let tickIndex = Js.Math.ceil_int((number /. maximum) *. Belt.Int.toFloat(Belt.Array.length(ticks))) - 1 + let tickIndex = Js.Math.ceil_int((number /. maximum) *. Belt.Int.toFloat(Belt.Array.length(ticks))) - 1 - let tickIndex = if maximum == 0.0 || tickIndex < 0 { + let tickIndex = if maximum == 0.0 || tickIndex < 0 { 0 } else { tickIndex } - ticks[tickIndex] - } - ret + + ticks[tickIndex] } - let ret = Belt.Array.map(numbers, toMapWith) - // Belt.Array.reduce(ret, "", (x, y) => x ++ y) - Js.Array.joinWith("", ret) + Js.Array.joinWith("", Belt.Array.map(numbers, toMapWith)) } From cdbbededa446745b098a12f6d4f815293e1a4256 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Tue, 29 Mar 2022 14:36:54 -0400 Subject: [PATCH 23/41] Refactoring GenericOperation to prefer -> operator --- .../GenericDist/GenericOperation__Test.res | 24 +++++++++---------- .../src/rescript/GenericDist/GenericDist.res | 4 ++-- .../src/rescript/GenericDist/GenericDist.resi | 4 ++-- .../GenericDist_GenericOperation.res | 16 +++++++------ .../GenericDist_GenericOperation.resi | 2 +- .../GenericDist/GenericDist_Types.res | 4 ++-- .../src/rescript/GenericDist/README.md | 6 ++++- 7 files changed, 33 insertions(+), 27 deletions(-) diff --git a/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res b/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res index 22121903..6263155b 100644 --- a/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res +++ b/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res @@ -37,9 +37,9 @@ describe("mixture", () => { test("on two normal distributions", () => { let result = run(#mixture([(normalDist10, 0.5), (normalDist20, 0.5)])) - |> fmap(#fromDist(#toFloat(#Mean))) - |> toFloat - |> toExt + ->fmap(#fromDist(#toFloat(#Mean))) + ->toFloat + ->toExt expect(result)->toBeCloseTo(15.28) }) }) @@ -48,15 +48,15 @@ describe("toPointSet", () => { test("on symbolic normal distribution", () => { let result = run(#fromDist(#toDist(#toPointSet), normalDist)) - |> fmap(#fromDist(#toFloat(#Mean))) - |> toFloat - |> toExt + ->fmap(#fromDist(#toFloat(#Mean))) + ->toFloat + ->toExt expect(result)->toBeCloseTo(5.09) }) test("on sample set distribution with under 4 points", () => { let result = - run(#fromDist(#toDist(#toPointSet), #SampleSet([0.0, 1.0, 2.0, 3.0]))) |> fmap( + run(#fromDist(#toDist(#toPointSet), #SampleSet([0.0, 1.0, 2.0, 3.0])))->fmap( #fromDist(#toFloat(#Mean)), ) expect(result)->toEqual(#Error(Other("Converting sampleSet to pointSet failed"))) @@ -65,11 +65,11 @@ describe("toPointSet", () => { test("on sample set", () => { let result = run(#fromDist(#toDist(#toPointSet), normalDist)) - |> fmap(#fromDist(#toDist(#toSampleSet(1000)))) - |> fmap(#fromDist(#toDist(#toPointSet))) - |> fmap(#fromDist(#toFloat(#Mean))) - |> toFloat - |> toExt + ->fmap(#fromDist(#toDist(#toSampleSet(1000)))) + ->fmap(#fromDist(#toDist(#toPointSet))) + ->fmap(#fromDist(#toFloat(#Mean))) + ->toFloat + ->toExt expect(result)->toBeCloseTo(5.09) }) }) diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res index 0b8d7717..3a10558a 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res @@ -6,7 +6,7 @@ type toSampleSetFn = t => result, error> type scaleMultiplyFn = (t, float) => result type pointwiseAddFn = (t, t) => result -let sampleN = (n, t: t) => +let sampleN = (t: t, n) => switch t { | #PointSet(r) => Ok(PointSetDist.sampleNRendered(n, r)) | #Symbolic(r) => Ok(SymbolicDist.T.sampleN(n, r)) @@ -53,7 +53,7 @@ let defaultSamplingInputs: SamplingInputs.samplingInputs = { kernelWidth: None, } -let toPointSet = (xyPointLength, t: t): result => { +let toPointSet = (t, xyPointLength): result => { switch t { | #PointSet(pointSet) => Ok(pointSet) | #Symbolic(r) => Ok(SymbolicDist.T.toPointSetDist(xyPointLength, r)) diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.resi b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.resi index 0f8734e0..18dcc0eb 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.resi +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.resi @@ -5,7 +5,7 @@ type toSampleSetFn = t => result, error> type scaleMultiplyFn = (t, float) => result type pointwiseAddFn = (t, t) => result -let sampleN: (int, t) => result, error> +let sampleN: (t, int) => result, error> let fromFloat: float => t @@ -15,7 +15,7 @@ let normalize: t => t let operationToFloat: (toPointSetFn, Operation.distToFloatOperation, t) => result -let toPointSet: (int, t) => result +let toPointSet: (t, int) => result let truncate: (toPointSetFn, option, option, t) => result diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res index 9993ef20..2923445e 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res @@ -16,6 +16,7 @@ type outputType = [ | #String(string) ] + module Output = { let toDist = (o: outputType) => switch o { @@ -82,13 +83,14 @@ let rec run = (extra, fnName: operation): outputType => { reCall( ~fnName=#fromDist(#toDistCombination(#Pointwise, #Multiply, #Float(weight)), r), (), - ) |> outputToDistResult + ) -> outputToDistResult let pointwiseAdd = (r1, r2) => reCall( ~fnName=#fromDist(#toDistCombination(#Pointwise, #Add, #Dist(r2)), r1), (), - ) |> outputToDistResult + ) -> outputToDistResult + let fromDistFn = (subFn: GenericDist_Types.Operation.fromDist, dist: genericDist) => switch subFn { @@ -96,21 +98,21 @@ let rec run = (extra, fnName: operation): outputType => { GenericDist.operationToFloat(toPointSet, fnName, dist) |> E.R.fmap(r => #Float(r)) |> fromResult - | #toString => dist |> GenericDist.toString |> (r => #String(r)) + | #toString => dist -> GenericDist.toString -> (r => #String(r)) | #toDist(#consoleLog) => { Js.log2("Console log requested: ", dist) #Dist(dist) } - | #toDist(#normalize) => dist |> GenericDist.normalize |> (r => #Dist(r)) + | #toDist(#normalize) => dist -> GenericDist.normalize -> (r => #Dist(r)) | #toDist(#truncate(left, right)) => dist |> GenericDist.truncate(toPointSet, left, right) |> E.R.fmap(r => #Dist(r)) |> fromResult | #toDist(#toPointSet) => dist - |> GenericDist.toPointSet(xyPointLength) + -> GenericDist.toPointSet(xyPointLength) |> E.R.fmap(r => #Dist(#PointSet(r))) |> fromResult | #toDist(#toSampleSet(n)) => - dist |> GenericDist.sampleN(n) |> E.R.fmap(r => #Dist(#SampleSet(r))) |> fromResult + dist -> GenericDist.sampleN(n) |> E.R.fmap(r => #Dist(#SampleSet(r))) |> fromResult | #toDistCombination(#Algebraic, _, #Float(_)) => #Error(NotYetImplemented) | #toDistCombination(#Algebraic, operation, #Dist(dist2)) => dist @@ -142,8 +144,8 @@ let runFromFloat = (extra, fnName, float) => run(extra, #fromFloat(fnName, float let fmap = ( extra, - fn: GenericDist_Types.Operation.singleParamaterFunction, input: outputType, + fn: GenericDist_Types.Operation.singleParamaterFunction, ): outputType => { let newFnCall: result = switch (fn, input) { | (#fromDist(fromDist), #Dist(o)) => Ok(#fromDist(fromDist, o)) diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi index d05f4ddc..53e1463a 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi @@ -17,7 +17,7 @@ let runFromDist: ( GenericDist_Types.genericDist, ) => outputType let runFromFloat: (params, GenericDist_Types.Operation.fromDist, float) => outputType -let fmap: (params, GenericDist_Types.Operation.singleParamaterFunction, outputType) => outputType +let fmap: (params, outputType, GenericDist_Types.Operation.singleParamaterFunction) => outputType module Output: { let toDist: outputType => option diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res index 7aad46b8..c2fb64d7 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res @@ -78,11 +78,11 @@ module Operation = { | #toFloat(#Cdf(r)) => `cdf(${E.Float.toFixed(r)})` | #toFloat(#Inv(r)) => `inv(${E.Float.toFixed(r)})` | #toFloat(#Mean) => `mean` - | #toFloat(#Pdf(r)) => `pdf${E.Float.toFixed(r)}` + | #toFloat(#Pdf(r)) => `pdf(${E.Float.toFixed(r)})` | #toFloat(#Sample) => `sample` | #toDist(#normalize) => `normalize` | #toDist(#toPointSet) => `toPointSet` - | #toDist(#toSampleSet(r)) => `toSampleSet${E.I.toString(r)}` + | #toDist(#toSampleSet(r)) => `toSampleSet(${E.I.toString(r)})` | #toDist(#truncate(_, _)) => `truncate` | #toDist(#consoleLog) => `consoleLog` | #toString => `toString` diff --git a/packages/squiggle-lang/src/rescript/GenericDist/README.md b/packages/squiggle-lang/src/rescript/GenericDist/README.md index f37009de..e69e449e 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/README.md +++ b/packages/squiggle-lang/src/rescript/GenericDist/README.md @@ -31,6 +31,8 @@ graph TD D --> Cauchy(Cauchy) ``` +## Diagram of Generic Distribution Types + ## Todo - [ ] Lots of cleanup - [ ] Simple test story @@ -41,4 +43,6 @@ graph TD - [ ] Remove most of DistPlus, much of which is not needed anymore - [ ] More functions for Sample Set, which is very minimal now - [ ] Allow these functions to be run on web workers -- [ ] Refactor interpreter to use GenericDist. This might not be necessary, as the new reducer-inspired interpreter is integrated. \ No newline at end of file +- [ ] Refactor interpreter to use GenericDist. This might not be necessary, as the new reducer-inspired interpreter is integrated. + +## More todos \ No newline at end of file From 320b8da91af6aba68408a343ea4fd1e25f84ad59 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Tue, 29 Mar 2022 15:10:20 -0400 Subject: [PATCH 24/41] most of the refactor based on @OAGr's comments --- .../squiggle-lang/__tests__/Symbolic_test.res | 38 ++++++++++--------- .../squiggle-lang/src/rescript/utility/E.res | 1 + .../src/rescript/utility/Sparklines.res | 33 ++++++++-------- 3 files changed, 37 insertions(+), 35 deletions(-) diff --git a/packages/squiggle-lang/__tests__/Symbolic_test.res b/packages/squiggle-lang/__tests__/Symbolic_test.res index 98032804..7d2076ae 100644 --- a/packages/squiggle-lang/__tests__/Symbolic_test.res +++ b/packages/squiggle-lang/__tests__/Symbolic_test.res @@ -8,23 +8,27 @@ let makeTest = (~only=false, str, item1, item2) => ? Only.test(str, () => expect(item1) -> toEqual(item2)) : test(str, () => expect(item1) -> toEqual(item2)) -let normalParams1: SymbolicDistTypes.normal = {mean: 5.0, stdev: 2.0} -// let normalParams2: SymbolicDistTypes.normal = {mean: 10.0, stdev: 2.0} -let normalParams3: SymbolicDistTypes.normal = {mean: 20.0, stdev: 2.0} -let range20 = [0.0,1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0,16.0,17.0,18.0,19.0,20.0] -let forSparkline = (thisPdf, inps) => map(thisPdf, inps) +let pdfImage = (thePdf, inps) => map(thePdf, inps) -describe("Normal with Sparklines", () => { - let pdf1 = x => Normal.pdf(x, normalParams1) - let forSparkline1 = forSparkline(pdf1, range20) - makeTest("mean=5", Sparklines.sparkly(forSparkline1, ~options={minimum: None, maximum: None}), `▁▂▃▅███▅▃▂▁▁▁▁▁▁▁▁▁▁▁`) - - let normal4 = Normal.add(normalParams1, normalParams3) - let normalParams4 = switch normal4 { - | #Normal(params) => params - | _ => {mean: 0.0, stdev: 1.0} +let parameterWiseAdditionHelper = (n1: SymbolicDistTypes.normal, n2: SymbolicDistTypes.normal) => { + let normalDistAtSumMeanConstr = Normal.add(n1, n2) + let normalDistAtSumMean: SymbolicDistTypes.normal = switch normalDistAtSumMeanConstr { + | #Normal(params) => params } - let pdf4 = x => Normal.pdf(x, normalParams4) - let forSparkline4 = forSparkline(pdf4, range20) - makeTest("mixture of two normals", Sparklines.sparkly(forSparkline4, ~options={minimum: None, maximum: None}), `▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▂▅█`) + x => Normal.pdf(x, normalDistAtSumMean) +} + +describe("Normal distribution with sparklines", () => { + + let normalDistAtMean5: SymbolicDistTypes.normal = {mean: 5.0, stdev: 2.0} + let normalDistAtMean10: SymbolicDistTypes.normal = {mean: 10.0, stdev: 2.0} + let range20Float = [0.0,1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0,16.0,17.0,18.0,19.0,] + + let pdfNormalDistAtMean5 = x => Normal.pdf(x, normalDistAtMean5) + let sparklineMean5 = pdfImage(pdfNormalDistAtMean5, range20Float) + makeTest("mean=5", Sparklines.create(sparklineMean5, ()), `▁▂▃▅███▅▃▂▁▁▁▁▁▁▁▁▁▁`) + + let sparklineMean15 = normalDistAtMean5 -> parameterWiseAdditionHelper(normalDistAtMean10) -> pdfImage(range20Float) + // let sparklineMean15 = pdfImage(pdfNormalDistAtMean15, range20Float) + makeTest("parameter-wise addition of two normal distributions", Sparklines.create(sparklineMean15, ()), `▁▁▁▁▁▁▁▁▁▁▂▃▅▇███▇▅▃`) }) diff --git a/packages/squiggle-lang/src/rescript/utility/E.res b/packages/squiggle-lang/src/rescript/utility/E.res index d17850fd..19febbad 100644 --- a/packages/squiggle-lang/src/rescript/utility/E.res +++ b/packages/squiggle-lang/src/rescript/utility/E.res @@ -323,6 +323,7 @@ module A = { } ) let filter = (o, e) => Js.Array.filter(o, e) + let joinWith = (fill, arr) => Js.Array.joinWith(fill, arr) module O = { let concatSomes = (optionals: array>): array<'a> => diff --git a/packages/squiggle-lang/src/rescript/utility/Sparklines.res b/packages/squiggle-lang/src/rescript/utility/Sparklines.res index 2e5c9cf0..627804ca 100644 --- a/packages/squiggle-lang/src/rescript/utility/Sparklines.res +++ b/packages/squiggle-lang/src/rescript/utility/Sparklines.res @@ -2,31 +2,28 @@ // reference implementation: https://github.com/sindresorhus/sparkly // Omitting rgb "fire" style, so no `chalk` dependency -type sparklyConfig = { - minimum: option, - maximum: option -} - -let sparkly = ( +let create = ( numbers: array, - ~options = {minimum: None, maximum: None} + ~minimum=?, + ~maximum=?, + () ) => { // Unlike reference impl, we assume that all numbers are finite, i.e. no NaN. let ticks = [`▁`, `▂`, `▃`, `▄`, `▅`, `▆`, `▇`, `█`] - let minimum = E.O.default(Js.Math.minMany_float(numbers), options.minimum) - let maximum = E.O.default(Js.Math.maxMany_float(numbers), options.maximum) + let minimum = E.O.default(Js.Math.minMany_float(numbers), minimum) + let maximum = E.O.default(Js.Math.maxMany_float(numbers), maximum) - // Use a high tick if data is constant and max is not equal - let ticks = if minimum == maximum && maximum != 0.0 { - [ticks[4]] - } else { - ticks - } +// // Use a high tick if data is constant and max is not equal to min or zero +// let ticks = if minimum == maximum && maximum != 0.0 { +// [ticks[4]] +// } else { +// ticks +// } - let toMapWith = (number: float) => { - let tickIndex = Js.Math.ceil_int((number /. maximum) *. Belt.Int.toFloat(Belt.Array.length(ticks))) - 1 + let toHeight = (number: float) => { + let tickIndex = Js.Math.ceil_int((number /. maximum) *. (ticks -> Belt.Array.length -> Belt.Int.toFloat)) - 1 let tickIndex = if maximum == 0.0 || tickIndex < 0 { 0 @@ -36,5 +33,5 @@ let sparkly = ( ticks[tickIndex] } - Js.Array.joinWith("", Belt.Array.map(numbers, toMapWith)) + toHeight -> E.A.fmap(numbers) -> (arr => E.A.joinWith("", arr)) } From 539c7cf7831e1a1c2469abbed7eabb05f542a2a4 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Tue, 29 Mar 2022 15:21:38 -0400 Subject: [PATCH 25/41] Trying to change more |> into -> --- .../src/rescript/GenericDist/GenericDist.res | 29 ++++++++++++------- .../GenericDist_GenericOperation.res | 25 ++++++---------- .../squiggle-lang/src/rescript/utility/E.res | 14 +++++++++ 3 files changed, 42 insertions(+), 26 deletions(-) diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res index 3a10558a..e956a303 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res @@ -41,7 +41,7 @@ let operationToFloat = (toPointSet: toPointSetFn, fnName, t) => { switch symbolicSolution { | Some(r) => Ok(r) - | None => toPointSet(t) |> E.R.fmap(PointSetDist.operate(fnName)) + | None => toPointSet(t)->E.R.fmap2(PointSetDist.operate(fnName)) } } @@ -53,6 +53,8 @@ let defaultSamplingInputs: SamplingInputs.samplingInputs = { kernelWidth: None, } +//Todo: If it's a pointSet, but the xyPointLenght is different from what it has, it should change. +// This is tricky because the case of discrete distributions. let toPointSet = (t, xyPointLength): result => { switch t { | #PointSet(pointSet) => Ok(pointSet) @@ -106,7 +108,10 @@ let truncate = Truncate.run /* Given two random variables A and B, this returns the distribution of a new variable that is the result of the operation on A and B. For instance, normal(0, 1) + normal(1, 1) -> normal(1, 2). - In general, this is implemented via convolution. */ + In general, this is implemented via convolution. + + TODO: It would be useful to be able to pass in a paramater to get this to run either with convolution or monte carlo. +*/ module AlgebraicCombination = { let tryAnalyticalSimplification = ( operation: GenericDist_Types.Operation.arithmeticOperation, @@ -174,9 +179,9 @@ module AlgebraicCombination = { | None => switch chooseConvolutionOrMonteCarlo(t1, t2) { | #CalculateWithMonteCarlo => - runMonteCarlo(toSampleSet, algebraicOp, t1, t2) |> E.R.fmap(r => #SampleSet(r)) + runMonteCarlo(toSampleSet, algebraicOp, t1, t2)->E.R.fmap2(r => #SampleSet(r)) | #CalculateWithConvolution => - runConvolution(toPointSet, algebraicOp, t1, t2) |> E.R.fmap(r => #PointSet(r)) + runConvolution(toPointSet, algebraicOp, t1, t2)->E.R.fmap2(r => #PointSet(r)) } } } @@ -190,10 +195,10 @@ let pointwiseCombination = (toPointSet: toPointSetFn, operation, t2: t, t1: t): error, > => { E.R.merge(toPointSet(t1), toPointSet(t2)) - |> E.R.fmap(((t1, t2)) => + ->E.R.fmap2(((t1, t2)) => PointSetDist.combinePointwise(GenericDist_Types.Operation.arithmeticToFn(operation), t1, t2) ) - |> E.R.fmap(r => #PointSet(r)) + ->E.R.fmap2(r => #PointSet(r)) } let pointwiseCombinationFloat = ( @@ -205,7 +210,7 @@ let pointwiseCombinationFloat = ( switch operation { | #Add | #Subtract => Error(GenericDist_Types.DistributionVerticalShiftIsInvalid) | (#Multiply | #Divide | #Exponentiate | #Log) as operation => - toPointSet(t) |> E.R.fmap(t => { + toPointSet(t)->E.R.fmap2(t => { //TODO: Move to PointSet codebase let fn = (secondary, main) => Operation.Scale.toFn(operation, main, secondary) let integralSumCacheFn = Operation.Scale.toIntegralSumCacheFn(operation) @@ -217,9 +222,10 @@ let pointwiseCombinationFloat = ( t, ) }) - } |> E.R.fmap(r => #PointSet(r)) + }->E.R.fmap2(r => #PointSet(r)) } +//Note: The result should always cumulatively sum to 1. let mixture = ( scaleMultiply: scaleMultiplyFn, pointwiseAdd: pointwiseAddFn, @@ -228,9 +234,12 @@ let mixture = ( if E.A.length(values) == 0 { Error(GenericDist_Types.Other("mixture must have at least 1 element")) } else { + let totalWeight = values->E.A.fmap2(E.Tuple2.second)->E.A.Floats.sum let properlyWeightedValues = - values |> E.A.fmap(((dist, weight)) => scaleMultiply(dist, weight)) |> E.A.R.firstErrorOrOpen - properlyWeightedValues |> E.R.bind(_, values => { + values + ->E.A.fmap2(((dist, weight)) => scaleMultiply(dist, weight /. totalWeight)) + ->E.A.R.firstErrorOrOpen + properlyWeightedValues->E.R.bind(values => { values |> Js.Array.sliceFrom(1) |> E.A.fold_left( diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res index 2923445e..8dbdfe5a 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res @@ -16,7 +16,6 @@ type outputType = [ | #String(string) ] - module Output = { let toDist = (o: outputType) => switch o { @@ -83,36 +82,30 @@ let rec run = (extra, fnName: operation): outputType => { reCall( ~fnName=#fromDist(#toDistCombination(#Pointwise, #Multiply, #Float(weight)), r), (), - ) -> outputToDistResult + )->outputToDistResult let pointwiseAdd = (r1, r2) => reCall( ~fnName=#fromDist(#toDistCombination(#Pointwise, #Add, #Dist(r2)), r1), (), - ) -> outputToDistResult - + )->outputToDistResult let fromDistFn = (subFn: GenericDist_Types.Operation.fromDist, dist: genericDist) => switch subFn { | #toFloat(fnName) => - GenericDist.operationToFloat(toPointSet, fnName, dist) - |> E.R.fmap(r => #Float(r)) - |> fromResult - | #toString => dist -> GenericDist.toString -> (r => #String(r)) + GenericDist.operationToFloat(toPointSet, fnName, dist)->E.R.fmap2(r => #Float(r))->fromResult + | #toString => dist->GenericDist.toString->(r => #String(r)) | #toDist(#consoleLog) => { Js.log2("Console log requested: ", dist) #Dist(dist) } - | #toDist(#normalize) => dist -> GenericDist.normalize -> (r => #Dist(r)) + | #toDist(#normalize) => dist->GenericDist.normalize->(r => #Dist(r)) | #toDist(#truncate(left, right)) => dist |> GenericDist.truncate(toPointSet, left, right) |> E.R.fmap(r => #Dist(r)) |> fromResult | #toDist(#toPointSet) => - dist - -> GenericDist.toPointSet(xyPointLength) - |> E.R.fmap(r => #Dist(#PointSet(r))) - |> fromResult + dist->GenericDist.toPointSet(xyPointLength)->E.R.fmap2(r => #Dist(#PointSet(r)))->fromResult | #toDist(#toSampleSet(n)) => - dist -> GenericDist.sampleN(n) |> E.R.fmap(r => #Dist(#SampleSet(r))) |> fromResult + dist->GenericDist.sampleN(n)->E.R.fmap2(r => #Dist(#SampleSet(r)))->fromResult | #toDistCombination(#Algebraic, _, #Float(_)) => #Error(NotYetImplemented) | #toDistCombination(#Algebraic, operation, #Dist(dist2)) => dist @@ -135,7 +128,7 @@ let rec run = (extra, fnName: operation): outputType => { | #fromDist(subFn, dist) => fromDistFn(subFn, dist) | #fromFloat(subFn, float) => reCall(~fnName=#fromDist(subFn, GenericDist.fromFloat(float)), ()) | #mixture(dists) => - GenericDist.mixture(scaleMultiply, pointwiseAdd, dists) |> E.R.fmap(r => #Dist(r)) |> fromResult + GenericDist.mixture(scaleMultiply, pointwiseAdd, dists)->E.R.fmap2(r => #Dist(r))->fromResult } } @@ -154,5 +147,5 @@ let fmap = ( | (#fromDist(_), _) => Error(Other("Expected dist, got something else")) | (#fromFloat(_), _) => Error(Other("Expected float, got something else")) } - newFnCall |> E.R.fmap(r => run(extra, r)) |> fromResult + newFnCall->E.R.fmap2(r => run(extra, r))->fromResult } diff --git a/packages/squiggle-lang/src/rescript/utility/E.res b/packages/squiggle-lang/src/rescript/utility/E.res index d17850fd..69d8ecc1 100644 --- a/packages/squiggle-lang/src/rescript/utility/E.res +++ b/packages/squiggle-lang/src/rescript/utility/E.res @@ -33,6 +33,17 @@ module U = { let id = e => e } +module Tuple2 = { + let first = (v: ('a, 'b)) => { + let (a, _) = v + a + } + let second = (v: ('a, 'b)) => { + let (_, b) = v + b + } +} + module O = { let dimap = (sFn, rFn, e) => switch e { @@ -137,6 +148,7 @@ module R = { let result = Rationale.Result.result let id = e => e |> result(U.id, U.id) let fmap = Rationale.Result.fmap + let fmap2 = (a,b) => Rationale.Result.fmap(b,a) let bind = Rationale.Result.bind let toExn = Belt.Result.getExn let default = (default, res: Belt.Result.t<'a, 'b>) => @@ -233,6 +245,7 @@ module L = { /* A for Array */ module A = { let fmap = Array.map + let fmap2 = (a,b) => Array.map(b,a) let fmapi = Array.mapi let to_list = Array.to_list let of_list = Array.of_list @@ -405,6 +418,7 @@ module A = { : { let _ = Js.Array.push(element, continuous) } + () }) From 50f5256dc53a52c114c1d58bccefd00586191742 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Tue, 29 Mar 2022 15:27:23 -0400 Subject: [PATCH 26/41] perhaps the final push of PR 124? --- packages/squiggle-lang/__tests__/Symbolic_test.res | 6 +++--- packages/squiggle-lang/src/rescript/utility/E.res | 1 + .../src/rescript/utility/Sparklines.res | 13 ++----------- 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/packages/squiggle-lang/__tests__/Symbolic_test.res b/packages/squiggle-lang/__tests__/Symbolic_test.res index 7d2076ae..bac4d17b 100644 --- a/packages/squiggle-lang/__tests__/Symbolic_test.res +++ b/packages/squiggle-lang/__tests__/Symbolic_test.res @@ -22,13 +22,13 @@ describe("Normal distribution with sparklines", () => { let normalDistAtMean5: SymbolicDistTypes.normal = {mean: 5.0, stdev: 2.0} let normalDistAtMean10: SymbolicDistTypes.normal = {mean: 10.0, stdev: 2.0} - let range20Float = [0.0,1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0,16.0,17.0,18.0,19.0,] + let range20Float = E.A.rangeFloat(0, 20) // [0.0,1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0,16.0,17.0,18.0,19.0,] let pdfNormalDistAtMean5 = x => Normal.pdf(x, normalDistAtMean5) let sparklineMean5 = pdfImage(pdfNormalDistAtMean5, range20Float) - makeTest("mean=5", Sparklines.create(sparklineMean5, ()), `▁▂▃▅███▅▃▂▁▁▁▁▁▁▁▁▁▁`) + makeTest("mean=5", Sparklines.create(sparklineMean5, ()), `▁▂▃▅███▅▃▂▁▁▁▁▁▁▁▁▁▁▁`) let sparklineMean15 = normalDistAtMean5 -> parameterWiseAdditionHelper(normalDistAtMean10) -> pdfImage(range20Float) // let sparklineMean15 = pdfImage(pdfNormalDistAtMean15, range20Float) - makeTest("parameter-wise addition of two normal distributions", Sparklines.create(sparklineMean15, ()), `▁▁▁▁▁▁▁▁▁▁▂▃▅▇███▇▅▃`) + makeTest("parameter-wise addition of two normal distributions", Sparklines.create(sparklineMean15, ()), `▁▁▁▁▁▁▁▁▁▁▂▃▅▇███▇▅▃▂`) }) diff --git a/packages/squiggle-lang/src/rescript/utility/E.res b/packages/squiggle-lang/src/rescript/utility/E.res index 19febbad..eb366a6d 100644 --- a/packages/squiggle-lang/src/rescript/utility/E.res +++ b/packages/squiggle-lang/src/rescript/utility/E.res @@ -269,6 +269,7 @@ module A = { )) |> Rationale.Result.return } + let rangeFloat = (start, stop) => start -> Belt.Array.rangeBy(stop, ~step=1) -> (arr => fmap(Belt.Int.toFloat, arr)) // This zips while taking the longest elements of each array. let zipMaxLength = (array1, array2) => { diff --git a/packages/squiggle-lang/src/rescript/utility/Sparklines.res b/packages/squiggle-lang/src/rescript/utility/Sparklines.res index 627804ca..09e17e37 100644 --- a/packages/squiggle-lang/src/rescript/utility/Sparklines.res +++ b/packages/squiggle-lang/src/rescript/utility/Sparklines.res @@ -1,6 +1,7 @@ // Port of Sindre Sorhus' Sparkly to Rescript // reference implementation: https://github.com/sindresorhus/sparkly // Omitting rgb "fire" style, so no `chalk` dependency +// Omitting: NaN handling, special consideration for constant data. let create = ( numbers: array, @@ -8,30 +9,20 @@ let create = ( ~maximum=?, () ) => { - // Unlike reference impl, we assume that all numbers are finite, i.e. no NaN. - let ticks = [`▁`, `▂`, `▃`, `▄`, `▅`, `▆`, `▇`, `█`] let minimum = E.O.default(Js.Math.minMany_float(numbers), minimum) let maximum = E.O.default(Js.Math.maxMany_float(numbers), maximum) -// // Use a high tick if data is constant and max is not equal to min or zero -// let ticks = if minimum == maximum && maximum != 0.0 { -// [ticks[4]] -// } else { -// ticks -// } - let toHeight = (number: float) => { let tickIndex = Js.Math.ceil_int((number /. maximum) *. (ticks -> Belt.Array.length -> Belt.Int.toFloat)) - 1 - let tickIndex = if maximum == 0.0 || tickIndex < 0 { 0 } else { tickIndex } - ticks[tickIndex] } + toHeight -> E.A.fmap(numbers) -> (arr => E.A.joinWith("", arr)) } From c158b4183bd6c06fcf9b81de51cf211ab416968f Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Tue, 29 Mar 2022 15:47:32 -0400 Subject: [PATCH 27/41] More refactors of |> to -> --- .../src/rescript/GenericDist/GenericDist.res | 22 ++++++++--------- .../src/rescript/GenericDist/GenericDist.resi | 12 +++++----- .../GenericDist_GenericOperation.res | 24 +++++++++---------- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res index e956a303..56a086ca 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res @@ -29,7 +29,7 @@ let normalize = (t: t) => | #SampleSet(_) => t } -let operationToFloat = (toPointSet: toPointSetFn, fnName, t) => { +let operationToFloat = (t, toPointSet: toPointSetFn, fnName) => { let symbolicSolution = switch t { | #Symbolic(r) => switch SymbolicDist.T.operate(fnName, r) { @@ -83,10 +83,10 @@ module Truncate = { } let run = ( + t: t, toPointSet: toPointSetFn, leftCutoff: option, rightCutoff: option, - t: t, ): result => { let doesNotNeedCutoff = E.O.isNone(leftCutoff) && E.O.isNone(rightCutoff) if doesNotNeedCutoff { @@ -95,7 +95,7 @@ module Truncate = { switch trySymbolicSimplification(leftCutoff, rightCutoff, t) { | Some(r) => Ok(r) | None => - toPointSet(t) |> E.R.fmap(t => + toPointSet(t)->E.R.fmap2(t => #PointSet(PointSetDist.T.truncate(leftCutoff, rightCutoff, t)) ) } @@ -134,7 +134,7 @@ module AlgebraicCombination = { t1: t, t2: t, ) => - E.R.merge(toPointSet(t1), toPointSet(t2)) |> E.R.fmap(((a, b)) => + E.R.merge(toPointSet(t1), toPointSet(t2))->E.R.fmap2(((a, b)) => PointSetDist.combineAlgebraically(operation, a, b) ) @@ -145,8 +145,8 @@ module AlgebraicCombination = { t2: t, ) => { let operation = Operation.Algebraic.toFn(operation) - E.R.merge(toSampleSet(t1), toSampleSet(t2)) |> E.R.fmap(((a, b)) => { - Belt.Array.zip(a, b) |> E.A.fmap(((a, b)) => operation(a, b)) + E.R.merge(toSampleSet(t1), toSampleSet(t2)) -> E.R.fmap2(((a, b)) => { + Belt.Array.zip(a, b) -> E.A.fmap2(((a, b)) => operation(a, b)) }) } @@ -155,7 +155,7 @@ module AlgebraicCombination = { switch x { | #Symbolic(#Float(_)) => 1 | #Symbolic(_) => 1000 - | #PointSet(Discrete(m)) => m.xyShape |> XYShape.T.length + | #PointSet(Discrete(m)) => m.xyShape -> XYShape.T.length | #PointSet(Mixed(_)) => 1000 | #PointSet(Continuous(_)) => 1000 | _ => 1000 @@ -167,10 +167,10 @@ module AlgebraicCombination = { : #CalculateWithConvolution let run = ( + t1: t, toPointSet: toPointSetFn, toSampleSet: toSampleSetFn, algebraicOp, - t1: t, t2: t, ): result => { switch tryAnalyticalSimplification(algebraicOp, t1, t2) { @@ -190,7 +190,7 @@ module AlgebraicCombination = { let algebraicCombination = AlgebraicCombination.run //TODO: Add faster pointwiseCombine fn -let pointwiseCombination = (toPointSet: toPointSetFn, operation, t2: t, t1: t): result< +let pointwiseCombination = (t1: t, toPointSet: toPointSetFn, operation, t2: t): result< t, error, > => { @@ -202,10 +202,10 @@ let pointwiseCombination = (toPointSet: toPointSetFn, operation, t2: t, t1: t): } let pointwiseCombinationFloat = ( + t: t, toPointSet: toPointSetFn, operation: GenericDist_Types.Operation.arithmeticOperation, f: float, - t: t, ): result => { switch operation { | #Add | #Subtract => Error(GenericDist_Types.DistributionVerticalShiftIsInvalid) @@ -227,9 +227,9 @@ let pointwiseCombinationFloat = ( //Note: The result should always cumulatively sum to 1. let mixture = ( + values: array<(t, float)>, scaleMultiply: scaleMultiplyFn, pointwiseAdd: pointwiseAddFn, - values: array<(t, float)>, ) => { if E.A.length(values) == 0 { Error(GenericDist_Types.Other("mixture must have at least 1 element")) diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.resi b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.resi index 18dcc0eb..b7d6bb26 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.resi +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.resi @@ -13,32 +13,32 @@ let toString: t => string let normalize: t => t -let operationToFloat: (toPointSetFn, Operation.distToFloatOperation, t) => result +let operationToFloat: (t, toPointSetFn, Operation.distToFloatOperation) => result let toPointSet: (t, int) => result -let truncate: (toPointSetFn, option, option, t) => result +let truncate: (t, toPointSetFn, option, option) => result let algebraicCombination: ( + t, toPointSetFn, toSampleSetFn, GenericDist_Types.Operation.arithmeticOperation, t, - t, ) => result let pointwiseCombination: ( + t, toPointSetFn, GenericDist_Types.Operation.arithmeticOperation, t, - t, ) => result let pointwiseCombinationFloat: ( + t, toPointSetFn, GenericDist_Types.Operation.arithmeticOperation, float, - t, ) => result -let mixture: (scaleMultiplyFn, pointwiseAddFn, array<(t, float)>) => result +let mixture: (array<(t, float)>, scaleMultiplyFn, pointwiseAddFn) => result diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res index 8dbdfe5a..c3aa71f8 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res @@ -93,7 +93,7 @@ let rec run = (extra, fnName: operation): outputType => { let fromDistFn = (subFn: GenericDist_Types.Operation.fromDist, dist: genericDist) => switch subFn { | #toFloat(fnName) => - GenericDist.operationToFloat(toPointSet, fnName, dist)->E.R.fmap2(r => #Float(r))->fromResult + GenericDist.operationToFloat(dist, toPointSet, fnName)->E.R.fmap2(r => #Float(r))->fromResult | #toString => dist->GenericDist.toString->(r => #String(r)) | #toDist(#consoleLog) => { Js.log2("Console log requested: ", dist) @@ -101,7 +101,7 @@ let rec run = (extra, fnName: operation): outputType => { } | #toDist(#normalize) => dist->GenericDist.normalize->(r => #Dist(r)) | #toDist(#truncate(left, right)) => - dist |> GenericDist.truncate(toPointSet, left, right) |> E.R.fmap(r => #Dist(r)) |> fromResult + dist->GenericDist.truncate(toPointSet, left, right)->E.R.fmap2(r => #Dist(r))->fromResult | #toDist(#toPointSet) => dist->GenericDist.toPointSet(xyPointLength)->E.R.fmap2(r => #Dist(#PointSet(r)))->fromResult | #toDist(#toSampleSet(n)) => @@ -109,26 +109,26 @@ let rec run = (extra, fnName: operation): outputType => { | #toDistCombination(#Algebraic, _, #Float(_)) => #Error(NotYetImplemented) | #toDistCombination(#Algebraic, operation, #Dist(dist2)) => dist - |> GenericDist.algebraicCombination(toPointSet, toSampleSet, operation, dist2) - |> E.R.fmap(r => #Dist(r)) - |> fromResult + ->GenericDist.algebraicCombination(toPointSet, toSampleSet, operation, dist2) + ->E.R.fmap2(r => #Dist(r)) + ->fromResult | #toDistCombination(#Pointwise, operation, #Dist(dist2)) => dist - |> GenericDist.pointwiseCombination(toPointSet, operation, dist2) - |> E.R.fmap(r => #Dist(r)) - |> fromResult + ->GenericDist.pointwiseCombination(toPointSet, operation, dist2) + ->E.R.fmap2(r => #Dist(r)) + ->fromResult | #toDistCombination(#Pointwise, operation, #Float(f)) => dist - |> GenericDist.pointwiseCombinationFloat(toPointSet, operation, f) - |> E.R.fmap(r => #Dist(r)) - |> fromResult + ->GenericDist.pointwiseCombinationFloat(toPointSet, operation, f) + ->E.R.fmap2(r => #Dist(r)) + ->fromResult } switch fnName { | #fromDist(subFn, dist) => fromDistFn(subFn, dist) | #fromFloat(subFn, float) => reCall(~fnName=#fromDist(subFn, GenericDist.fromFloat(float)), ()) | #mixture(dists) => - GenericDist.mixture(scaleMultiply, pointwiseAdd, dists)->E.R.fmap2(r => #Dist(r))->fromResult + dists->GenericDist.mixture(scaleMultiply, pointwiseAdd)->E.R.fmap2(r => #Dist(r))->fromResult } } From cd5680f2dc034d7bfcf4d7e40914778c29a99590 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Tue, 29 Mar 2022 16:31:08 -0400 Subject: [PATCH 28/41] Refactored Sparklines.res --- .../squiggle-lang/__tests__/Symbolic_test.res | 3 +- .../squiggle-lang/src/rescript/utility/E.res | 6 ++- .../src/rescript/utility/Sparklines.res | 38 +++++++++---------- 3 files changed, 23 insertions(+), 24 deletions(-) diff --git a/packages/squiggle-lang/__tests__/Symbolic_test.res b/packages/squiggle-lang/__tests__/Symbolic_test.res index bac4d17b..8ec3be22 100644 --- a/packages/squiggle-lang/__tests__/Symbolic_test.res +++ b/packages/squiggle-lang/__tests__/Symbolic_test.res @@ -22,13 +22,12 @@ describe("Normal distribution with sparklines", () => { let normalDistAtMean5: SymbolicDistTypes.normal = {mean: 5.0, stdev: 2.0} let normalDistAtMean10: SymbolicDistTypes.normal = {mean: 10.0, stdev: 2.0} - let range20Float = E.A.rangeFloat(0, 20) // [0.0,1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0,16.0,17.0,18.0,19.0,] + let range20Float = E.A.rangeFloat(0, 20) // [0.0,1.0,2.0,3.0,4.0,...19.0,] let pdfNormalDistAtMean5 = x => Normal.pdf(x, normalDistAtMean5) let sparklineMean5 = pdfImage(pdfNormalDistAtMean5, range20Float) makeTest("mean=5", Sparklines.create(sparklineMean5, ()), `▁▂▃▅███▅▃▂▁▁▁▁▁▁▁▁▁▁▁`) let sparklineMean15 = normalDistAtMean5 -> parameterWiseAdditionHelper(normalDistAtMean10) -> pdfImage(range20Float) - // let sparklineMean15 = pdfImage(pdfNormalDistAtMean15, range20Float) makeTest("parameter-wise addition of two normal distributions", Sparklines.create(sparklineMean15, ()), `▁▁▁▁▁▁▁▁▁▁▂▃▅▇███▇▅▃▂`) }) diff --git a/packages/squiggle-lang/src/rescript/utility/E.res b/packages/squiggle-lang/src/rescript/utility/E.res index eb366a6d..6d2a2ce2 100644 --- a/packages/squiggle-lang/src/rescript/utility/E.res +++ b/packages/squiggle-lang/src/rescript/utility/E.res @@ -269,7 +269,8 @@ module A = { )) |> Rationale.Result.return } - let rangeFloat = (start, stop) => start -> Belt.Array.rangeBy(stop, ~step=1) -> (arr => fmap(Belt.Int.toFloat, arr)) + let rangeFloat = (~step=1, start, stop) => + Belt.Array.rangeBy(start, stop, ~step) |> fmap(Belt.Int.toFloat) // This zips while taking the longest elements of each array. let zipMaxLength = (array1, array2) => { @@ -324,7 +325,7 @@ module A = { } ) let filter = (o, e) => Js.Array.filter(o, e) - let joinWith = (fill, arr) => Js.Array.joinWith(fill, arr) + let joinWith = Js.Array.joinWith module O = { let concatSomes = (optionals: array>): array<'a> => @@ -407,6 +408,7 @@ module A = { : { let _ = Js.Array.push(element, continuous) } + () }) diff --git a/packages/squiggle-lang/src/rescript/utility/Sparklines.res b/packages/squiggle-lang/src/rescript/utility/Sparklines.res index 09e17e37..cc07860f 100644 --- a/packages/squiggle-lang/src/rescript/utility/Sparklines.res +++ b/packages/squiggle-lang/src/rescript/utility/Sparklines.res @@ -3,26 +3,24 @@ // Omitting rgb "fire" style, so no `chalk` dependency // Omitting: NaN handling, special consideration for constant data. -let create = ( - numbers: array, - ~minimum=?, - ~maximum=?, - () -) => { - let ticks = [`▁`, `▂`, `▃`, `▄`, `▅`, `▆`, `▇`, `█`] +let ticks = [`▁`, `▂`, `▃`, `▄`, `▅`, `▆`, `▇`, `█`] - let minimum = E.O.default(Js.Math.minMany_float(numbers), minimum) - let maximum = E.O.default(Js.Math.maxMany_float(numbers), maximum) +let _ticksLength = E.A.length(ticks) - let toHeight = (number: float) => { - let tickIndex = Js.Math.ceil_int((number /. maximum) *. (ticks -> Belt.Array.length -> Belt.Int.toFloat)) - 1 - let tickIndex = if maximum == 0.0 || tickIndex < 0 { - 0 - } else { - tickIndex - } - ticks[tickIndex] - } - - toHeight -> E.A.fmap(numbers) -> (arr => E.A.joinWith("", arr)) +let _heightToTickIndex = (maximum: float, v: float) => { + let v = Js.Math.ceil_int(v /. maximum *. Belt.Int.toFloat(_ticksLength)) - 1 + min(v, 0) +} + +let create = (relativeHeights: array, ~maximum=?, ()) => { + if E.A.length(relativeHeights) === 0 { + "" + } else { + let maximum = maximum |> E.O.default(E.A.max(relativeHeights) |> E.O.toExn("")) + + relativeHeights + |> E.A.fmap(_heightToTickIndex(maximum)) + |> E.A.fmap(r => E.A.get(ticks, r) |> E.O.toExn("")) + |> E.A.joinWith("") + } } From 649f3e833adb6a74f219c7c48685e3f267532d55 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Tue, 29 Mar 2022 17:00:20 -0400 Subject: [PATCH 29/41] Changes as was requested in CR --- packages/squiggle-lang/src/rescript/utility/E.res | 13 +++++++++++-- .../src/rescript/utility/Sparklines.res | 12 ++++++------ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/packages/squiggle-lang/src/rescript/utility/E.res b/packages/squiggle-lang/src/rescript/utility/E.res index 6d2a2ce2..71c4c1e4 100644 --- a/packages/squiggle-lang/src/rescript/utility/E.res +++ b/packages/squiggle-lang/src/rescript/utility/E.res @@ -1,5 +1,4 @@ open Rationale.Function.Infix - module FloatFloatMap = { module Id = Belt.Id.MakeComparable({ type t = float @@ -87,6 +86,11 @@ module O = { let max = compare(\">") } +module O2 = { + let default = (a, b) => O.default(b, a) + let toExn = (a, b) => O.toExn(b, a) +} + /* Functions */ module F = { let apply = (a, e) => a |> e @@ -324,7 +328,7 @@ module A = { | r => Some(r) } ) - let filter = (o, e) => Js.Array.filter(o, e) + let filter = Js.Array.filter let joinWith = Js.Array.joinWith module O = { @@ -438,6 +442,11 @@ module A = { } } +module A2 = { + let fmap = (a, b) => Array.map(b, a) + let joinWith = (a, b) => A.joinWith(b, a) +} + module JsArray = { let concatSomes = (optionals: Js.Array.t>): Js.Array.t<'a> => optionals diff --git a/packages/squiggle-lang/src/rescript/utility/Sparklines.res b/packages/squiggle-lang/src/rescript/utility/Sparklines.res index cc07860f..12d509fa 100644 --- a/packages/squiggle-lang/src/rescript/utility/Sparklines.res +++ b/packages/squiggle-lang/src/rescript/utility/Sparklines.res @@ -8,19 +8,19 @@ let ticks = [`▁`, `▂`, `▃`, `▄`, `▅`, `▆`, `▇`, `█`] let _ticksLength = E.A.length(ticks) let _heightToTickIndex = (maximum: float, v: float) => { - let v = Js.Math.ceil_int(v /. maximum *. Belt.Int.toFloat(_ticksLength)) - 1 - min(v, 0) + let suggestedTickIndex = Js.Math.ceil_int(v /. maximum *. Belt.Int.toFloat(_ticksLength)) - 1 + max(suggestedTickIndex, 0) } let create = (relativeHeights: array, ~maximum=?, ()) => { if E.A.length(relativeHeights) === 0 { "" } else { - let maximum = maximum |> E.O.default(E.A.max(relativeHeights) |> E.O.toExn("")) + let maximum = maximum->E.O2.default(E.A.max(relativeHeights)->E.O2.toExn("")) relativeHeights - |> E.A.fmap(_heightToTickIndex(maximum)) - |> E.A.fmap(r => E.A.get(ticks, r) |> E.O.toExn("")) - |> E.A.joinWith("") + ->E.A2.fmap(_heightToTickIndex(maximum)) + ->E.A2.fmap(r => E.A.get(ticks, r)->E.O2.toExn("")) + ->E.A2.joinWith("") } } From 2fce3d67e95258604290161d0d85006cb8d02877 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Tue, 29 Mar 2022 17:35:33 -0400 Subject: [PATCH 30/41] fmap2 -> E.R2.fmap --- .../src/rescript/GenericDist/GenericDist.res | 30 +++++++++---------- .../GenericDist_GenericOperation.res | 18 +++++------ .../squiggle-lang/src/rescript/utility/E.res | 10 +++++-- 3 files changed, 32 insertions(+), 26 deletions(-) diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res index 56a086ca..48588afe 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res @@ -41,7 +41,7 @@ let operationToFloat = (t, toPointSet: toPointSetFn, fnName) => { switch symbolicSolution { | Some(r) => Ok(r) - | None => toPointSet(t)->E.R.fmap2(PointSetDist.operate(fnName)) + | None => toPointSet(t)->E.R2.fmap(PointSetDist.operate(fnName)) } } @@ -95,7 +95,7 @@ module Truncate = { switch trySymbolicSimplification(leftCutoff, rightCutoff, t) { | Some(r) => Ok(r) | None => - toPointSet(t)->E.R.fmap2(t => + toPointSet(t)->E.R2.fmap(t => #PointSet(PointSetDist.T.truncate(leftCutoff, rightCutoff, t)) ) } @@ -134,7 +134,7 @@ module AlgebraicCombination = { t1: t, t2: t, ) => - E.R.merge(toPointSet(t1), toPointSet(t2))->E.R.fmap2(((a, b)) => + E.R.merge(toPointSet(t1), toPointSet(t2))->E.R2.fmap(((a, b)) => PointSetDist.combineAlgebraically(operation, a, b) ) @@ -145,8 +145,8 @@ module AlgebraicCombination = { t2: t, ) => { let operation = Operation.Algebraic.toFn(operation) - E.R.merge(toSampleSet(t1), toSampleSet(t2)) -> E.R.fmap2(((a, b)) => { - Belt.Array.zip(a, b) -> E.A.fmap2(((a, b)) => operation(a, b)) + E.R.merge(toSampleSet(t1), toSampleSet(t2))->E.R2.fmap(((a, b)) => { + Belt.Array.zip(a, b)->E.A2.fmap(((a, b)) => operation(a, b)) }) } @@ -155,7 +155,7 @@ module AlgebraicCombination = { switch x { | #Symbolic(#Float(_)) => 1 | #Symbolic(_) => 1000 - | #PointSet(Discrete(m)) => m.xyShape -> XYShape.T.length + | #PointSet(Discrete(m)) => m.xyShape->XYShape.T.length | #PointSet(Mixed(_)) => 1000 | #PointSet(Continuous(_)) => 1000 | _ => 1000 @@ -179,9 +179,9 @@ module AlgebraicCombination = { | None => switch chooseConvolutionOrMonteCarlo(t1, t2) { | #CalculateWithMonteCarlo => - runMonteCarlo(toSampleSet, algebraicOp, t1, t2)->E.R.fmap2(r => #SampleSet(r)) + runMonteCarlo(toSampleSet, algebraicOp, t1, t2)->E.R2.fmap(r => #SampleSet(r)) | #CalculateWithConvolution => - runConvolution(toPointSet, algebraicOp, t1, t2)->E.R.fmap2(r => #PointSet(r)) + runConvolution(toPointSet, algebraicOp, t1, t2)->E.R2.fmap(r => #PointSet(r)) } } } @@ -195,10 +195,10 @@ let pointwiseCombination = (t1: t, toPointSet: toPointSetFn, operation, t2: t): error, > => { E.R.merge(toPointSet(t1), toPointSet(t2)) - ->E.R.fmap2(((t1, t2)) => + ->E.R2.fmap(((t1, t2)) => PointSetDist.combinePointwise(GenericDist_Types.Operation.arithmeticToFn(operation), t1, t2) ) - ->E.R.fmap2(r => #PointSet(r)) + ->E.R2.fmap(r => #PointSet(r)) } let pointwiseCombinationFloat = ( @@ -210,7 +210,7 @@ let pointwiseCombinationFloat = ( switch operation { | #Add | #Subtract => Error(GenericDist_Types.DistributionVerticalShiftIsInvalid) | (#Multiply | #Divide | #Exponentiate | #Log) as operation => - toPointSet(t)->E.R.fmap2(t => { + toPointSet(t)->E.R2.fmap(t => { //TODO: Move to PointSet codebase let fn = (secondary, main) => Operation.Scale.toFn(operation, main, secondary) let integralSumCacheFn = Operation.Scale.toIntegralSumCacheFn(operation) @@ -222,10 +222,10 @@ let pointwiseCombinationFloat = ( t, ) }) - }->E.R.fmap2(r => #PointSet(r)) + }->E.R2.fmap(r => #PointSet(r)) } -//Note: The result should always cumulatively sum to 1. +//Note: The result should always cumulatively sum to 1. This would be good to test. let mixture = ( values: array<(t, float)>, scaleMultiply: scaleMultiplyFn, @@ -234,10 +234,10 @@ let mixture = ( if E.A.length(values) == 0 { Error(GenericDist_Types.Other("mixture must have at least 1 element")) } else { - let totalWeight = values->E.A.fmap2(E.Tuple2.second)->E.A.Floats.sum + let totalWeight = values->E.A2.fmap(E.Tuple2.second)->E.A.Floats.sum let properlyWeightedValues = values - ->E.A.fmap2(((dist, weight)) => scaleMultiply(dist, weight /. totalWeight)) + ->E.A2.fmap(((dist, weight)) => scaleMultiply(dist, weight /. totalWeight)) ->E.A.R.firstErrorOrOpen properlyWeightedValues->E.R.bind(values => { values diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res index c3aa71f8..43aec78f 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res @@ -93,7 +93,7 @@ let rec run = (extra, fnName: operation): outputType => { let fromDistFn = (subFn: GenericDist_Types.Operation.fromDist, dist: genericDist) => switch subFn { | #toFloat(fnName) => - GenericDist.operationToFloat(dist, toPointSet, fnName)->E.R.fmap2(r => #Float(r))->fromResult + GenericDist.operationToFloat(dist, toPointSet, fnName)->E.R2.fmap(r => #Float(r))->fromResult | #toString => dist->GenericDist.toString->(r => #String(r)) | #toDist(#consoleLog) => { Js.log2("Console log requested: ", dist) @@ -101,26 +101,26 @@ let rec run = (extra, fnName: operation): outputType => { } | #toDist(#normalize) => dist->GenericDist.normalize->(r => #Dist(r)) | #toDist(#truncate(left, right)) => - dist->GenericDist.truncate(toPointSet, left, right)->E.R.fmap2(r => #Dist(r))->fromResult + dist->GenericDist.truncate(toPointSet, left, right)->E.R2.fmap(r => #Dist(r))->fromResult | #toDist(#toPointSet) => - dist->GenericDist.toPointSet(xyPointLength)->E.R.fmap2(r => #Dist(#PointSet(r)))->fromResult + dist->GenericDist.toPointSet(xyPointLength)->E.R2.fmap(r => #Dist(#PointSet(r)))->fromResult | #toDist(#toSampleSet(n)) => - dist->GenericDist.sampleN(n)->E.R.fmap2(r => #Dist(#SampleSet(r)))->fromResult + dist->GenericDist.sampleN(n)->E.R2.fmap(r => #Dist(#SampleSet(r)))->fromResult | #toDistCombination(#Algebraic, _, #Float(_)) => #Error(NotYetImplemented) | #toDistCombination(#Algebraic, operation, #Dist(dist2)) => dist ->GenericDist.algebraicCombination(toPointSet, toSampleSet, operation, dist2) - ->E.R.fmap2(r => #Dist(r)) + ->E.R2.fmap(r => #Dist(r)) ->fromResult | #toDistCombination(#Pointwise, operation, #Dist(dist2)) => dist ->GenericDist.pointwiseCombination(toPointSet, operation, dist2) - ->E.R.fmap2(r => #Dist(r)) + ->E.R2.fmap(r => #Dist(r)) ->fromResult | #toDistCombination(#Pointwise, operation, #Float(f)) => dist ->GenericDist.pointwiseCombinationFloat(toPointSet, operation, f) - ->E.R.fmap2(r => #Dist(r)) + ->E.R2.fmap(r => #Dist(r)) ->fromResult } @@ -128,7 +128,7 @@ let rec run = (extra, fnName: operation): outputType => { | #fromDist(subFn, dist) => fromDistFn(subFn, dist) | #fromFloat(subFn, float) => reCall(~fnName=#fromDist(subFn, GenericDist.fromFloat(float)), ()) | #mixture(dists) => - dists->GenericDist.mixture(scaleMultiply, pointwiseAdd)->E.R.fmap2(r => #Dist(r))->fromResult + dists->GenericDist.mixture(scaleMultiply, pointwiseAdd)->E.R2.fmap(r => #Dist(r))->fromResult } } @@ -147,5 +147,5 @@ let fmap = ( | (#fromDist(_), _) => Error(Other("Expected dist, got something else")) | (#fromFloat(_), _) => Error(Other("Expected float, got something else")) } - newFnCall->E.R.fmap2(r => run(extra, r))->fromResult + newFnCall->E.R2.fmap(r => run(extra, r))->fromResult } diff --git a/packages/squiggle-lang/src/rescript/utility/E.res b/packages/squiggle-lang/src/rescript/utility/E.res index 69d8ecc1..121ecb91 100644 --- a/packages/squiggle-lang/src/rescript/utility/E.res +++ b/packages/squiggle-lang/src/rescript/utility/E.res @@ -148,7 +148,6 @@ module R = { let result = Rationale.Result.result let id = e => e |> result(U.id, U.id) let fmap = Rationale.Result.fmap - let fmap2 = (a,b) => Rationale.Result.fmap(b,a) let bind = Rationale.Result.bind let toExn = Belt.Result.getExn let default = (default, res: Belt.Result.t<'a, 'b>) => @@ -172,6 +171,10 @@ module R = { errorCondition(r) ? Error(errorMessage) : Ok(r) } +module R2 = { + let fmap = (a,b) => R.fmap(b,a) +} + let safe_fn_of_string = (fn, s: string): option<'a> => try Some(fn(s)) catch { | _ => None @@ -245,7 +248,6 @@ module L = { /* A for Array */ module A = { let fmap = Array.map - let fmap2 = (a,b) => Array.map(b,a) let fmapi = Array.mapi let to_list = Array.to_list let of_list = Array.of_list @@ -448,6 +450,10 @@ module A = { } } +module A2 = { + let fmap = (a,b) => A.fmap(b,a) +} + module JsArray = { let concatSomes = (optionals: Js.Array.t>): Js.Array.t<'a> => optionals From ffc622fb6db06fca8e335c7f4452f39678e6fcce Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Tue, 29 Mar 2022 21:28:14 -0400 Subject: [PATCH 31/41] Responded to two simple CR comments --- .../GenericDist/GenericOperation__Test.res | 2 +- .../src/rescript/GenericDist/GenericDist.res | 6 +++-- .../GenericDist_GenericOperation.res | 26 +++++++++---------- .../GenericDist_GenericOperation.resi | 4 +-- .../GenericDist/GenericDist_Types.res | 5 ++-- 5 files changed, 23 insertions(+), 20 deletions(-) diff --git a/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res b/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res index 6263155b..9dfadbf8 100644 --- a/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res +++ b/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res @@ -59,7 +59,7 @@ describe("toPointSet", () => { run(#fromDist(#toDist(#toPointSet), #SampleSet([0.0, 1.0, 2.0, 3.0])))->fmap( #fromDist(#toFloat(#Mean)), ) - expect(result)->toEqual(#Error(Other("Converting sampleSet to pointSet failed"))) + expect(result)->toEqual(#GenDistError(Other("Converting sampleSet to pointSet failed"))) }) test("on sample set", () => { diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res index 48588afe..91e9daf6 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res @@ -207,7 +207,7 @@ let pointwiseCombinationFloat = ( operation: GenericDist_Types.Operation.arithmeticOperation, f: float, ): result => { - switch operation { + let m = switch operation { | #Add | #Subtract => Error(GenericDist_Types.DistributionVerticalShiftIsInvalid) | (#Multiply | #Divide | #Exponentiate | #Log) as operation => toPointSet(t)->E.R2.fmap(t => { @@ -222,10 +222,12 @@ let pointwiseCombinationFloat = ( t, ) }) - }->E.R2.fmap(r => #PointSet(r)) + } + m->E.R2.fmap(r => #PointSet(r)) } //Note: The result should always cumulatively sum to 1. This would be good to test. +//Note: If the inputs are not normalized, this will return poor results. The weights probably refer to the post-normalized forms. It would be good to apply a catch to this. let mixture = ( values: array<(t, float)>, scaleMultiply: scaleMultiplyFn, diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res index 43aec78f..5a893150 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res @@ -1,4 +1,4 @@ -type operation = GenericDist_Types.Operation.genericFunctionCall +type operation = GenericDist_Types.Operation.genericFunctionCallInfo type genericDist = GenericDist_Types.genericDist type error = GenericDist_Types.error @@ -11,9 +11,9 @@ type params = { type outputType = [ | #Dist(genericDist) - | #Error(error) | #Float(float) | #String(string) + | #GenDistError(error) ] module Output = { @@ -37,7 +37,7 @@ module Output = { let toError = (o: outputType) => switch o { - | #Error(d) => Some(d) + | #GenDistError(d) => Some(d) | _ => None } } @@ -45,14 +45,14 @@ module Output = { let fromResult = (r: result): outputType => switch r { | Ok(o) => o - | Error(e) => #Error(e) + | Error(e) => #GenDistError(e) } let outputToDistResult = (b: outputType): result => switch b { | #Dist(r) => Ok(r) - | #Error(r) => Error(r) - | _ => Error(ImpossiblePath) + | #GenDistError(r) => Error(r) + | _ => Error(Unreachable) } let rec run = (extra, fnName: operation): outputType => { @@ -65,16 +65,16 @@ let rec run = (extra, fnName: operation): outputType => { let toPointSet = r => { switch reCall(~fnName=#fromDist(#toDist(#toPointSet), r), ()) { | #Dist(#PointSet(p)) => Ok(p) - | #Error(r) => Error(r) - | _ => Error(ImpossiblePath) + | #GenDistError(r) => Error(r) + | _ => Error(Unreachable) } } let toSampleSet = r => { switch reCall(~fnName=#fromDist(#toDist(#toSampleSet(sampleCount)), r), ()) { | #Dist(#SampleSet(p)) => Ok(p) - | #Error(r) => Error(r) - | _ => Error(ImpossiblePath) + | #GenDistError(r) => Error(r) + | _ => Error(Unreachable) } } @@ -106,7 +106,7 @@ let rec run = (extra, fnName: operation): outputType => { dist->GenericDist.toPointSet(xyPointLength)->E.R2.fmap(r => #Dist(#PointSet(r)))->fromResult | #toDist(#toSampleSet(n)) => dist->GenericDist.sampleN(n)->E.R2.fmap(r => #Dist(#SampleSet(r)))->fromResult - | #toDistCombination(#Algebraic, _, #Float(_)) => #Error(NotYetImplemented) + | #toDistCombination(#Algebraic, _, #Float(_)) => #GenDistError(NotYetImplemented) | #toDistCombination(#Algebraic, operation, #Dist(dist2)) => dist ->GenericDist.algebraicCombination(toPointSet, toSampleSet, operation, dist2) @@ -143,9 +143,9 @@ let fmap = ( let newFnCall: result = switch (fn, input) { | (#fromDist(fromDist), #Dist(o)) => Ok(#fromDist(fromDist, o)) | (#fromFloat(fromDist), #Float(o)) => Ok(#fromFloat(fromDist, o)) - | (_, #Error(r)) => Error(r) + | (_, #GenDistError(r)) => Error(r) | (#fromDist(_), _) => Error(Other("Expected dist, got something else")) | (#fromFloat(_), _) => Error(Other("Expected float, got something else")) } newFnCall->E.R2.fmap(r => run(extra, r))->fromResult -} +} \ No newline at end of file diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi index 53e1463a..f8acdc42 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi @@ -5,12 +5,12 @@ type params = { type outputType = [ | #Dist(GenericDist_Types.genericDist) - | #Error(GenericDist_Types.error) + | #GenDistError(GenericDist_Types.error) | #Float(float) | #String(string) ] -let run: (params, GenericDist_Types.Operation.genericFunctionCall) => outputType +let run: (params, GenericDist_Types.Operation.genericFunctionCallInfo) => outputType let runFromDist: ( params, GenericDist_Types.Operation.fromDist, diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res index c2fb64d7..3a55ee63 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res @@ -6,7 +6,7 @@ type genericDist = [ type error = | NotYetImplemented - | ImpossiblePath + | Unreachable | DistributionVerticalShiftIsInvalid | Other(string) @@ -67,12 +67,13 @@ module Operation = { | #fromFloat(fromDist) ] - type genericFunctionCall = [ + type genericFunctionCallInfo = [ | #fromDist(fromDist, genericDist) | #fromFloat(fromDist, float) | #mixture(array<(genericDist, float)>) ] + //TODO: Should support all genericFunctionCallInfo types let toString = (distFunction: fromDist): string => switch distFunction { | #toFloat(#Cdf(r)) => `cdf(${E.Float.toFixed(r)})` From 41db968e99093e6ae333439553056f980aae338f Mon Sep 17 00:00:00 2001 From: Sam Nolan Date: Wed, 30 Mar 2022 21:39:32 +1100 Subject: [PATCH 32/41] Fix XSS issue with Squiggle Editor --- packages/components/src/SquiggleEditor.tsx | 6 +---- .../src/stories/SquiggleEditor.stories.mdx | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 packages/components/src/stories/SquiggleEditor.stories.mdx diff --git a/packages/components/src/SquiggleEditor.tsx b/packages/components/src/SquiggleEditor.tsx index ac199b1e..fdfda430 100644 --- a/packages/components/src/SquiggleEditor.tsx +++ b/packages/components/src/SquiggleEditor.tsx @@ -25,11 +25,7 @@ export interface SquiggleEditorProps { onEnvChange?(env: exportEnv): void; } -const highlight = (editor: HTMLInputElement) => { - let code = editor.textContent; - code = code.replace(/\((\w+?)(\b)/g, '($1$2'); - editor.innerHTML = code; -}; +const highlight = (_: HTMLInputElement) => {}; interface SquiggleEditorState { expression: string; diff --git a/packages/components/src/stories/SquiggleEditor.stories.mdx b/packages/components/src/stories/SquiggleEditor.stories.mdx new file mode 100644 index 00000000..9f3a5a1a --- /dev/null +++ b/packages/components/src/stories/SquiggleEditor.stories.mdx @@ -0,0 +1,22 @@ +import { SquiggleEditor } from "../SquiggleEditor"; +import { Canvas, Meta, Story, Props } from "@storybook/addon-docs"; + + + +export const Template = (props) => ; + +# Squiggle Editor + +Squiggle Editor is a Squiggle chart with a text editor included for changing +the distribution. + + + + {Template.bind({})} + + From 9d189d489a69411d5ea30b55ff8d365123715369 Mon Sep 17 00:00:00 2001 From: Sam Nolan Date: Thu, 31 Mar 2022 10:19:01 +1100 Subject: [PATCH 33/41] Removes the action bubble from vega charts --- packages/components/src/SquiggleChart.tsx | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/components/src/SquiggleChart.tsx b/packages/components/src/SquiggleChart.tsx index 47087481..d7e79075 100644 --- a/packages/components/src/SquiggleChart.tsx +++ b/packages/components/src/SquiggleChart.tsx @@ -13,11 +13,11 @@ import * as chartSpecification from "./spec-distributions.json"; import * as percentilesSpec from "./spec-percentiles.json"; let SquiggleVegaChart = createClassFromSpec({ - spec: chartSpecification as Spec, + spec: chartSpecification as Spec }); let SquigglePercentilesChart = createClassFromSpec({ - spec: percentilesSpec as Spec, + spec: percentilesSpec as Spec }); export interface SquiggleChartProps { @@ -74,7 +74,7 @@ export const SquiggleChart: React.FC = (props) => { y: y, })); - return ; + return ; } else if (shape.tag === "Discrete") { let xyShape = shape.value.xyShape; let totalY = xyShape.ys.reduce((a, b) => a + b); @@ -89,7 +89,7 @@ export const SquiggleChart: React.FC = (props) => { y: y, })); - return ; + return ; } else if (shape.tag === "Mixed") { let discreteShape = shape.value.discrete.xyShape; let totalDiscrete = discreteShape.ys.reduce((a, b) => a + b); @@ -156,6 +156,7 @@ export const SquiggleChart: React.FC = (props) => { return ( ); } @@ -195,7 +196,10 @@ export const SquiggleChart: React.FC = (props) => { return null; } }); - return x !== null) }} />; + return x !== null) }} + actions={false} + />; } }); return <>{chartResults}; From dbac6936075b3f612a6958e8d0657e12003aadb2 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Thu, 31 Mar 2022 08:37:04 -0400 Subject: [PATCH 34/41] Minor fixes for PR --- .../__tests__/GenericDist/GenericOperation__Test.res | 2 +- .../GenericDist/GenericDist_GenericOperation.res | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res b/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res index 9dfadbf8..2cb65a55 100644 --- a/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res +++ b/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res @@ -62,7 +62,7 @@ describe("toPointSet", () => { expect(result)->toEqual(#GenDistError(Other("Converting sampleSet to pointSet failed"))) }) - test("on sample set", () => { + Skip.test("on sample set", () => { let result = run(#fromDist(#toDist(#toPointSet), normalDist)) ->fmap(#fromDist(#toDist(#toSampleSet(1000)))) diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res index 5a893150..b6c3814a 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res @@ -48,8 +48,8 @@ let fromResult = (r: result): outputType => | Error(e) => #GenDistError(e) } -let outputToDistResult = (b: outputType): result => - switch b { +let outputToDistResult = (o: outputType): result => + switch o { | #Dist(r) => Ok(r) | #GenDistError(r) => Error(r) | _ => Error(Unreachable) @@ -90,8 +90,8 @@ let rec run = (extra, fnName: operation): outputType => { (), )->outputToDistResult - let fromDistFn = (subFn: GenericDist_Types.Operation.fromDist, dist: genericDist) => - switch subFn { + let fromDistFn = (subFnName: GenericDist_Types.Operation.fromDist, dist: genericDist) => + switch subFnName { | #toFloat(fnName) => GenericDist.operationToFloat(dist, toPointSet, fnName)->E.R2.fmap(r => #Float(r))->fromResult | #toString => dist->GenericDist.toString->(r => #String(r)) @@ -125,8 +125,8 @@ let rec run = (extra, fnName: operation): outputType => { } switch fnName { - | #fromDist(subFn, dist) => fromDistFn(subFn, dist) - | #fromFloat(subFn, float) => reCall(~fnName=#fromDist(subFn, GenericDist.fromFloat(float)), ()) + | #fromDist(subFnName, dist) => fromDistFn(subFnName, dist) + | #fromFloat(subFnName, float) => reCall(~fnName=#fromDist(subFnName, GenericDist.fromFloat(float)), ()) | #mixture(dists) => dists->GenericDist.mixture(scaleMultiply, pointwiseAdd)->E.R2.fmap(r => #Dist(r))->fromResult } From d61f521a0e3c15d3061e37c20b822c5c42827e6a Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Thu, 31 Mar 2022 08:41:50 -0400 Subject: [PATCH 35/41] fmap -> outputmap --- .../GenericDist/GenericOperation__Test.res | 16 ++++++++-------- .../GenericDist/GenericDist_GenericOperation.res | 2 +- .../GenericDist_GenericOperation.resi | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res b/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res index 2cb65a55..59f9b126 100644 --- a/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res +++ b/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res @@ -12,9 +12,9 @@ let normalDist20: GenericDist_Types.genericDist = #Symbolic(#Normal({mean: 20.0, let uniformDist: GenericDist_Types.genericDist = #Symbolic(#Uniform({low: 9.0, high: 10.0})) let {toFloat, toDist, toString, toError} = module(GenericDist_GenericOperation.Output) -let {run, fmap} = module(GenericDist_GenericOperation) +let {run, outputMap} = module(GenericDist_GenericOperation) let run = run(params) -let fmap = fmap(params) +let outputMap = outputMap(params) let toExt: option<'a> => 'a = E.O.toExt( "Should be impossible to reach (This error is in test file)", ) @@ -37,7 +37,7 @@ describe("mixture", () => { test("on two normal distributions", () => { let result = run(#mixture([(normalDist10, 0.5), (normalDist20, 0.5)])) - ->fmap(#fromDist(#toFloat(#Mean))) + ->outputMap(#fromDist(#toFloat(#Mean))) ->toFloat ->toExt expect(result)->toBeCloseTo(15.28) @@ -48,7 +48,7 @@ describe("toPointSet", () => { test("on symbolic normal distribution", () => { let result = run(#fromDist(#toDist(#toPointSet), normalDist)) - ->fmap(#fromDist(#toFloat(#Mean))) + ->outputMap(#fromDist(#toFloat(#Mean))) ->toFloat ->toExt expect(result)->toBeCloseTo(5.09) @@ -56,7 +56,7 @@ describe("toPointSet", () => { test("on sample set distribution with under 4 points", () => { let result = - run(#fromDist(#toDist(#toPointSet), #SampleSet([0.0, 1.0, 2.0, 3.0])))->fmap( + 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"))) @@ -65,9 +65,9 @@ describe("toPointSet", () => { Skip.test("on sample set", () => { let result = run(#fromDist(#toDist(#toPointSet), normalDist)) - ->fmap(#fromDist(#toDist(#toSampleSet(1000)))) - ->fmap(#fromDist(#toDist(#toPointSet))) - ->fmap(#fromDist(#toFloat(#Mean))) + ->outputMap(#fromDist(#toDist(#toSampleSet(1000)))) + ->outputMap(#fromDist(#toDist(#toPointSet))) + ->outputMap(#fromDist(#toFloat(#Mean))) ->toFloat ->toExt expect(result)->toBeCloseTo(5.09) diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res index b6c3814a..1c84dcd6 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res @@ -135,7 +135,7 @@ let rec run = (extra, fnName: operation): outputType => { let runFromDist = (extra, fnName, dist) => run(extra, #fromDist(fnName, dist)) let runFromFloat = (extra, fnName, float) => run(extra, #fromFloat(fnName, float)) -let fmap = ( +let outputMap = ( extra, input: outputType, fn: GenericDist_Types.Operation.singleParamaterFunction, diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi index f8acdc42..37d6593a 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi @@ -17,7 +17,7 @@ let runFromDist: ( GenericDist_Types.genericDist, ) => outputType let runFromFloat: (params, GenericDist_Types.Operation.fromDist, float) => outputType -let fmap: (params, outputType, GenericDist_Types.Operation.singleParamaterFunction) => outputType +let outputMap: (params, outputType, GenericDist_Types.Operation.singleParamaterFunction) => outputType module Output: { let toDist: outputType => option From f2d03c8f1188e11d92eb79d8dd14fa45a351fdde Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Thu, 31 Mar 2022 09:19:27 -0400 Subject: [PATCH 36/41] Added named paramaters to most GenericDist functions --- .../src/rescript/GenericDist/GenericDist.res | 59 ++++++++++--------- .../src/rescript/GenericDist/GenericDist.resi | 40 +++++++++---- .../GenericDist_GenericOperation.res | 54 ++++++++++------- .../GenericDist/GenericDist_Types.res | 4 +- 4 files changed, 94 insertions(+), 63 deletions(-) diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res index 91e9daf6..bb48c36e 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res @@ -29,10 +29,14 @@ let normalize = (t: t) => | #SampleSet(_) => t } -let operationToFloat = (t, toPointSet: toPointSetFn, fnName) => { +let operationToFloat = ( + t, + ~toPointSetFn: toPointSetFn, + ~operation: Operation.distToFloatOperation, +) => { let symbolicSolution = switch t { | #Symbolic(r) => - switch SymbolicDist.T.operate(fnName, r) { + switch SymbolicDist.T.operate(operation, r) { | Ok(f) => Some(f) | _ => None } @@ -41,7 +45,7 @@ let operationToFloat = (t, toPointSet: toPointSetFn, fnName) => { switch symbolicSolution { | Some(r) => Ok(r) - | None => toPointSet(t)->E.R2.fmap(PointSetDist.operate(fnName)) + | None => toPointSetFn(t)->E.R2.fmap(PointSetDist.operate(operation)) } } @@ -84,9 +88,10 @@ module Truncate = { let run = ( t: t, - toPointSet: toPointSetFn, - leftCutoff: option, - rightCutoff: option, + ~toPointSetFn: toPointSetFn, + ~leftCutoff=None: option, + ~rightCutoff=None: option, + (), ): result => { let doesNotNeedCutoff = E.O.isNone(leftCutoff) && E.O.isNone(rightCutoff) if doesNotNeedCutoff { @@ -95,7 +100,7 @@ module Truncate = { switch trySymbolicSimplification(leftCutoff, rightCutoff, t) { | Some(r) => Ok(r) | None => - toPointSet(t)->E.R2.fmap(t => + toPointSetFn(t)->E.R2.fmap(t => #PointSet(PointSetDist.T.truncate(leftCutoff, rightCutoff, t)) ) } @@ -168,20 +173,20 @@ module AlgebraicCombination = { let run = ( t1: t, - toPointSet: toPointSetFn, - toSampleSet: toSampleSetFn, - algebraicOp, - t2: t, + ~toPointSetFn: toPointSetFn, + ~toSampleSetFn: toSampleSetFn, + ~operation, + ~t2: t, ): result => { - switch tryAnalyticalSimplification(algebraicOp, t1, t2) { + switch tryAnalyticalSimplification(operation, t1, t2) { | Some(Ok(symbolicDist)) => Ok(#Symbolic(symbolicDist)) | Some(Error(e)) => Error(Other(e)) | None => switch chooseConvolutionOrMonteCarlo(t1, t2) { | #CalculateWithMonteCarlo => - runMonteCarlo(toSampleSet, algebraicOp, t1, t2)->E.R2.fmap(r => #SampleSet(r)) + runMonteCarlo(toSampleSetFn, operation, t1, t2)->E.R2.fmap(r => #SampleSet(r)) | #CalculateWithConvolution => - runConvolution(toPointSet, algebraicOp, t1, t2)->E.R2.fmap(r => #PointSet(r)) + runConvolution(toPointSetFn, operation, t1, t2)->E.R2.fmap(r => #PointSet(r)) } } } @@ -190,11 +195,11 @@ module AlgebraicCombination = { let algebraicCombination = AlgebraicCombination.run //TODO: Add faster pointwiseCombine fn -let pointwiseCombination = (t1: t, toPointSet: toPointSetFn, operation, t2: t): result< +let pointwiseCombination = (t1: t, ~toPointSetFn: toPointSetFn, ~operation, ~t2: t): result< t, error, > => { - E.R.merge(toPointSet(t1), toPointSet(t2)) + E.R.merge(toPointSetFn(t1), toPointSetFn(t2)) ->E.R2.fmap(((t1, t2)) => PointSetDist.combinePointwise(GenericDist_Types.Operation.arithmeticToFn(operation), t1, t2) ) @@ -203,22 +208,22 @@ let pointwiseCombination = (t1: t, toPointSet: toPointSetFn, operation, t2: t): let pointwiseCombinationFloat = ( t: t, - toPointSet: toPointSetFn, - operation: GenericDist_Types.Operation.arithmeticOperation, - f: float, + ~toPointSetFn: toPointSetFn, + ~operation: GenericDist_Types.Operation.arithmeticOperation, + ~float: float, ): result => { let m = switch operation { | #Add | #Subtract => Error(GenericDist_Types.DistributionVerticalShiftIsInvalid) | (#Multiply | #Divide | #Exponentiate | #Log) as operation => - toPointSet(t)->E.R2.fmap(t => { + toPointSetFn(t)->E.R2.fmap(t => { //TODO: Move to PointSet codebase let fn = (secondary, main) => Operation.Scale.toFn(operation, main, secondary) let integralSumCacheFn = Operation.Scale.toIntegralSumCacheFn(operation) let integralCacheFn = Operation.Scale.toIntegralCacheFn(operation) PointSetDist.T.mapY( - ~integralSumCacheFn=integralSumCacheFn(f), - ~integralCacheFn=integralCacheFn(f), - ~fn=fn(f), + ~integralSumCacheFn=integralSumCacheFn(float), + ~integralCacheFn=integralCacheFn(float), + ~fn=fn(float), t, ) }) @@ -230,8 +235,8 @@ let pointwiseCombinationFloat = ( //Note: If the inputs are not normalized, this will return poor results. The weights probably refer to the post-normalized forms. It would be good to apply a catch to this. let mixture = ( values: array<(t, float)>, - scaleMultiply: scaleMultiplyFn, - pointwiseAdd: pointwiseAddFn, + ~scaleMultiplyFn: scaleMultiplyFn, + ~pointwiseAddFn: pointwiseAddFn, ) => { if E.A.length(values) == 0 { Error(GenericDist_Types.Other("mixture must have at least 1 element")) @@ -239,13 +244,13 @@ let mixture = ( let totalWeight = values->E.A2.fmap(E.Tuple2.second)->E.A.Floats.sum let properlyWeightedValues = values - ->E.A2.fmap(((dist, weight)) => scaleMultiply(dist, weight /. totalWeight)) + ->E.A2.fmap(((dist, weight)) => scaleMultiplyFn(dist, weight /. totalWeight)) ->E.A.R.firstErrorOrOpen properlyWeightedValues->E.R.bind(values => { values |> Js.Array.sliceFrom(1) |> E.A.fold_left( - (acc, x) => E.R.bind(acc, acc => pointwiseAdd(acc, x)), + (acc, x) => E.R.bind(acc, acc => pointwiseAddFn(acc, x)), Ok(E.A.unsafe_get(values, 0)), ) }) diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.resi b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.resi index b7d6bb26..92830670 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.resi +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.resi @@ -13,32 +13,46 @@ let toString: t => string let normalize: t => t -let operationToFloat: (t, toPointSetFn, Operation.distToFloatOperation) => result +let operationToFloat: ( + t, + ~toPointSetFn: toPointSetFn, + ~operation: Operation.distToFloatOperation, +) => result let toPointSet: (t, int) => result -let truncate: (t, toPointSetFn, option, option) => result +let truncate: ( + t, + ~toPointSetFn: toPointSetFn, + ~leftCutoff: option=?, + ~rightCutoff: option=?, + unit, +) => result let algebraicCombination: ( t, - toPointSetFn, - toSampleSetFn, - GenericDist_Types.Operation.arithmeticOperation, - t, + ~toPointSetFn: toPointSetFn, + ~toSampleSetFn: toSampleSetFn, + ~operation: GenericDist_Types.Operation.arithmeticOperation, + ~t2: t, ) => result let pointwiseCombination: ( t, - toPointSetFn, - GenericDist_Types.Operation.arithmeticOperation, - t, + ~toPointSetFn: toPointSetFn, + ~operation: GenericDist_Types.Operation.arithmeticOperation, + ~t2: t, ) => result let pointwiseCombinationFloat: ( t, - toPointSetFn, - GenericDist_Types.Operation.arithmeticOperation, - float, + ~toPointSetFn: toPointSetFn, + ~operation: GenericDist_Types.Operation.arithmeticOperation, + ~float: float, ) => result -let mixture: (array<(t, float)>, scaleMultiplyFn, pointwiseAddFn) => result +let mixture: ( + array<(t, float)>, + ~scaleMultiplyFn: scaleMultiplyFn, + ~pointwiseAddFn: pointwiseAddFn, +) => result diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res index 1c84dcd6..a90bb824 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res @@ -48,11 +48,17 @@ let fromResult = (r: result): outputType => | Error(e) => #GenDistError(e) } +//This is used to catch errors in other switch statements. +let _errorMap = (o: outputType): error => + switch o { + | #GenDistError(r) => r + | _ => Unreachable + } + let outputToDistResult = (o: outputType): result => switch o { | #Dist(r) => Ok(r) - | #GenDistError(r) => Error(r) - | _ => Error(Unreachable) + | r => Error(_errorMap(r)) } let rec run = (extra, fnName: operation): outputType => { @@ -62,19 +68,17 @@ let rec run = (extra, fnName: operation): outputType => { run(extra, fnName) } - let toPointSet = r => { + let toPointSetFn = r => { switch reCall(~fnName=#fromDist(#toDist(#toPointSet), r), ()) { | #Dist(#PointSet(p)) => Ok(p) - | #GenDistError(r) => Error(r) - | _ => Error(Unreachable) + | r => Error(_errorMap(r)) } } - let toSampleSet = r => { + let toSampleSetFn = r => { switch reCall(~fnName=#fromDist(#toDist(#toSampleSet(sampleCount)), r), ()) { | #Dist(#SampleSet(p)) => Ok(p) - | #GenDistError(r) => Error(r) - | _ => Error(Unreachable) + | r => Error(_errorMap(r)) } } @@ -93,42 +97,50 @@ let rec run = (extra, fnName: operation): outputType => { let fromDistFn = (subFnName: GenericDist_Types.Operation.fromDist, dist: genericDist) => switch subFnName { | #toFloat(fnName) => - GenericDist.operationToFloat(dist, toPointSet, fnName)->E.R2.fmap(r => #Float(r))->fromResult + GenericDist.operationToFloat(dist, ~toPointSetFn, ~operation=fnName) + ->E.R2.fmap(r => #Float(r)) + ->fromResult | #toString => dist->GenericDist.toString->(r => #String(r)) - | #toDist(#consoleLog) => { + | #toDist(#inspect) => { Js.log2("Console log requested: ", dist) #Dist(dist) } | #toDist(#normalize) => dist->GenericDist.normalize->(r => #Dist(r)) - | #toDist(#truncate(left, right)) => - dist->GenericDist.truncate(toPointSet, left, right)->E.R2.fmap(r => #Dist(r))->fromResult + | #toDist(#truncate(leftCutoff, rightCutoff)) => + GenericDist.truncate(~toPointSetFn, ~leftCutoff, ~rightCutoff, dist, ()) + ->E.R2.fmap(r => #Dist(r)) + ->fromResult | #toDist(#toPointSet) => dist->GenericDist.toPointSet(xyPointLength)->E.R2.fmap(r => #Dist(#PointSet(r)))->fromResult | #toDist(#toSampleSet(n)) => dist->GenericDist.sampleN(n)->E.R2.fmap(r => #Dist(#SampleSet(r)))->fromResult | #toDistCombination(#Algebraic, _, #Float(_)) => #GenDistError(NotYetImplemented) - | #toDistCombination(#Algebraic, operation, #Dist(dist2)) => + | #toDistCombination(#Algebraic, operation, #Dist(t2)) => dist - ->GenericDist.algebraicCombination(toPointSet, toSampleSet, operation, dist2) + ->GenericDist.algebraicCombination(~toPointSetFn, ~toSampleSetFn, ~operation, ~t2) ->E.R2.fmap(r => #Dist(r)) ->fromResult - | #toDistCombination(#Pointwise, operation, #Dist(dist2)) => + | #toDistCombination(#Pointwise, operation, #Dist(t2)) => dist - ->GenericDist.pointwiseCombination(toPointSet, operation, dist2) + ->GenericDist.pointwiseCombination(~toPointSetFn, ~operation, ~t2) ->E.R2.fmap(r => #Dist(r)) ->fromResult - | #toDistCombination(#Pointwise, operation, #Float(f)) => + | #toDistCombination(#Pointwise, operation, #Float(float)) => dist - ->GenericDist.pointwiseCombinationFloat(toPointSet, operation, f) + ->GenericDist.pointwiseCombinationFloat(~toPointSetFn, ~operation, ~float) ->E.R2.fmap(r => #Dist(r)) ->fromResult } switch fnName { | #fromDist(subFnName, dist) => fromDistFn(subFnName, dist) - | #fromFloat(subFnName, float) => reCall(~fnName=#fromDist(subFnName, GenericDist.fromFloat(float)), ()) + | #fromFloat(subFnName, float) => + reCall(~fnName=#fromDist(subFnName, GenericDist.fromFloat(float)), ()) | #mixture(dists) => - dists->GenericDist.mixture(scaleMultiply, pointwiseAdd)->E.R2.fmap(r => #Dist(r))->fromResult + dists + ->GenericDist.mixture(~scaleMultiplyFn=scaleMultiply, ~pointwiseAddFn=pointwiseAdd) + ->E.R2.fmap(r => #Dist(r)) + ->fromResult } } @@ -148,4 +160,4 @@ let outputMap = ( | (#fromFloat(_), _) => Error(Other("Expected float, got something else")) } newFnCall->E.R2.fmap(r => run(extra, r))->fromResult -} \ No newline at end of file +} diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res index 3a55ee63..e8b9a0c0 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res @@ -48,7 +48,7 @@ module Operation = { | #toPointSet | #toSampleSet(int) | #truncate(option, option) - | #consoleLog + | #inspect ] type toFloatArray = [ @@ -85,7 +85,7 @@ module Operation = { | #toDist(#toPointSet) => `toPointSet` | #toDist(#toSampleSet(r)) => `toSampleSet(${E.I.toString(r)})` | #toDist(#truncate(_, _)) => `truncate` - | #toDist(#consoleLog) => `consoleLog` + | #toDist(#inspect) => `inspect` | #toString => `toString` | #toDistCombination(#Algebraic, _, _) => `algebraic` | #toDistCombination(#Pointwise, _, _) => `pointwise` From d82615cef0cfccd28ab59d25236afa5a05a1ed15 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Thu, 31 Mar 2022 09:27:36 -0400 Subject: [PATCH 37/41] Changed GenericDist outputType from polymorphic variant to regular variant --- .../GenericDist/GenericOperation__Test.res | 6 +- .../GenericDist_GenericOperation.res | 59 +++++++++---------- .../GenericDist_GenericOperation.resi | 17 +++--- 3 files changed, 42 insertions(+), 40 deletions(-) diff --git a/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res b/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res index 59f9b126..52ab6c19 100644 --- a/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res +++ b/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res @@ -22,14 +22,14 @@ let toExt: option<'a> => 'a = E.O.toExt( describe("normalize", () => { test("has no impact on normal dist", () => { let result = run(#fromDist(#toDist(#normalize), normalDist)) - expect(result)->toEqual(#Dist(normalDist)) + expect(result)->toEqual(Dist(normalDist)) }) }) describe("mean", () => { test("for a normal distribution", () => { let result = GenericDist_GenericOperation.run(params, #fromDist(#toFloat(#Mean), normalDist)) - expect(result)->toEqual(#Float(5.0)) + expect(result)->toEqual(Float(5.0)) }) }) @@ -59,7 +59,7 @@ describe("toPointSet", () => { 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"))) + expect(result)->toEqual(GenDistError(Other("Converting sampleSet to pointSet failed"))) }) Skip.test("on sample set", () => { diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res index a90bb824..e744543e 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res @@ -9,35 +9,34 @@ type params = { xyPointLength: int, } -type outputType = [ - | #Dist(genericDist) - | #Float(float) - | #String(string) - | #GenDistError(error) -] +type outputType = + | Dist(GenericDist_Types.genericDist) + | Float(float) + | String(string) + | GenDistError(GenericDist_Types.error) module Output = { let toDist = (o: outputType) => switch o { - | #Dist(d) => Some(d) + | Dist(d) => Some(d) | _ => None } let toFloat = (o: outputType) => switch o { - | #Float(d) => Some(d) + | Float(d) => Some(d) | _ => None } let toString = (o: outputType) => switch o { - | #String(d) => Some(d) + | String(d) => Some(d) | _ => None } let toError = (o: outputType) => switch o { - | #GenDistError(d) => Some(d) + | GenDistError(d) => Some(d) | _ => None } } @@ -45,19 +44,19 @@ module Output = { let fromResult = (r: result): outputType => switch r { | Ok(o) => o - | Error(e) => #GenDistError(e) + | Error(e) => GenDistError(e) } //This is used to catch errors in other switch statements. let _errorMap = (o: outputType): error => switch o { - | #GenDistError(r) => r + | GenDistError(r) => r | _ => Unreachable } let outputToDistResult = (o: outputType): result => switch o { - | #Dist(r) => Ok(r) + | Dist(r) => Ok(r) | r => Error(_errorMap(r)) } @@ -70,14 +69,14 @@ let rec run = (extra, fnName: operation): outputType => { let toPointSetFn = r => { switch reCall(~fnName=#fromDist(#toDist(#toPointSet), r), ()) { - | #Dist(#PointSet(p)) => Ok(p) + | Dist(#PointSet(p)) => Ok(p) | r => Error(_errorMap(r)) } } let toSampleSetFn = r => { switch reCall(~fnName=#fromDist(#toDist(#toSampleSet(sampleCount)), r), ()) { - | #Dist(#SampleSet(p)) => Ok(p) + | Dist(#SampleSet(p)) => Ok(p) | r => Error(_errorMap(r)) } } @@ -98,37 +97,37 @@ let rec run = (extra, fnName: operation): outputType => { switch subFnName { | #toFloat(fnName) => GenericDist.operationToFloat(dist, ~toPointSetFn, ~operation=fnName) - ->E.R2.fmap(r => #Float(r)) + ->E.R2.fmap(r => Float(r)) ->fromResult - | #toString => dist->GenericDist.toString->(r => #String(r)) + | #toString => dist->GenericDist.toString->String | #toDist(#inspect) => { Js.log2("Console log requested: ", dist) - #Dist(dist) + Dist(dist) } - | #toDist(#normalize) => dist->GenericDist.normalize->(r => #Dist(r)) + | #toDist(#normalize) => dist->GenericDist.normalize->Dist | #toDist(#truncate(leftCutoff, rightCutoff)) => GenericDist.truncate(~toPointSetFn, ~leftCutoff, ~rightCutoff, dist, ()) - ->E.R2.fmap(r => #Dist(r)) + ->E.R2.fmap(r => Dist(r)) ->fromResult | #toDist(#toPointSet) => - dist->GenericDist.toPointSet(xyPointLength)->E.R2.fmap(r => #Dist(#PointSet(r)))->fromResult + dist->GenericDist.toPointSet(xyPointLength)->E.R2.fmap(r => Dist(#PointSet(r)))->fromResult | #toDist(#toSampleSet(n)) => - dist->GenericDist.sampleN(n)->E.R2.fmap(r => #Dist(#SampleSet(r)))->fromResult - | #toDistCombination(#Algebraic, _, #Float(_)) => #GenDistError(NotYetImplemented) + dist->GenericDist.sampleN(n)->E.R2.fmap(r => Dist(#SampleSet(r)))->fromResult + | #toDistCombination(#Algebraic, _, #Float(_)) => GenDistError(NotYetImplemented) | #toDistCombination(#Algebraic, operation, #Dist(t2)) => dist ->GenericDist.algebraicCombination(~toPointSetFn, ~toSampleSetFn, ~operation, ~t2) - ->E.R2.fmap(r => #Dist(r)) + ->E.R2.fmap(r => Dist(r)) ->fromResult | #toDistCombination(#Pointwise, operation, #Dist(t2)) => dist ->GenericDist.pointwiseCombination(~toPointSetFn, ~operation, ~t2) - ->E.R2.fmap(r => #Dist(r)) + ->E.R2.fmap(r => Dist(r)) ->fromResult | #toDistCombination(#Pointwise, operation, #Float(float)) => dist ->GenericDist.pointwiseCombinationFloat(~toPointSetFn, ~operation, ~float) - ->E.R2.fmap(r => #Dist(r)) + ->E.R2.fmap(r => Dist(r)) ->fromResult } @@ -139,7 +138,7 @@ let rec run = (extra, fnName: operation): outputType => { | #mixture(dists) => dists ->GenericDist.mixture(~scaleMultiplyFn=scaleMultiply, ~pointwiseAddFn=pointwiseAdd) - ->E.R2.fmap(r => #Dist(r)) + ->E.R2.fmap(r => Dist(r)) ->fromResult } } @@ -153,9 +152,9 @@ let outputMap = ( fn: GenericDist_Types.Operation.singleParamaterFunction, ): outputType => { let newFnCall: result = switch (fn, input) { - | (#fromDist(fromDist), #Dist(o)) => Ok(#fromDist(fromDist, o)) - | (#fromFloat(fromDist), #Float(o)) => Ok(#fromFloat(fromDist, o)) - | (_, #GenDistError(r)) => Error(r) + | (#fromDist(fromDist), Dist(o)) => Ok(#fromDist(fromDist, o)) + | (#fromFloat(fromDist), Float(o)) => Ok(#fromFloat(fromDist, o)) + | (_, GenDistError(r)) => Error(r) | (#fromDist(_), _) => Error(Other("Expected dist, got something else")) | (#fromFloat(_), _) => Error(Other("Expected float, got something else")) } diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi index 37d6593a..22864ed3 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi @@ -3,12 +3,11 @@ type params = { xyPointLength: int, } -type outputType = [ - | #Dist(GenericDist_Types.genericDist) - | #GenDistError(GenericDist_Types.error) - | #Float(float) - | #String(string) -] +type outputType = + | Dist(GenericDist_Types.genericDist) + | Float(float) + | String(string) + | GenDistError(GenericDist_Types.error) let run: (params, GenericDist_Types.Operation.genericFunctionCallInfo) => outputType let runFromDist: ( @@ -17,7 +16,11 @@ let runFromDist: ( GenericDist_Types.genericDist, ) => outputType let runFromFloat: (params, GenericDist_Types.Operation.fromDist, float) => outputType -let outputMap: (params, outputType, GenericDist_Types.Operation.singleParamaterFunction) => outputType +let outputMap: ( + params, + outputType, + GenericDist_Types.Operation.singleParamaterFunction, +) => outputType module Output: { let toDist: outputType => option From 4702cf3e5ea0c46ca6638d23203eb6066cefab19 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Thu, 31 Mar 2022 13:26:29 -0400 Subject: [PATCH 38/41] Reorganized Output module in GenericOperation --- .../GenericDist/GenericOperation__Test.res | 5 +- .../GenericDist_GenericOperation.res | 119 ++++++++++-------- .../GenericDist_GenericOperation.resi | 18 ++- .../squiggle-lang/src/rescript/utility/E.res | 4 + 4 files changed, 79 insertions(+), 67 deletions(-) diff --git a/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res b/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res index 52ab6c19..1e5b5397 100644 --- a/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res +++ b/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res @@ -12,9 +12,10 @@ let normalDist20: GenericDist_Types.genericDist = #Symbolic(#Normal({mean: 20.0, let uniformDist: GenericDist_Types.genericDist = #Symbolic(#Uniform({low: 9.0, high: 10.0})) let {toFloat, toDist, toString, toError} = module(GenericDist_GenericOperation.Output) -let {run, outputMap} = module(GenericDist_GenericOperation) +let {run} = module(GenericDist_GenericOperation) +let {fmap} = module(GenericDist_GenericOperation.Output) let run = run(params) -let outputMap = outputMap(params) +let outputMap = fmap(params) let toExt: option<'a> => 'a = E.O.toExt( "Should be impossible to reach (This error is in test file)", ) diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res index e744543e..9cc93949 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res @@ -9,57 +9,59 @@ type params = { xyPointLength: int, } -type outputType = +type outputType = | Dist(GenericDist_Types.genericDist) | Float(float) | String(string) | GenDistError(GenericDist_Types.error) -module Output = { - let toDist = (o: outputType) => - switch o { +/* +We're going to add another function to this module later, so first define a +local version, which is not exported. +*/ +module OutputLocal = { + type t = outputType + + let toError = (t: outputType) => + switch t { + | GenDistError(d) => Some(d) + | _ => None + } + + let toErrorOrUnreachable = (t: t): error => t->toError->E.O2.default((Unreachable: error)) + + let toDistR = (t: t): result => + switch t { + | Dist(r) => Ok(r) + | e => Error(toErrorOrUnreachable(e)) + } + + let toDist = (t: t) => + switch t { | Dist(d) => Some(d) | _ => None } - let toFloat = (o: outputType) => - switch o { + let toFloat = (t: t) => + switch t { | Float(d) => Some(d) | _ => None } - let toString = (o: outputType) => - switch o { + let toString = (t: t) => + switch t { | String(d) => Some(d) | _ => None } - let toError = (o: outputType) => - switch o { - | GenDistError(d) => Some(d) - | _ => None + //This is used to catch errors in other switch statements. + let fromResult = (r: result): outputType => + switch r { + | Ok(t) => t + | Error(e) => GenDistError(e) } } -let fromResult = (r: result): outputType => - switch r { - | Ok(o) => o - | Error(e) => GenDistError(e) - } - -//This is used to catch errors in other switch statements. -let _errorMap = (o: outputType): error => - switch o { - | GenDistError(r) => r - | _ => Unreachable - } - -let outputToDistResult = (o: outputType): result => - switch o { - | Dist(r) => Ok(r) - | r => Error(_errorMap(r)) - } - let rec run = (extra, fnName: operation): outputType => { let {sampleCount, xyPointLength} = extra @@ -70,14 +72,14 @@ let rec run = (extra, fnName: operation): outputType => { let toPointSetFn = r => { switch reCall(~fnName=#fromDist(#toDist(#toPointSet), r), ()) { | Dist(#PointSet(p)) => Ok(p) - | r => Error(_errorMap(r)) + | e => Error(OutputLocal.toErrorOrUnreachable(e)) } } let toSampleSetFn = r => { switch reCall(~fnName=#fromDist(#toDist(#toSampleSet(sampleCount)), r), ()) { | Dist(#SampleSet(p)) => Ok(p) - | r => Error(_errorMap(r)) + | e => Error(OutputLocal.toErrorOrUnreachable(e)) } } @@ -85,20 +87,20 @@ let rec run = (extra, fnName: operation): outputType => { reCall( ~fnName=#fromDist(#toDistCombination(#Pointwise, #Multiply, #Float(weight)), r), (), - )->outputToDistResult + )->OutputLocal.toDistR let pointwiseAdd = (r1, r2) => reCall( ~fnName=#fromDist(#toDistCombination(#Pointwise, #Add, #Dist(r2)), r1), (), - )->outputToDistResult + )->OutputLocal.toDistR let fromDistFn = (subFnName: GenericDist_Types.Operation.fromDist, dist: genericDist) => switch subFnName { | #toFloat(fnName) => GenericDist.operationToFloat(dist, ~toPointSetFn, ~operation=fnName) ->E.R2.fmap(r => Float(r)) - ->fromResult + ->OutputLocal.fromResult | #toString => dist->GenericDist.toString->String | #toDist(#inspect) => { Js.log2("Console log requested: ", dist) @@ -108,27 +110,30 @@ let rec run = (extra, fnName: operation): outputType => { | #toDist(#truncate(leftCutoff, rightCutoff)) => GenericDist.truncate(~toPointSetFn, ~leftCutoff, ~rightCutoff, dist, ()) ->E.R2.fmap(r => Dist(r)) - ->fromResult + ->OutputLocal.fromResult | #toDist(#toPointSet) => - dist->GenericDist.toPointSet(xyPointLength)->E.R2.fmap(r => Dist(#PointSet(r)))->fromResult + dist + ->GenericDist.toPointSet(xyPointLength) + ->E.R2.fmap(r => Dist(#PointSet(r))) + ->OutputLocal.fromResult | #toDist(#toSampleSet(n)) => - dist->GenericDist.sampleN(n)->E.R2.fmap(r => Dist(#SampleSet(r)))->fromResult + dist->GenericDist.sampleN(n)->E.R2.fmap(r => Dist(#SampleSet(r)))->OutputLocal.fromResult | #toDistCombination(#Algebraic, _, #Float(_)) => GenDistError(NotYetImplemented) | #toDistCombination(#Algebraic, operation, #Dist(t2)) => dist ->GenericDist.algebraicCombination(~toPointSetFn, ~toSampleSetFn, ~operation, ~t2) ->E.R2.fmap(r => Dist(r)) - ->fromResult + ->OutputLocal.fromResult | #toDistCombination(#Pointwise, operation, #Dist(t2)) => dist ->GenericDist.pointwiseCombination(~toPointSetFn, ~operation, ~t2) ->E.R2.fmap(r => Dist(r)) - ->fromResult + ->OutputLocal.fromResult | #toDistCombination(#Pointwise, operation, #Float(float)) => dist ->GenericDist.pointwiseCombinationFloat(~toPointSetFn, ~operation, ~float) ->E.R2.fmap(r => Dist(r)) - ->fromResult + ->OutputLocal.fromResult } switch fnName { @@ -139,24 +144,28 @@ let rec run = (extra, fnName: operation): outputType => { dists ->GenericDist.mixture(~scaleMultiplyFn=scaleMultiply, ~pointwiseAddFn=pointwiseAdd) ->E.R2.fmap(r => Dist(r)) - ->fromResult + ->OutputLocal.fromResult } } let runFromDist = (extra, fnName, dist) => run(extra, #fromDist(fnName, dist)) let runFromFloat = (extra, fnName, float) => run(extra, #fromFloat(fnName, float)) -let outputMap = ( - extra, - input: outputType, - fn: GenericDist_Types.Operation.singleParamaterFunction, -): outputType => { - let newFnCall: result = switch (fn, input) { - | (#fromDist(fromDist), Dist(o)) => Ok(#fromDist(fromDist, o)) - | (#fromFloat(fromDist), Float(o)) => Ok(#fromFloat(fromDist, o)) - | (_, GenDistError(r)) => Error(r) - | (#fromDist(_), _) => Error(Other("Expected dist, got something else")) - | (#fromFloat(_), _) => Error(Other("Expected float, got something else")) +module Output = { + include OutputLocal + + let fmap = ( + extra, + input: outputType, + fn: GenericDist_Types.Operation.singleParamaterFunction, + ): outputType => { + let newFnCall: result = switch (fn, input) { + | (#fromDist(fromDist), Dist(o)) => Ok(#fromDist(fromDist, o)) + | (#fromFloat(fromDist), Float(o)) => Ok(#fromFloat(fromDist, o)) + | (_, GenDistError(r)) => Error(r) + | (#fromDist(_), _) => Error(Other("Expected dist, got something else")) + | (#fromFloat(_), _) => Error(Other("Expected float, got something else")) + } + newFnCall->E.R2.fmap(r => run(extra, r))->OutputLocal.fromResult } - newFnCall->E.R2.fmap(r => run(extra, r))->fromResult } diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi index 22864ed3..2769a505 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi @@ -16,15 +16,13 @@ let runFromDist: ( GenericDist_Types.genericDist, ) => outputType let runFromFloat: (params, GenericDist_Types.Operation.fromDist, float) => outputType -let outputMap: ( - params, - outputType, - GenericDist_Types.Operation.singleParamaterFunction, -) => outputType module Output: { - let toDist: outputType => option - let toFloat: outputType => option - let toString: outputType => option - let toError: outputType => option -} + type t = outputType + let toDist: t => option + let toDistR: t => result + let toFloat: t => option + let toString: t => option + let toError: t => option + let fmap: (params, t, GenericDist_Types.Operation.singleParamaterFunction) => t +} \ No newline at end of file diff --git a/packages/squiggle-lang/src/rescript/utility/E.res b/packages/squiggle-lang/src/rescript/utility/E.res index 121ecb91..9c6c2a73 100644 --- a/packages/squiggle-lang/src/rescript/utility/E.res +++ b/packages/squiggle-lang/src/rescript/utility/E.res @@ -98,6 +98,10 @@ module O = { let max = compare(\">") } +module O2 = { + let default = (a,b) => O.default(b,a) +} + /* Functions */ module F = { let apply = (a, e) => a |> e From 4b3f24b38daeb53db7f2117438146dcf7b74511d Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Thu, 31 Mar 2022 14:07:39 -0400 Subject: [PATCH 39/41] Converted params to env, named several arguments --- .../GenericDist/GenericOperation__Test.res | 8 +- .../src/rescript/GenericDist/GenericDist.res | 80 ++++++++++--------- .../src/rescript/GenericDist/GenericDist.resi | 16 ++-- .../GenericDist_GenericOperation.res | 55 ++++++------- .../GenericDist_GenericOperation.resi | 14 ++-- 5 files changed, 91 insertions(+), 82 deletions(-) diff --git a/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res b/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res index 1e5b5397..a5c1011f 100644 --- a/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res +++ b/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res @@ -1,7 +1,7 @@ open Jest open Expect -let params: GenericDist_GenericOperation.params = { +let env: GenericDist_GenericOperation.env = { sampleCount: 100, xyPointLength: 100, } @@ -14,8 +14,8 @@ let uniformDist: GenericDist_Types.genericDist = #Symbolic(#Uniform({low: 9.0, h let {toFloat, toDist, toString, toError} = module(GenericDist_GenericOperation.Output) let {run} = module(GenericDist_GenericOperation) let {fmap} = module(GenericDist_GenericOperation.Output) -let run = run(params) -let outputMap = fmap(params) +let run = run(~env) +let outputMap = fmap(~env) let toExt: option<'a> => 'a = E.O.toExt( "Should be impossible to reach (This error is in test file)", ) @@ -29,7 +29,7 @@ describe("normalize", () => { describe("mean", () => { test("for a normal distribution", () => { - let result = GenericDist_GenericOperation.run(params, #fromDist(#toFloat(#Mean), normalDist)) + let result = GenericDist_GenericOperation.run(~env, #fromDist(#toFloat(#Mean), normalDist)) expect(result)->toEqual(Float(5.0)) }) }) diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res index bb48c36e..ba293541 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res @@ -29,14 +29,14 @@ let normalize = (t: t) => | #SampleSet(_) => t } -let operationToFloat = ( +let toFloatOperation = ( t, ~toPointSetFn: toPointSetFn, - ~operation: Operation.distToFloatOperation, + ~distToFloatOperation: Operation.distToFloatOperation, ) => { let symbolicSolution = switch t { | #Symbolic(r) => - switch SymbolicDist.T.operate(operation, r) { + switch SymbolicDist.T.operate(distToFloatOperation, r) { | Ok(f) => Some(f) | _ => None } @@ -45,28 +45,26 @@ let operationToFloat = ( switch symbolicSolution { | Some(r) => Ok(r) - | None => toPointSetFn(t)->E.R2.fmap(PointSetDist.operate(operation)) + | None => toPointSetFn(t)->E.R2.fmap(PointSetDist.operate(distToFloatOperation)) } } -//TODO: Refactor this bit. -let defaultSamplingInputs: SamplingInputs.samplingInputs = { - sampleCount: 10000, - outputXYPoints: 10000, - pointSetDistLength: 1000, - kernelWidth: None, -} - //Todo: If it's a pointSet, but the xyPointLenght is different from what it has, it should change. // This is tricky because the case of discrete distributions. -let toPointSet = (t, xyPointLength): result => { +// Also, change the outputXYPoints/pointSetDistLength details +let toPointSet = (~xyPointLength, ~sampleCount, t): result => { switch t { | #PointSet(pointSet) => Ok(pointSet) | #Symbolic(r) => Ok(SymbolicDist.T.toPointSetDist(xyPointLength, r)) | #SampleSet(r) => { let response = SampleSet.toPointSetDist( ~samples=r, - ~samplingInputs=defaultSamplingInputs, + ~samplingInputs={ + sampleCount: sampleCount, + outputXYPoints: xyPointLength, + pointSetDistLength: xyPointLength, + kernelWidth: None, + }, (), ).pointSetDist switch response { @@ -119,13 +117,13 @@ let truncate = Truncate.run */ module AlgebraicCombination = { let tryAnalyticalSimplification = ( - operation: GenericDist_Types.Operation.arithmeticOperation, + arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation, t1: t, t2: t, ): option> => - switch (operation, t1, t2) { - | (operation, #Symbolic(d1), #Symbolic(d2)) => - switch SymbolicDist.T.tryAnalyticalSimplification(d1, d2, operation) { + switch (arithmeticOperation, t1, t2) { + | (arithmeticOperation, #Symbolic(d1), #Symbolic(d2)) => + switch SymbolicDist.T.tryAnalyticalSimplification(d1, d2, arithmeticOperation) { | #AnalyticalSolution(symbolicDist) => Some(Ok(symbolicDist)) | #Error(er) => Some(Error(er)) | #NoSolution => None @@ -135,23 +133,23 @@ module AlgebraicCombination = { let runConvolution = ( toPointSet: toPointSetFn, - operation: GenericDist_Types.Operation.arithmeticOperation, + arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation, t1: t, t2: t, ) => E.R.merge(toPointSet(t1), toPointSet(t2))->E.R2.fmap(((a, b)) => - PointSetDist.combineAlgebraically(operation, a, b) + PointSetDist.combineAlgebraically(arithmeticOperation, a, b) ) let runMonteCarlo = ( toSampleSet: toSampleSetFn, - operation: GenericDist_Types.Operation.arithmeticOperation, + arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation, t1: t, t2: t, ) => { - let operation = Operation.Algebraic.toFn(operation) + 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)) => operation(a, b)) + Belt.Array.zip(a, b)->E.A2.fmap(((a, b)) => arithmeticOperation(a, b)) }) } @@ -175,18 +173,18 @@ module AlgebraicCombination = { t1: t, ~toPointSetFn: toPointSetFn, ~toSampleSetFn: toSampleSetFn, - ~operation, + ~arithmeticOperation, ~t2: t, ): result => { - switch tryAnalyticalSimplification(operation, t1, t2) { + switch tryAnalyticalSimplification(arithmeticOperation, t1, t2) { | Some(Ok(symbolicDist)) => Ok(#Symbolic(symbolicDist)) | Some(Error(e)) => Error(Other(e)) | None => switch chooseConvolutionOrMonteCarlo(t1, t2) { | #CalculateWithMonteCarlo => - runMonteCarlo(toSampleSetFn, operation, t1, t2)->E.R2.fmap(r => #SampleSet(r)) + runMonteCarlo(toSampleSetFn, arithmeticOperation, t1, t2)->E.R2.fmap(r => #SampleSet(r)) | #CalculateWithConvolution => - runConvolution(toPointSetFn, operation, t1, t2)->E.R2.fmap(r => #PointSet(r)) + runConvolution(toPointSetFn, arithmeticOperation, t1, t2)->E.R2.fmap(r => #PointSet(r)) } } } @@ -195,13 +193,19 @@ module AlgebraicCombination = { let algebraicCombination = AlgebraicCombination.run //TODO: Add faster pointwiseCombine fn -let pointwiseCombination = (t1: t, ~toPointSetFn: toPointSetFn, ~operation, ~t2: t): result< - t, - error, -> => { +let pointwiseCombination = ( + t1: t, + ~toPointSetFn: toPointSetFn, + ~arithmeticOperation, + ~t2: t, +): result => { E.R.merge(toPointSetFn(t1), toPointSetFn(t2)) ->E.R2.fmap(((t1, t2)) => - PointSetDist.combinePointwise(GenericDist_Types.Operation.arithmeticToFn(operation), t1, t2) + PointSetDist.combinePointwise( + GenericDist_Types.Operation.arithmeticToFn(arithmeticOperation), + t1, + t2, + ) ) ->E.R2.fmap(r => #PointSet(r)) } @@ -209,17 +213,17 @@ let pointwiseCombination = (t1: t, ~toPointSetFn: toPointSetFn, ~operation, ~t2: let pointwiseCombinationFloat = ( t: t, ~toPointSetFn: toPointSetFn, - ~operation: GenericDist_Types.Operation.arithmeticOperation, + ~arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation, ~float: float, ): result => { - let m = switch operation { + let m = switch arithmeticOperation { | #Add | #Subtract => Error(GenericDist_Types.DistributionVerticalShiftIsInvalid) - | (#Multiply | #Divide | #Exponentiate | #Log) as operation => + | (#Multiply | #Divide | #Exponentiate | #Log) as arithmeticOperation => toPointSetFn(t)->E.R2.fmap(t => { //TODO: Move to PointSet codebase - let fn = (secondary, main) => Operation.Scale.toFn(operation, main, secondary) - let integralSumCacheFn = Operation.Scale.toIntegralSumCacheFn(operation) - let integralCacheFn = Operation.Scale.toIntegralCacheFn(operation) + let fn = (secondary, main) => Operation.Scale.toFn(arithmeticOperation, main, secondary) + let integralSumCacheFn = Operation.Scale.toIntegralSumCacheFn(arithmeticOperation) + let integralCacheFn = Operation.Scale.toIntegralCacheFn(arithmeticOperation) PointSetDist.T.mapY( ~integralSumCacheFn=integralSumCacheFn(float), ~integralCacheFn=integralCacheFn(float), diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.resi b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.resi index 92830670..f61a983f 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.resi +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.resi @@ -13,13 +13,17 @@ let toString: t => string let normalize: t => t -let operationToFloat: ( +let toFloatOperation: ( t, ~toPointSetFn: toPointSetFn, - ~operation: Operation.distToFloatOperation, + ~distToFloatOperation: Operation.distToFloatOperation, ) => result -let toPointSet: (t, int) => result +let toPointSet: ( + ~xyPointLength: int, + ~sampleCount: int, + t, +) => result let truncate: ( t, @@ -33,21 +37,21 @@ let algebraicCombination: ( t, ~toPointSetFn: toPointSetFn, ~toSampleSetFn: toSampleSetFn, - ~operation: GenericDist_Types.Operation.arithmeticOperation, + ~arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation, ~t2: t, ) => result let pointwiseCombination: ( t, ~toPointSetFn: toPointSetFn, - ~operation: GenericDist_Types.Operation.arithmeticOperation, + ~arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation, ~t2: t, ) => result let pointwiseCombinationFloat: ( t, ~toPointSetFn: toPointSetFn, - ~operation: GenericDist_Types.Operation.arithmeticOperation, + ~arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation, ~float: float, ) => result diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res index 9cc93949..51878a7d 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res @@ -1,10 +1,10 @@ -type operation = GenericDist_Types.Operation.genericFunctionCallInfo +type functionCallInfo = GenericDist_Types.Operation.genericFunctionCallInfo type genericDist = GenericDist_Types.genericDist type error = GenericDist_Types.error // TODO: It could be great to use a cache for some calculations (basically, do memoization). Also, better analytics/tracking could go a long way. -type params = { +type env = { sampleCount: int, xyPointLength: int, } @@ -62,22 +62,22 @@ module OutputLocal = { } } -let rec run = (extra, fnName: operation): outputType => { - let {sampleCount, xyPointLength} = extra +let rec run = (~env, functionCallInfo: functionCallInfo): outputType => { + let {sampleCount, xyPointLength} = env - let reCall = (~extra=extra, ~fnName=fnName, ()) => { - run(extra, fnName) + let reCall = (~env=env, ~functionCallInfo=functionCallInfo, ()) => { + run(~env, functionCallInfo) } let toPointSetFn = r => { - switch reCall(~fnName=#fromDist(#toDist(#toPointSet), r), ()) { + switch reCall(~functionCallInfo=#fromDist(#toDist(#toPointSet), r), ()) { | Dist(#PointSet(p)) => Ok(p) | e => Error(OutputLocal.toErrorOrUnreachable(e)) } } let toSampleSetFn = r => { - switch reCall(~fnName=#fromDist(#toDist(#toSampleSet(sampleCount)), r), ()) { + switch reCall(~functionCallInfo=#fromDist(#toDist(#toSampleSet(sampleCount)), r), ()) { | Dist(#SampleSet(p)) => Ok(p) | e => Error(OutputLocal.toErrorOrUnreachable(e)) } @@ -85,20 +85,20 @@ let rec run = (extra, fnName: operation): outputType => { let scaleMultiply = (r, weight) => reCall( - ~fnName=#fromDist(#toDistCombination(#Pointwise, #Multiply, #Float(weight)), r), + ~functionCallInfo=#fromDist(#toDistCombination(#Pointwise, #Multiply, #Float(weight)), r), (), )->OutputLocal.toDistR let pointwiseAdd = (r1, r2) => reCall( - ~fnName=#fromDist(#toDistCombination(#Pointwise, #Add, #Dist(r2)), r1), + ~functionCallInfo=#fromDist(#toDistCombination(#Pointwise, #Add, #Dist(r2)), r1), (), )->OutputLocal.toDistR let fromDistFn = (subFnName: GenericDist_Types.Operation.fromDist, dist: genericDist) => switch subFnName { - | #toFloat(fnName) => - GenericDist.operationToFloat(dist, ~toPointSetFn, ~operation=fnName) + | #toFloat(distToFloatOperation) => + GenericDist.toFloatOperation(dist, ~toPointSetFn, ~distToFloatOperation) ->E.R2.fmap(r => Float(r)) ->OutputLocal.fromResult | #toString => dist->GenericDist.toString->String @@ -113,33 +113,33 @@ let rec run = (extra, fnName: operation): outputType => { ->OutputLocal.fromResult | #toDist(#toPointSet) => dist - ->GenericDist.toPointSet(xyPointLength) + ->GenericDist.toPointSet(~xyPointLength, ~sampleCount) ->E.R2.fmap(r => Dist(#PointSet(r))) ->OutputLocal.fromResult | #toDist(#toSampleSet(n)) => dist->GenericDist.sampleN(n)->E.R2.fmap(r => Dist(#SampleSet(r)))->OutputLocal.fromResult | #toDistCombination(#Algebraic, _, #Float(_)) => GenDistError(NotYetImplemented) - | #toDistCombination(#Algebraic, operation, #Dist(t2)) => + | #toDistCombination(#Algebraic, arithmeticOperation, #Dist(t2)) => dist - ->GenericDist.algebraicCombination(~toPointSetFn, ~toSampleSetFn, ~operation, ~t2) + ->GenericDist.algebraicCombination(~toPointSetFn, ~toSampleSetFn, ~arithmeticOperation, ~t2) ->E.R2.fmap(r => Dist(r)) ->OutputLocal.fromResult - | #toDistCombination(#Pointwise, operation, #Dist(t2)) => + | #toDistCombination(#Pointwise, arithmeticOperation, #Dist(t2)) => dist - ->GenericDist.pointwiseCombination(~toPointSetFn, ~operation, ~t2) + ->GenericDist.pointwiseCombination(~toPointSetFn, ~arithmeticOperation, ~t2) ->E.R2.fmap(r => Dist(r)) ->OutputLocal.fromResult - | #toDistCombination(#Pointwise, operation, #Float(float)) => + | #toDistCombination(#Pointwise, arithmeticOperation, #Float(float)) => dist - ->GenericDist.pointwiseCombinationFloat(~toPointSetFn, ~operation, ~float) + ->GenericDist.pointwiseCombinationFloat(~toPointSetFn, ~arithmeticOperation, ~float) ->E.R2.fmap(r => Dist(r)) ->OutputLocal.fromResult } - switch fnName { + switch functionCallInfo { | #fromDist(subFnName, dist) => fromDistFn(subFnName, dist) | #fromFloat(subFnName, float) => - reCall(~fnName=#fromDist(subFnName, GenericDist.fromFloat(float)), ()) + reCall(~functionCallInfo=#fromDist(subFnName, GenericDist.fromFloat(float)), ()) | #mixture(dists) => dists ->GenericDist.mixture(~scaleMultiplyFn=scaleMultiply, ~pointwiseAddFn=pointwiseAdd) @@ -148,24 +148,25 @@ let rec run = (extra, fnName: operation): outputType => { } } -let runFromDist = (extra, fnName, dist) => run(extra, #fromDist(fnName, dist)) -let runFromFloat = (extra, fnName, float) => run(extra, #fromFloat(fnName, float)) +let runFromDist = (~env, ~functionCallInfo, dist) => run(~env, #fromDist(functionCallInfo, dist)) +let runFromFloat = (~env, ~functionCallInfo, float) => + run(~env, #fromFloat(functionCallInfo, float)) module Output = { include OutputLocal let fmap = ( - extra, + ~env, input: outputType, - fn: GenericDist_Types.Operation.singleParamaterFunction, + functionCallInfo: GenericDist_Types.Operation.singleParamaterFunction, ): outputType => { - let newFnCall: result = switch (fn, input) { + let newFnCall: result = switch (functionCallInfo, input) { | (#fromDist(fromDist), Dist(o)) => Ok(#fromDist(fromDist, o)) | (#fromFloat(fromDist), Float(o)) => Ok(#fromFloat(fromDist, o)) | (_, GenDistError(r)) => Error(r) | (#fromDist(_), _) => Error(Other("Expected dist, got something else")) | (#fromFloat(_), _) => Error(Other("Expected float, got something else")) } - newFnCall->E.R2.fmap(r => run(extra, r))->OutputLocal.fromResult + newFnCall->E.R2.fmap(run(~env))->OutputLocal.fromResult } } diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi index 2769a505..c9e26058 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi @@ -1,4 +1,4 @@ -type params = { +type env = { sampleCount: int, xyPointLength: int, } @@ -9,13 +9,13 @@ type outputType = | String(string) | GenDistError(GenericDist_Types.error) -let run: (params, GenericDist_Types.Operation.genericFunctionCallInfo) => outputType +let run: (~env: env, GenericDist_Types.Operation.genericFunctionCallInfo) => outputType let runFromDist: ( - params, - GenericDist_Types.Operation.fromDist, + ~env: env, + ~functionCallInfo: GenericDist_Types.Operation.fromDist, GenericDist_Types.genericDist, ) => outputType -let runFromFloat: (params, GenericDist_Types.Operation.fromDist, float) => outputType +let runFromFloat: (~env: env, ~functionCallInfo: GenericDist_Types.Operation.fromDist, float) => outputType module Output: { type t = outputType @@ -24,5 +24,5 @@ module Output: { let toFloat: t => option let toString: t => option let toError: t => option - let fmap: (params, t, GenericDist_Types.Operation.singleParamaterFunction) => t -} \ No newline at end of file + let fmap: (~env: env, t, GenericDist_Types.Operation.singleParamaterFunction) => t +} From 680726e8b075e242c54a6bf468de7c8385f5c593 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Thu, 31 Mar 2022 14:15:21 -0400 Subject: [PATCH 40/41] Changed genericDist from being a polymorphic variant --- .../GenericDist/GenericOperation__Test.res | 10 +-- .../src/rescript/GenericDist/GenericDist.res | 78 +++++++++++-------- .../GenericDist_GenericOperation.res | 8 +- .../GenericDist_GenericOperation.resi | 6 +- .../GenericDist/GenericDist_Types.res | 9 +-- 5 files changed, 62 insertions(+), 49 deletions(-) diff --git a/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res b/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res index a5c1011f..4e3f207c 100644 --- a/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res +++ b/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res @@ -6,10 +6,10 @@ let env: GenericDist_GenericOperation.env = { xyPointLength: 100, } -let normalDist: GenericDist_Types.genericDist = #Symbolic(#Normal({mean: 5.0, stdev: 2.0})) -let normalDist10: GenericDist_Types.genericDist = #Symbolic(#Normal({mean: 10.0, stdev: 2.0})) -let normalDist20: GenericDist_Types.genericDist = #Symbolic(#Normal({mean: 20.0, stdev: 2.0})) -let uniformDist: GenericDist_Types.genericDist = #Symbolic(#Uniform({low: 9.0, high: 10.0})) +let normalDist: GenericDist_Types.genericDist = Symbolic(#Normal({mean: 5.0, stdev: 2.0})) +let normalDist10: GenericDist_Types.genericDist = Symbolic(#Normal({mean: 10.0, stdev: 2.0})) +let normalDist20: GenericDist_Types.genericDist = Symbolic(#Normal({mean: 20.0, stdev: 2.0})) +let uniformDist: GenericDist_Types.genericDist = Symbolic(#Uniform({low: 9.0, high: 10.0})) let {toFloat, toDist, toString, toError} = module(GenericDist_GenericOperation.Output) let {run} = module(GenericDist_GenericOperation) @@ -57,7 +57,7 @@ describe("toPointSet", () => { 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( + 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"))) diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res index ba293541..bb2f8d71 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res @@ -8,25 +8,25 @@ type pointwiseAddFn = (t, t) => result 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) + | PointSet(r) => Ok(PointSetDist.sampleNRendered(n, r)) + | Symbolic(r) => Ok(SymbolicDist.T.sampleN(n, r)) + | SampleSet(_) => Error(GenericDist_Types.NotYetImplemented) } -let fromFloat = (f: float) => #Symbolic(SymbolicDist.Float.make(f)) +let fromFloat = (f: float): t => Symbolic(SymbolicDist.Float.make(f)) let toString = (t: t) => switch t { - | #PointSet(_) => "Point Set Distribution" - | #Symbolic(r) => SymbolicDist.T.toString(r) - | #SampleSet(_) => "Sample Set Distribution" + | PointSet(_) => "Point Set Distribution" + | Symbolic(r) => SymbolicDist.T.toString(r) + | SampleSet(_) => "Sample Set Distribution" } -let normalize = (t: t) => +let normalize = (t: t): t => switch t { - | #PointSet(r) => #PointSet(PointSetDist.T.normalize(r)) - | #Symbolic(_) => t - | #SampleSet(_) => t + | PointSet(r) => PointSet(PointSetDist.T.normalize(r)) + | Symbolic(_) => t + | SampleSet(_) => t } let toFloatOperation = ( @@ -34,8 +34,8 @@ let toFloatOperation = ( ~toPointSetFn: toPointSetFn, ~distToFloatOperation: Operation.distToFloatOperation, ) => { - let symbolicSolution = switch t { - | #Symbolic(r) => + let symbolicSolution = switch (t: t) { + | Symbolic(r) => switch SymbolicDist.T.operate(distToFloatOperation, r) { | Ok(f) => Some(f) | _ => None @@ -53,10 +53,10 @@ let toFloatOperation = ( // This is tricky because the case of discrete distributions. // Also, change the outputXYPoints/pointSetDistLength details let toPointSet = (~xyPointLength, ~sampleCount, t): result => { - switch t { - | #PointSet(pointSet) => Ok(pointSet) - | #Symbolic(r) => Ok(SymbolicDist.T.toPointSetDist(xyPointLength, r)) - | #SampleSet(r) => { + switch (t: t) { + | PointSet(pointSet) => Ok(pointSet) + | Symbolic(r) => Ok(SymbolicDist.T.toPointSetDist(xyPointLength, r)) + | SampleSet(r) => { let response = SampleSet.toPointSetDist( ~samples=r, ~samplingInputs={ @@ -76,11 +76,11 @@ let toPointSet = (~xyPointLength, ~sampleCount, t): result => + let trySymbolicSimplification = (leftCutoff, rightCutoff, t: t): option => switch (leftCutoff, rightCutoff, t) { | (None, None, _) => None - | (lc, rc, #Symbolic(#Uniform(u))) if lc < rc => - Some(#Symbolic(#Uniform(SymbolicDist.Uniform.truncate(lc, rc, u)))) + | (lc, rc, Symbolic(#Uniform(u))) if lc < rc => + Some(Symbolic(#Uniform(SymbolicDist.Uniform.truncate(lc, rc, u)))) | _ => None } @@ -98,9 +98,9 @@ module Truncate = { switch trySymbolicSimplification(leftCutoff, rightCutoff, t) { | Some(r) => Ok(r) | None => - toPointSetFn(t)->E.R2.fmap(t => - #PointSet(PointSetDist.T.truncate(leftCutoff, rightCutoff, t)) - ) + toPointSetFn(t)->E.R2.fmap(t => { + GenericDist_Types.PointSet(PointSetDist.T.truncate(leftCutoff, rightCutoff, t)) + }) } } } @@ -122,7 +122,7 @@ module AlgebraicCombination = { t2: t, ): option> => switch (arithmeticOperation, t1, t2) { - | (arithmeticOperation, #Symbolic(d1), #Symbolic(d2)) => + | (arithmeticOperation, Symbolic(d1), Symbolic(d2)) => switch SymbolicDist.T.tryAnalyticalSimplification(d1, d2, arithmeticOperation) { | #AnalyticalSolution(symbolicDist) => Some(Ok(symbolicDist)) | #Error(er) => Some(Error(er)) @@ -156,11 +156,11 @@ module AlgebraicCombination = { //I'm (Ozzie) really just guessing here, very little idea what's best let expectedConvolutionCost: t => int = x => switch x { - | #Symbolic(#Float(_)) => 1 - | #Symbolic(_) => 1000 - | #PointSet(Discrete(m)) => m.xyShape->XYShape.T.length - | #PointSet(Mixed(_)) => 1000 - | #PointSet(Continuous(_)) => 1000 + | Symbolic(#Float(_)) => 1 + | Symbolic(_) => 1000 + | PointSet(Discrete(m)) => m.xyShape->XYShape.T.length + | PointSet(Mixed(_)) => 1000 + | PointSet(Continuous(_)) => 1000 | _ => 1000 } @@ -177,14 +177,24 @@ module AlgebraicCombination = { ~t2: t, ): result => { switch tryAnalyticalSimplification(arithmeticOperation, t1, t2) { - | Some(Ok(symbolicDist)) => Ok(#Symbolic(symbolicDist)) + | Some(Ok(symbolicDist)) => Ok(Symbolic(symbolicDist)) | Some(Error(e)) => Error(Other(e)) | None => switch chooseConvolutionOrMonteCarlo(t1, t2) { | #CalculateWithMonteCarlo => - runMonteCarlo(toSampleSetFn, arithmeticOperation, t1, t2)->E.R2.fmap(r => #SampleSet(r)) + runMonteCarlo( + toSampleSetFn, + arithmeticOperation, + t1, + t2, + )->E.R2.fmap(r => GenericDist_Types.SampleSet(r)) | #CalculateWithConvolution => - runConvolution(toPointSetFn, arithmeticOperation, t1, t2)->E.R2.fmap(r => #PointSet(r)) + runConvolution( + toPointSetFn, + arithmeticOperation, + t1, + t2, + )->E.R2.fmap(r => GenericDist_Types.PointSet(r)) } } } @@ -207,7 +217,7 @@ let pointwiseCombination = ( t2, ) ) - ->E.R2.fmap(r => #PointSet(r)) + ->E.R2.fmap(r => GenericDist_Types.PointSet(r)) } let pointwiseCombinationFloat = ( @@ -232,7 +242,7 @@ let pointwiseCombinationFloat = ( ) }) } - m->E.R2.fmap(r => #PointSet(r)) + m->E.R2.fmap(r => GenericDist_Types.PointSet(r)) } //Note: The result should always cumulatively sum to 1. This would be good to test. diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res index 51878a7d..55f6c621 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res @@ -71,14 +71,14 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => { let toPointSetFn = r => { switch reCall(~functionCallInfo=#fromDist(#toDist(#toPointSet), r), ()) { - | Dist(#PointSet(p)) => Ok(p) + | Dist(PointSet(p)) => Ok(p) | e => Error(OutputLocal.toErrorOrUnreachable(e)) } } let toSampleSetFn = r => { switch reCall(~functionCallInfo=#fromDist(#toDist(#toSampleSet(sampleCount)), r), ()) { - | Dist(#SampleSet(p)) => Ok(p) + | Dist(SampleSet(p)) => Ok(p) | e => Error(OutputLocal.toErrorOrUnreachable(e)) } } @@ -114,10 +114,10 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => { | #toDist(#toPointSet) => dist ->GenericDist.toPointSet(~xyPointLength, ~sampleCount) - ->E.R2.fmap(r => Dist(#PointSet(r))) + ->E.R2.fmap(r => Dist(PointSet(r))) ->OutputLocal.fromResult | #toDist(#toSampleSet(n)) => - dist->GenericDist.sampleN(n)->E.R2.fmap(r => Dist(#SampleSet(r)))->OutputLocal.fromResult + dist->GenericDist.sampleN(n)->E.R2.fmap(r => Dist(SampleSet(r)))->OutputLocal.fromResult | #toDistCombination(#Algebraic, _, #Float(_)) => GenDistError(NotYetImplemented) | #toDistCombination(#Algebraic, arithmeticOperation, #Dist(t2)) => dist diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi index c9e26058..abbd713e 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi @@ -15,7 +15,11 @@ let runFromDist: ( ~functionCallInfo: GenericDist_Types.Operation.fromDist, GenericDist_Types.genericDist, ) => outputType -let runFromFloat: (~env: env, ~functionCallInfo: GenericDist_Types.Operation.fromDist, float) => outputType +let runFromFloat: ( + ~env: env, + ~functionCallInfo: GenericDist_Types.Operation.fromDist, + float, +) => outputType module Output: { type t = outputType diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res index e8b9a0c0..bc79cfc1 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res @@ -1,8 +1,7 @@ -type genericDist = [ - | #PointSet(PointSetTypes.pointSetDist) - | #SampleSet(array) - | #Symbolic(SymbolicDistTypes.symbolicDist) -] +type genericDist = + | PointSet(PointSetTypes.pointSetDist) + | SampleSet(array) + | Symbolic(SymbolicDistTypes.symbolicDist) type error = | NotYetImplemented From 15534b10ce4374e3d0d10c860773c1adf8be476a Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Thu, 31 Mar 2022 14:51:42 -0400 Subject: [PATCH 41/41] Converted most of Operation to not be polymorphic --- .../GenericDist/GenericOperation__Test.res | 24 ++--- .../src/rescript/GenericDist/GenericDist.resi | 2 +- .../GenericDist_GenericOperation.res | 53 ++++++----- .../GenericDist/GenericDist_Types.res | 88 +++++++++---------- 4 files changed, 82 insertions(+), 85 deletions(-) diff --git a/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res b/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res index 4e3f207c..90d5a67c 100644 --- a/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res +++ b/packages/squiggle-lang/__tests__/GenericDist/GenericOperation__Test.res @@ -22,14 +22,14 @@ let toExt: option<'a> => 'a = E.O.toExt( describe("normalize", () => { test("has no impact on normal dist", () => { - let result = run(#fromDist(#toDist(#normalize), normalDist)) + let result = run(FromDist(ToDist(Normalize), normalDist)) expect(result)->toEqual(Dist(normalDist)) }) }) describe("mean", () => { test("for a normal distribution", () => { - let result = GenericDist_GenericOperation.run(~env, #fromDist(#toFloat(#Mean), normalDist)) + let result = GenericDist_GenericOperation.run(~env, FromDist(ToFloat(#Mean), normalDist)) expect(result)->toEqual(Float(5.0)) }) }) @@ -37,8 +37,8 @@ describe("mean", () => { describe("mixture", () => { test("on two normal distributions", () => { let result = - run(#mixture([(normalDist10, 0.5), (normalDist20, 0.5)])) - ->outputMap(#fromDist(#toFloat(#Mean))) + run(Mixture([(normalDist10, 0.5), (normalDist20, 0.5)])) + ->outputMap(FromDist(ToFloat(#Mean))) ->toFloat ->toExt expect(result)->toBeCloseTo(15.28) @@ -48,8 +48,8 @@ describe("mixture", () => { describe("toPointSet", () => { test("on symbolic normal distribution", () => { let result = - run(#fromDist(#toDist(#toPointSet), normalDist)) - ->outputMap(#fromDist(#toFloat(#Mean))) + run(FromDist(ToDist(ToPointSet), normalDist)) + ->outputMap(FromDist(ToFloat(#Mean))) ->toFloat ->toExt expect(result)->toBeCloseTo(5.09) @@ -57,18 +57,18 @@ describe("toPointSet", () => { 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)), + 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"))) }) Skip.test("on sample set", () => { let result = - run(#fromDist(#toDist(#toPointSet), normalDist)) - ->outputMap(#fromDist(#toDist(#toSampleSet(1000)))) - ->outputMap(#fromDist(#toDist(#toPointSet))) - ->outputMap(#fromDist(#toFloat(#Mean))) + run(FromDist(ToDist(ToPointSet), normalDist)) + ->outputMap(FromDist(ToDist(ToSampleSet(1000)))) + ->outputMap(FromDist(ToDist(ToPointSet))) + ->outputMap(FromDist(ToFloat(#Mean))) ->toFloat ->toExt expect(result)->toBeCloseTo(5.09) diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.resi b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.resi index f61a983f..f567f6be 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.resi +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.resi @@ -59,4 +59,4 @@ let mixture: ( array<(t, float)>, ~scaleMultiplyFn: scaleMultiplyFn, ~pointwiseAddFn: pointwiseAddFn, -) => result +) => result \ No newline at end of file diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res index 55f6c621..67db34e1 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res @@ -70,14 +70,14 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => { } let toPointSetFn = r => { - switch reCall(~functionCallInfo=#fromDist(#toDist(#toPointSet), r), ()) { + switch reCall(~functionCallInfo=FromDist(ToDist(ToPointSet), r), ()) { | Dist(PointSet(p)) => Ok(p) | e => Error(OutputLocal.toErrorOrUnreachable(e)) } } let toSampleSetFn = r => { - switch reCall(~functionCallInfo=#fromDist(#toDist(#toSampleSet(sampleCount)), r), ()) { + switch reCall(~functionCallInfo=FromDist(ToDist(ToSampleSet(sampleCount)), r), ()) { | Dist(SampleSet(p)) => Ok(p) | e => Error(OutputLocal.toErrorOrUnreachable(e)) } @@ -85,51 +85,51 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => { let scaleMultiply = (r, weight) => reCall( - ~functionCallInfo=#fromDist(#toDistCombination(#Pointwise, #Multiply, #Float(weight)), r), + ~functionCallInfo=FromDist(ToDistCombination(Pointwise, #Multiply, #Float(weight)), r), (), )->OutputLocal.toDistR let pointwiseAdd = (r1, r2) => reCall( - ~functionCallInfo=#fromDist(#toDistCombination(#Pointwise, #Add, #Dist(r2)), r1), + ~functionCallInfo=FromDist(ToDistCombination(Pointwise, #Add, #Dist(r2)), r1), (), )->OutputLocal.toDistR let fromDistFn = (subFnName: GenericDist_Types.Operation.fromDist, dist: genericDist) => switch subFnName { - | #toFloat(distToFloatOperation) => + | ToFloat(distToFloatOperation) => GenericDist.toFloatOperation(dist, ~toPointSetFn, ~distToFloatOperation) ->E.R2.fmap(r => Float(r)) ->OutputLocal.fromResult - | #toString => dist->GenericDist.toString->String - | #toDist(#inspect) => { + | ToString => dist->GenericDist.toString->String + | ToDist(Inspect) => { Js.log2("Console log requested: ", dist) Dist(dist) } - | #toDist(#normalize) => dist->GenericDist.normalize->Dist - | #toDist(#truncate(leftCutoff, rightCutoff)) => + | ToDist(Normalize) => dist->GenericDist.normalize->Dist + | ToDist(Truncate(leftCutoff, rightCutoff)) => GenericDist.truncate(~toPointSetFn, ~leftCutoff, ~rightCutoff, dist, ()) ->E.R2.fmap(r => Dist(r)) ->OutputLocal.fromResult - | #toDist(#toPointSet) => + | ToDist(ToSampleSet(n)) => + dist->GenericDist.sampleN(n)->E.R2.fmap(r => Dist(SampleSet(r)))->OutputLocal.fromResult + | ToDist(ToPointSet) => dist ->GenericDist.toPointSet(~xyPointLength, ~sampleCount) ->E.R2.fmap(r => Dist(PointSet(r))) ->OutputLocal.fromResult - | #toDist(#toSampleSet(n)) => - dist->GenericDist.sampleN(n)->E.R2.fmap(r => Dist(SampleSet(r)))->OutputLocal.fromResult - | #toDistCombination(#Algebraic, _, #Float(_)) => GenDistError(NotYetImplemented) - | #toDistCombination(#Algebraic, arithmeticOperation, #Dist(t2)) => + | ToDistCombination(Algebraic, _, #Float(_)) => GenDistError(NotYetImplemented) + | ToDistCombination(Algebraic, arithmeticOperation, #Dist(t2)) => dist ->GenericDist.algebraicCombination(~toPointSetFn, ~toSampleSetFn, ~arithmeticOperation, ~t2) ->E.R2.fmap(r => Dist(r)) ->OutputLocal.fromResult - | #toDistCombination(#Pointwise, arithmeticOperation, #Dist(t2)) => + | ToDistCombination(Pointwise, arithmeticOperation, #Dist(t2)) => dist ->GenericDist.pointwiseCombination(~toPointSetFn, ~arithmeticOperation, ~t2) ->E.R2.fmap(r => Dist(r)) ->OutputLocal.fromResult - | #toDistCombination(#Pointwise, arithmeticOperation, #Float(float)) => + | ToDistCombination(Pointwise, arithmeticOperation, #Float(float)) => dist ->GenericDist.pointwiseCombinationFloat(~toPointSetFn, ~arithmeticOperation, ~float) ->E.R2.fmap(r => Dist(r)) @@ -137,10 +137,10 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => { } switch functionCallInfo { - | #fromDist(subFnName, dist) => fromDistFn(subFnName, dist) - | #fromFloat(subFnName, float) => - reCall(~functionCallInfo=#fromDist(subFnName, GenericDist.fromFloat(float)), ()) - | #mixture(dists) => + | FromDist(subFnName, dist) => fromDistFn(subFnName, dist) + | FromFloat(subFnName, float) => + reCall(~functionCallInfo=FromDist(subFnName, GenericDist.fromFloat(float)), ()) + | Mixture(dists) => dists ->GenericDist.mixture(~scaleMultiplyFn=scaleMultiply, ~pointwiseAddFn=pointwiseAdd) ->E.R2.fmap(r => Dist(r)) @@ -148,9 +148,8 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => { } } -let runFromDist = (~env, ~functionCallInfo, dist) => run(~env, #fromDist(functionCallInfo, dist)) -let runFromFloat = (~env, ~functionCallInfo, float) => - run(~env, #fromFloat(functionCallInfo, float)) +let runFromDist = (~env, ~functionCallInfo, dist) => run(~env, FromDist(functionCallInfo, dist)) +let runFromFloat = (~env, ~functionCallInfo, float) => run(~env, FromFloat(functionCallInfo, float)) module Output = { include OutputLocal @@ -161,11 +160,11 @@ module Output = { functionCallInfo: GenericDist_Types.Operation.singleParamaterFunction, ): outputType => { let newFnCall: result = switch (functionCallInfo, input) { - | (#fromDist(fromDist), Dist(o)) => Ok(#fromDist(fromDist, o)) - | (#fromFloat(fromDist), Float(o)) => Ok(#fromFloat(fromDist, o)) + | (FromDist(fromDist), Dist(o)) => Ok(FromDist(fromDist, o)) + | (FromFloat(fromDist), Float(o)) => Ok(FromFloat(fromDist, o)) | (_, GenDistError(r)) => Error(r) - | (#fromDist(_), _) => Error(Other("Expected dist, got something else")) - | (#fromFloat(_), _) => Error(Other("Expected float, got something else")) + | (FromDist(_), _) => Error(Other("Expected dist, got something else")) + | (FromFloat(_), _) => Error(Other("Expected float, got something else")) } newFnCall->E.R2.fmap(run(~env))->OutputLocal.fromResult } diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res index bc79cfc1..98c0da25 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res @@ -10,10 +10,9 @@ type error = | Other(string) module Operation = { - type direction = [ - | #Algebraic - | #Pointwise - ] + type direction = + | Algebraic + | Pointwise type arithmeticOperation = [ | #Add @@ -42,51 +41,50 @@ module Operation = { | #Sample ] - type toDist = [ - | #normalize - | #toPointSet - | #toSampleSet(int) - | #truncate(option, option) - | #inspect - ] + type toDist = + | Normalize + | ToPointSet + | ToSampleSet(int) + | Truncate(option, option) + | Inspect - type toFloatArray = [ - | #Sample(int) - ] + type toFloatArray = Sample(int) - type fromDist = [ - | #toFloat(toFloat) - | #toDist(toDist) - | #toDistCombination(direction, arithmeticOperation, [#Dist(genericDist) | #Float(float)]) - | #toString - ] + type fromDist = + | ToFloat(toFloat) + | ToDist(toDist) + | ToDistCombination(direction, arithmeticOperation, [#Dist(genericDist) | #Float(float)]) + | ToString - type singleParamaterFunction = [ - | #fromDist(fromDist) - | #fromFloat(fromDist) - ] + type singleParamaterFunction = + | FromDist(fromDist) + | FromFloat(fromDist) - type genericFunctionCallInfo = [ - | #fromDist(fromDist, genericDist) - | #fromFloat(fromDist, float) - | #mixture(array<(genericDist, float)>) - ] + type genericFunctionCallInfo = + | FromDist(fromDist, genericDist) + | FromFloat(fromDist, float) + | Mixture(array<(genericDist, float)>) - //TODO: Should support all genericFunctionCallInfo types - let toString = (distFunction: fromDist): string => + let distCallToString = (distFunction: fromDist): string => switch distFunction { - | #toFloat(#Cdf(r)) => `cdf(${E.Float.toFixed(r)})` - | #toFloat(#Inv(r)) => `inv(${E.Float.toFixed(r)})` - | #toFloat(#Mean) => `mean` - | #toFloat(#Pdf(r)) => `pdf(${E.Float.toFixed(r)})` - | #toFloat(#Sample) => `sample` - | #toDist(#normalize) => `normalize` - | #toDist(#toPointSet) => `toPointSet` - | #toDist(#toSampleSet(r)) => `toSampleSet(${E.I.toString(r)})` - | #toDist(#truncate(_, _)) => `truncate` - | #toDist(#inspect) => `inspect` - | #toString => `toString` - | #toDistCombination(#Algebraic, _, _) => `algebraic` - | #toDistCombination(#Pointwise, _, _) => `pointwise` + | ToFloat(#Cdf(r)) => `cdf(${E.Float.toFixed(r)})` + | ToFloat(#Inv(r)) => `inv(${E.Float.toFixed(r)})` + | ToFloat(#Mean) => `mean` + | ToFloat(#Pdf(r)) => `pdf(${E.Float.toFixed(r)})` + | ToFloat(#Sample) => `sample` + | ToDist(Normalize) => `normalize` + | ToDist(ToPointSet) => `toPointSet` + | ToDist(ToSampleSet(r)) => `toSampleSet(${E.I.toString(r)})` + | ToDist(Truncate(_, _)) => `truncate` + | ToDist(Inspect) => `inspect` + | ToString => `toString` + | ToDistCombination(Algebraic, _, _) => `algebraic` + | ToDistCombination(Pointwise, _, _) => `pointwise` } -} \ No newline at end of file + + let toString = (d: genericFunctionCallInfo): string => + switch d { + | FromDist(f, _) | FromFloat(f, _) => distCallToString(f) + | Mixture(_) => `mixture` + } +}