From b3ccabae737a635fbded76762e84a18cfe0209a5 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Fri, 17 Jul 2020 11:29:18 +0100 Subject: [PATCH 1/4] Tiny refactor to use Shape methods directly --- src/distPlus/distribution/Shape.re | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/distPlus/distribution/Shape.re b/src/distPlus/distribution/Shape.re index 93dee24d..09dd7097 100644 --- a/src/distPlus/distribution/Shape.re +++ b/src/distPlus/distribution/Shape.re @@ -27,24 +27,19 @@ let combineAlgebraically = (op: ExpressionTypes.algebraicOperation, t1: t, t2: t): t => { switch (t1, t2) { | (Continuous(m1), Continuous(m2)) => - DistTypes.Continuous( - Continuous.combineAlgebraically(op, m1, m2), - ) + Continuous.combineAlgebraically(op, m1, m2) |> Continuous.T.toShape; | (Continuous(m1), Discrete(m2)) | (Discrete(m2), Continuous(m1)) => - DistTypes.Continuous( - Continuous.combineAlgebraicallyWithDiscrete(op, m1, m2), - ) + Continuous.combineAlgebraicallyWithDiscrete(op, m1, m2) |> Continuous.T.toShape | (Discrete(m1), Discrete(m2)) => - DistTypes.Discrete(Discrete.combineAlgebraically(op, m1, m2)) + Discrete.combineAlgebraically(op, m1, m2) |> Discrete.T.toShape | (m1, m2) => - DistTypes.Mixed( - Mixed.combineAlgebraically( - op, - toMixed(m1), - toMixed(m2), - ), + Mixed.combineAlgebraically( + op, + toMixed(m1), + toMixed(m2), ) + |> Mixed.T.toShape }; }; From 1dfea41ab89b9a757e7ff454354dd58e2bd6b05b Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Fri, 17 Jul 2020 11:29:51 +0100 Subject: [PATCH 2/4] Formatted AlgebraicShapeCombination and added |> ignore statements --- .../distribution/AlgebraicShapeCombination.re | 111 ++++++++---------- 1 file changed, 48 insertions(+), 63 deletions(-) diff --git a/src/distPlus/distribution/AlgebraicShapeCombination.re b/src/distPlus/distribution/AlgebraicShapeCombination.re index 1a2a5945..3b9201e3 100644 --- a/src/distPlus/distribution/AlgebraicShapeCombination.re +++ b/src/distPlus/distribution/AlgebraicShapeCombination.re @@ -80,7 +80,6 @@ let toDiscretePointMassesFromTriangulars = {n: n - 2, masses, means, variances}; } else { for (i in 1 to n - 2) { - // area of triangle = width * height / 2 let _ = Belt.Array.set( @@ -115,7 +114,11 @@ let toDiscretePointMassesFromTriangulars = }; let combineShapesContinuousContinuous = - (op: ExpressionTypes.algebraicOperation, s1: DistTypes.xyShape, s2: DistTypes.xyShape) + ( + op: ExpressionTypes.algebraicOperation, + s1: DistTypes.xyShape, + s2: DistTypes.xyShape, + ) : DistTypes.xyShape => { let t1n = s1 |> XYShape.T.length; let t2n = s2 |> XYShape.T.length; @@ -123,10 +126,11 @@ let combineShapesContinuousContinuous = // if we add the two distributions, we should probably use normal filters. // if we multiply the two distributions, we should probably use lognormal filters. let t1m = toDiscretePointMassesFromTriangulars(s1); - let t2m = switch (op) { + let t2m = + switch (op) { | `Divide => toDiscretePointMassesFromTriangulars(~inverse=true, s2) | _ => toDiscretePointMassesFromTriangulars(~inverse=false, s2) - }; + }; let combineMeansFn = switch (op) { @@ -190,27 +194,30 @@ let combineShapesContinuousContinuous = // we now want to create a set of target points. For now, let's just evenly distribute 200 points between // between the outputMinX and outputMaxX let nOut = 300; - let outputXs: array(float) = E.A.Floats.range(outputMinX^, outputMaxX^, nOut); + let outputXs: array(float) = + E.A.Floats.range(outputMinX^, outputMaxX^, nOut); let outputYs: array(float) = Belt.Array.make(nOut, 0.0); // now, for each of the outputYs, accumulate from a Gaussian kernel over each input point. - for (j in 0 to E.A.length(masses) - 1) { // go through all of the result points - let _ = if (variances[j] > 0. && masses[j] > 0.) { - for (i in 0 to E.A.length(outputXs) - 1) { // go through all of the target points + for (j in 0 to E.A.length(masses) - 1) { + // go through all of the result points + if (variances[j] > 0. && masses[j] > 0.) { + for (i in 0 to E.A.length(outputXs) - 1) { + // go through all of the target points let dx = outputXs[i] -. means[j]; - let contribution = masses[j] *. exp(-. (dx ** 2.) /. (2. *. variances[j])) /. (sqrt(2. *. 3.14159276 *. variances[j])); - let _ = Belt.Array.set(outputYs, i, outputYs[i] +. contribution); - (); + let contribution = + masses[j] + *. exp(-. (dx ** 2.) /. (2. *. variances[j])) + /. sqrt(2. *. 3.14159276 *. variances[j]); + Belt.Array.set(outputYs, i, outputYs[i] +. contribution) |> ignore; }; - (); }; - (); }; {xs: outputXs, ys: outputYs}; }; -let toDiscretePointMassesFromDiscrete = (s: DistTypes.xyShape): pointMassesWithMoments => { - let n = s |> XYShape.T.length; +let toDiscretePointMassesFromDiscrete = + (s: DistTypes.xyShape): pointMassesWithMoments => { let {xs, ys}: XYShape.T.t = s; let n = E.A.length(xs); @@ -219,36 +226,21 @@ let toDiscretePointMassesFromDiscrete = (s: DistTypes.xyShape): pointMassesWithM let variances: array(float) = Belt.Array.makeUninitializedUnsafe(n); for (i in 0 to n - 1) { - let _ = - Belt.Array.set( - masses, - i, - ys[i] - ); - - let _ = - Belt.Array.set( - means, - i, - xs[i] - ); - - let _ = - Belt.Array.set( - variances, - i, - 0.0 - ); - (); + Belt.Array.set(masses, i, ys[i]) |> ignore; + Belt.Array.set(means, i, xs[i]) |> ignore; + Belt.Array.set(variances, i, 0.0) |> ignore; }; {n, masses, means, variances}; }; let combineShapesContinuousDiscrete = - (op: ExpressionTypes.algebraicOperation, s1: DistTypes.xyShape, s2: DistTypes.xyShape) + ( + op: ExpressionTypes.algebraicOperation, + s1: DistTypes.xyShape, + s2: DistTypes.xyShape, + ) : array(DistTypes.xyShape) => { - let t1n = s1 |> XYShape.T.length; let t2n = s2 |> XYShape.T.length; @@ -260,7 +252,7 @@ let combineShapesContinuousDiscrete = switch (op) { | `Add - | `Subtract => { + | `Subtract => for (j in 0 to t2n - 1) { // for each one of the discrete points // create a new distribution, as long as the original continuous one @@ -268,40 +260,33 @@ let combineShapesContinuousDiscrete = Belt.Array.makeUninitializedUnsafe(t1n); for (i in 0 to t1n - 1) { - let _ = - Belt.Array.set( - dxyShape, - i, - (fn(s1.xs[i], s2.xs[j]), s1.ys[i] *. s2.ys[j]), - ); - (); + Belt.Array.set( + dxyShape, + i, + (fn(s1.xs[i], s2.xs[j]), s1.ys[i] *. s2.ys[j]), + ) + |> ignore; }; - let _ = Belt.Array.set(outXYShapes, j, dxyShape); - (); + Belt.Array.set(outXYShapes, j, dxyShape) |> ignore; } - } - | `Multiply - | `Divide => { + | `Multiply + | `Divide => for (j in 0 to t2n - 1) { // for each one of the discrete points // create a new distribution, as long as the original continuous one let dxyShape: array((float, float)) = Belt.Array.makeUninitializedUnsafe(t1n); for (i in 0 to t1n - 1) { - let _ = - Belt.Array.set( - dxyShape, - i, - (fn(s1.xs[i], s2.xs[j]), s1.ys[i] *. s2.ys[j] /. s2.xs[j]), - ); - (); + Belt.Array.set( + dxyShape, + i, + (fn(s1.xs[i], s2.xs[j]), s1.ys[i] *. s2.ys[j] /. s2.xs[j]), + ) + |> ignore; }; - let _ = Belt.Array.set(outXYShapes, j, dxyShape); - (); - } + Belt.Array.set(outXYShapes, j, dxyShape) |> ignore; } }; - outXYShapes - |> E.A.fmap(XYShape.T.fromZippedArray); + outXYShapes |> E.A.fmap(XYShape.T.fromZippedArray); }; From f09caaae5e11251b72c8585facfa27142ccc0ac0 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Fri, 17 Jul 2020 14:14:26 +0100 Subject: [PATCH 3/4] Change back to use combinationByRendering for this PR --- .../expressionTree/ExpressionTreeEvaluator.re | 48 +++++++++++------ .../expressionTree/ExpressionTypes.re | 53 ++++++++++++++++--- 2 files changed, 80 insertions(+), 21 deletions(-) diff --git a/src/distPlus/expressionTree/ExpressionTreeEvaluator.re b/src/distPlus/expressionTree/ExpressionTreeEvaluator.re index 76775759..1047fb8d 100644 --- a/src/distPlus/expressionTree/ExpressionTreeEvaluator.re +++ b/src/distPlus/expressionTree/ExpressionTreeEvaluator.re @@ -20,7 +20,7 @@ module AlgebraicCombination = { | _ => Ok(`AlgebraicCombination((operation, t1, t2))) }; - let tryCombination = (n, algebraicOp, t1: node, t2: node) => { + let combinationBySampling = (n, algebraicOp, t1: node, t2: node) => { let sampleN = mapRenderable(Shape.sampleNRendered(n), SymbolicDist.T.sampleN(n)); switch (sampleN(t1), sampleN(t2)) { @@ -33,24 +33,28 @@ module AlgebraicCombination = { }; }; - let renderIfNotRendered = (params, t) => - !renderable(t) - ? switch (render(params, t)) { - | Ok(r) => Ok(r) - | Error(e) => Error(e) - } - : Ok(t); + let combinationByRendering = + (evaluationParams, algebraicOp, t1: node, t2: node) + : result(node, string) => { + E.R.merge( + renderAndGetShape(evaluationParams, t1), + renderAndGetShape(evaluationParams, t2), + ) + |> E.R.fmap(((a, b)) => + `RenderedDist(Shape.combineAlgebraically(algebraicOp, a, b)) + ); + }; - let combineAsShapes = + let combineShapesUsingSampling = (evaluationParams: evaluationParams, algebraicOp, t1: node, t2: node) => { - let i1 = renderIfNotRendered(evaluationParams, t1); - let i2 = renderIfNotRendered(evaluationParams, t2); + let i1 = renderIfNotRenderable(evaluationParams, t1); + let i2 = renderIfNotRenderable(evaluationParams, t2); E.R.merge(i1, i2) |> E.R.bind( _, ((a, b)) => { let samples = - tryCombination( + combinationBySampling( evaluationParams.samplingInputs.sampleCount, algebraicOp, a, @@ -92,7 +96,7 @@ module AlgebraicCombination = { _, fun | `SymbolicDist(d) as t => Ok(t) - | _ => combineAsShapes(evaluationParams, algebraicOp, t1, t2), + | _ => combinationByRendering(evaluationParams, algebraicOp, t1, t2), ); }; @@ -131,7 +135,16 @@ module PointwiseCombination = { `RenderedDist( Shape.combinePointwise( ~integralSumCachesFn=(a, b) => Some(a +. b), - ~integralCachesFn=(a, b) => Some(Continuous.combinePointwise(~extrapolation=`UseOutermostPoints, (+.), a, b)), + ~integralCachesFn= + (a, b) => + Some( + Continuous.combinePointwise( + ~extrapolation=`UseOutermostPoints, + (+.), + a, + b, + ), + ), (+.), rs1, rs2, @@ -153,7 +166,12 @@ module PointwiseCombination = { }; let operationToLeaf = - (evaluationParams: evaluationParams, pointwiseOp: pointwiseOperation, t1: t, t2: t) => { + ( + evaluationParams: evaluationParams, + pointwiseOp: pointwiseOperation, + t1: t, + t2: t, + ) => { switch (pointwiseOp) { | `Add => pointwiseAdd(evaluationParams, t1, t2) | `Multiply => pointwiseMultiply(evaluationParams, t1, t2) diff --git a/src/distPlus/expressionTree/ExpressionTypes.re b/src/distPlus/expressionTree/ExpressionTypes.re index 06757d47..2cee4630 100644 --- a/src/distPlus/expressionTree/ExpressionTypes.re +++ b/src/distPlus/expressionTree/ExpressionTypes.re @@ -1,7 +1,13 @@ type algebraicOperation = [ | `Add | `Multiply | `Subtract | `Divide]; type pointwiseOperation = [ | `Add | `Multiply]; type scaleOperation = [ | `Multiply | `Exponentiate | `Log]; -type distToFloatOperation = [ | `Pdf(float) | `Cdf(float) | `Inv(float) | `Mean | `Sample]; +type distToFloatOperation = [ + | `Pdf(float) + | `Cdf(float) + | `Inv(float) + | `Mean + | `Sample +]; module ExpressionTree = { type node = [ @@ -34,17 +40,52 @@ module ExpressionTree = { let render = (evaluationParams: evaluationParams, r) => evaluateNode(evaluationParams, `Render(r)); - let evaluateAndRetry = (evaluationParams, fn, node) => - node - |> evaluationParams.evaluateNode(evaluationParams) - |> E.R.bind(_, fn(evaluationParams)); - let renderable = fun | `SymbolicDist(_) => true | `RenderedDist(_) => true | _ => false; + let renderIfNotRenderable = (params, t) => + !renderable(t) + ? switch (render(params, t)) { + | Ok(r) => Ok(r) + | Error(e) => Error(e) + } + : Ok(t); + + let renderIfNotRendered = (params, t) => + switch (t) { + | `RenderedDist(_) => Ok(t) + | _ => + switch (render(params, t)) { + | Ok(r) => Ok(r) + | Error(e) => Error(e) + } + }; + + let evaluateAndRetry = (evaluationParams, fn, node) => + node + |> evaluationParams.evaluateNode(evaluationParams) + |> E.R.bind(_, fn(evaluationParams)); + + let renderedShape = (item: node) => + switch (item) { + | `RenderedDist(r) => Some(r) + | _ => None + }; + + let renderAndGetShape = (params, t) => + switch (renderIfNotRendered(params, t)) { + | Ok(`RenderedDist(r)) => Ok(r) + | Error(r) => + Js.log(r); + Error(r); + | Ok(l) => + Js.log(l); + Error("Did not render as requested"); + }; + let mapRenderable = (renderedFn, symFn, item: node) => switch (item) { | `SymbolicDist(s) => Some(symFn(s)) From 6d71722d34a51d2f703618fb5fa0db7289bed01b Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Fri, 17 Jul 2020 23:00:17 +0100 Subject: [PATCH 4/4] Moved a bit of code to the SamplingDistribution file --- .../expressionTree/ExpressionTreeEvaluator.re | 62 ++------------ .../expressionTree/ExpressionTypes.re | 77 +++++++----------- .../expressionTree/SamplingDistribution.re | 80 +++++++++++++++++++ 3 files changed, 115 insertions(+), 104 deletions(-) create mode 100644 src/distPlus/expressionTree/SamplingDistribution.re diff --git a/src/distPlus/expressionTree/ExpressionTreeEvaluator.re b/src/distPlus/expressionTree/ExpressionTreeEvaluator.re index 1047fb8d..c1220506 100644 --- a/src/distPlus/expressionTree/ExpressionTreeEvaluator.re +++ b/src/distPlus/expressionTree/ExpressionTreeEvaluator.re @@ -20,68 +20,18 @@ module AlgebraicCombination = { | _ => Ok(`AlgebraicCombination((operation, t1, t2))) }; - let combinationBySampling = (n, algebraicOp, t1: node, t2: node) => { - let sampleN = - mapRenderable(Shape.sampleNRendered(n), SymbolicDist.T.sampleN(n)); - switch (sampleN(t1), sampleN(t2)) { - | (Some(a), Some(b)) => - Some( - Belt.Array.zip(a, b) - |> E.A.fmap(((a, b)) => Operation.Algebraic.toFn(algebraicOp, a, b)), - ) - | _ => None - }; - }; - let combinationByRendering = (evaluationParams, algebraicOp, t1: node, t2: node) : result(node, string) => { E.R.merge( - renderAndGetShape(evaluationParams, t1), - renderAndGetShape(evaluationParams, t2), + Render.ensureIsRenderedAndGetShape(evaluationParams, t1), + Render.ensureIsRenderedAndGetShape(evaluationParams, t2), ) |> E.R.fmap(((a, b)) => `RenderedDist(Shape.combineAlgebraically(algebraicOp, a, b)) ); }; - let combineShapesUsingSampling = - (evaluationParams: evaluationParams, algebraicOp, t1: node, t2: node) => { - let i1 = renderIfNotRenderable(evaluationParams, t1); - let i2 = renderIfNotRenderable(evaluationParams, t2); - E.R.merge(i1, i2) - |> E.R.bind( - _, - ((a, b)) => { - let samples = - combinationBySampling( - evaluationParams.samplingInputs.sampleCount, - algebraicOp, - a, - b, - ); - let shape = - samples - |> E.O.fmap( - Samples.T.fromSamples( - ~samplingInputs={ - sampleCount: - Some(evaluationParams.samplingInputs.sampleCount), - outputXYPoints: - Some(evaluationParams.samplingInputs.outputXYPoints), - kernelWidth: evaluationParams.samplingInputs.kernelWidth, - }, - ), - ) - |> E.O.bind(_, (r: RenderTypes.ShapeRenderer.Sampling.outputs) => - r.shape - ) - |> E.O.toResult("No response"); - shape |> E.R.fmap(r => `Normalize(`RenderedDist(r))); - }, - ); - }; - let operationToLeaf = ( evaluationParams: evaluationParams, @@ -107,7 +57,7 @@ module VerticalScaling = { let fn = Operation.Scale.toFn(scaleOp); let integralSumCacheFn = Operation.Scale.toIntegralSumCacheFn(scaleOp); let integralCacheFn = Operation.Scale.toIntegralCacheFn(scaleOp); - let renderedShape = render(evaluationParams, t); + let renderedShape = Render.render(evaluationParams, t); switch (renderedShape, scaleBy) { | (Ok(`RenderedDist(rs)), `SymbolicDist(`Float(sm))) => @@ -129,7 +79,7 @@ module VerticalScaling = { module PointwiseCombination = { let pointwiseAdd = (evaluationParams: evaluationParams, t1: t, t2: t) => { - switch (render(evaluationParams, t1), render(evaluationParams, t2)) { + switch (Render.render(evaluationParams, t1), Render.render(evaluationParams, t2)) { | (Ok(`RenderedDist(rs1)), Ok(`RenderedDist(rs2))) => Ok( `RenderedDist( @@ -184,7 +134,7 @@ module Truncate = { switch (leftCutoff, rightCutoff, t) { | (None, None, t) => `Solution(t) | (Some(lc), Some(rc), t) when lc > rc => - `Error("Left truncation bound must be smaller than right bound.") + `Error("Left truncation bound must be smaller than right truncation bound.") | (lc, rc, `SymbolicDist(`Uniform(u))) => `Solution( `SymbolicDist(`Uniform(SymbolicDist.Uniform.truncate(lc, rc, u))), @@ -197,7 +147,7 @@ module Truncate = { (evaluationParams: evaluationParams, leftCutoff, rightCutoff, t) => { // TODO: use named args for xMin/xMax in renderToShape; if we're lucky we can at least get the tail // of a distribution we otherwise wouldn't get at all - switch (render(evaluationParams, t)) { + switch (Render.ensureIsRendered(evaluationParams, t)) { | Ok(`RenderedDist(rs)) => Ok(`RenderedDist(Shape.T.truncate(leftCutoff, rightCutoff, rs))) | Error(e) => Error(e) diff --git a/src/distPlus/expressionTree/ExpressionTypes.re b/src/distPlus/expressionTree/ExpressionTypes.re index 2cee4630..a76aee74 100644 --- a/src/distPlus/expressionTree/ExpressionTypes.re +++ b/src/distPlus/expressionTree/ExpressionTypes.re @@ -37,61 +37,42 @@ module ExpressionTree = { let evaluateNode = (evaluationParams: evaluationParams) => evaluationParams.evaluateNode(evaluationParams); - let render = (evaluationParams: evaluationParams, r) => - evaluateNode(evaluationParams, `Render(r)); - - let renderable = - fun - | `SymbolicDist(_) => true - | `RenderedDist(_) => true - | _ => false; - - let renderIfNotRenderable = (params, t) => - !renderable(t) - ? switch (render(params, t)) { - | Ok(r) => Ok(r) - | Error(e) => Error(e) - } - : Ok(t); - - let renderIfNotRendered = (params, t) => - switch (t) { - | `RenderedDist(_) => Ok(t) - | _ => - switch (render(params, t)) { - | Ok(r) => Ok(r) - | Error(e) => Error(e) - } - }; - let evaluateAndRetry = (evaluationParams, fn, node) => node |> evaluationParams.evaluateNode(evaluationParams) |> E.R.bind(_, fn(evaluationParams)); - let renderedShape = (item: node) => - switch (item) { - | `RenderedDist(r) => Some(r) - | _ => None - }; + module Render = { + type t = node; - let renderAndGetShape = (params, t) => - switch (renderIfNotRendered(params, t)) { - | Ok(`RenderedDist(r)) => Ok(r) - | Error(r) => - Js.log(r); - Error(r); - | Ok(l) => - Js.log(l); - Error("Did not render as requested"); - }; + let render = (evaluationParams: evaluationParams, r) => + `Render(r) |> evaluateNode(evaluationParams); + + let ensureIsRendered = (params, t) => + switch (t) { + | `RenderedDist(_) => Ok(t) + | _ => + switch (render(params, t)) { + | Ok(`RenderedDist(r)) => Ok(`RenderedDist(r)) + | Ok(_) => Error("Did not render as requested") + | Error(e) => Error(e) + } + }; + + let ensureIsRenderedAndGetShape = (params, t) => + switch (ensureIsRendered(params, t)) { + | Ok(`RenderedDist(r)) => Ok(r) + | Ok(_) => Error("Did not render as requested") + | Error(e) => Error(e) + }; + + let getShape = (item: node) => + switch (item) { + | `RenderedDist(r) => Some(r) + | _ => None + }; + }; - let mapRenderable = (renderedFn, symFn, item: node) => - switch (item) { - | `SymbolicDist(s) => Some(symFn(s)) - | `RenderedDist(r) => Some(renderedFn(r)) - | _ => None - }; }; type simplificationResult = [ diff --git a/src/distPlus/expressionTree/SamplingDistribution.re b/src/distPlus/expressionTree/SamplingDistribution.re new file mode 100644 index 00000000..60886b40 --- /dev/null +++ b/src/distPlus/expressionTree/SamplingDistribution.re @@ -0,0 +1,80 @@ +open ExpressionTypes.ExpressionTree; + +let isSamplingDistribution: node => bool = + fun + | `SymbolicDist(_) => true + | `RenderedDist(_) => true + | _ => false; + +let renderIfIsNotSamplingDistribution = (params, t) => + !isSamplingDistribution(t) + ? switch (Render.render(params, t)) { + | Ok(r) => Ok(r) + | Error(e) => Error(e) + } + : Ok(t); + +let map = (~renderedDistFn, ~symbolicDistFn, node: node) => + node + |> ( + fun + | `RenderedDist(r) => Some(renderedDistFn(r)) + | `SymbolicDist(s) => Some(symbolicDistFn(s)) + | _ => None + ); + +let sampleN = n => + map( + ~renderedDistFn=Shape.sampleNRendered(n), + ~symbolicDistFn=SymbolicDist.T.sampleN(n), + ); + +let getCombinationSamples = (n, algebraicOp, t1: node, t2: node) => { + switch (sampleN(n, t1), sampleN(n, t2)) { + | (Some(a), Some(b)) => + Some( + Belt.Array.zip(a, b) + |> E.A.fmap(((a, b)) => Operation.Algebraic.toFn(algebraicOp, a, b)), + ) + | _ => None + }; +}; + +let combineShapesUsingSampling = + (evaluationParams: evaluationParams, algebraicOp, t1: node, t2: node) => { + let i1 = renderIfIsNotSamplingDistribution(evaluationParams, t1); + let i2 = renderIfIsNotSamplingDistribution(evaluationParams, t2); + E.R.merge(i1, i2) + |> E.R.bind( + _, + ((a, b)) => { + let samples = + getCombinationSamples( + evaluationParams.samplingInputs.sampleCount, + algebraicOp, + a, + b, + ); + + // todo: This bottom part should probably be somewhere else. + let shape = + samples + |> E.O.fmap( + Samples.T.fromSamples( + ~samplingInputs={ + sampleCount: + Some(evaluationParams.samplingInputs.sampleCount), + outputXYPoints: + Some(evaluationParams.samplingInputs.outputXYPoints), + kernelWidth: evaluationParams.samplingInputs.kernelWidth, + }, + ), + ) + |> E.O.bind(_, (r: RenderTypes.ShapeRenderer.Sampling.outputs) => + r.shape + ) + |> E.O.toResult("No response"); + shape |> E.R.fmap(r => `Normalize(`RenderedDist(r))); + }, + ); +};