From cd231cb9175e8aa5f71caf04adc891d5315c9e44 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Thu, 31 Mar 2022 16:44:52 -0400 Subject: [PATCH 01/10] added 'add' over EvDist, EvDist --- packages/squiggle-lang/bsconfig.json | 2 +- .../ReducerInterface_ExpressionValue.res | 3 +++ .../ReducerInterface_ExternalLibrary.res | 24 ++++++++++++++++++- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/packages/squiggle-lang/bsconfig.json b/packages/squiggle-lang/bsconfig.json index 64f5ffc1..4af37cb2 100644 --- a/packages/squiggle-lang/bsconfig.json +++ b/packages/squiggle-lang/bsconfig.json @@ -41,7 +41,7 @@ }, "refmt": 3, "warnings": { - "number": "+A-42-48-9-30-4-102" + "number": "+A-42-48-9-30-4-102-20-27-41" }, "ppx-flags": [] } diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExpressionValue.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExpressionValue.res index 8c94d752..2ad3a402 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExpressionValue.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExpressionValue.res @@ -12,6 +12,7 @@ type rec expressionValue = | EvSymbol(string) | EvArray(array) | EvRecord(Js.Dict.t) + | EvDist(GenericDist_Types.genericDist) type functionCall = (string, array) @@ -35,6 +36,7 @@ let rec toString = aValue => ->Js.String.concatMany("") `{${pairs}}` } + // | Dist() => } let toStringWithType = aValue => @@ -45,6 +47,7 @@ let toStringWithType = aValue => | EvSymbol(_) => `Symbol::${toString(aValue)}` | EvArray(_) => `Array::${toString(aValue)}` | EvRecord(_) => `Record::${toString(aValue)}` + // | Dist(_) => } let argsToString = (args: array): string => { diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalLibrary.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalLibrary.res index 4f726650..608c9792 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalLibrary.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalLibrary.res @@ -13,10 +13,32 @@ module Sample = { /* Map external calls of Reducer */ +let env: GenericDist_GenericOperation.env = { + sampleCount: 100, + xyPointLength: 100, +} + let dispatch = (call: ExpressionValue.functionCall, chain): result => switch call { | ("add", [EvNumber(a), EvNumber(b)]) => Sample.customAdd(a, b)->EvNumber->Ok - + | ("add", [EvDist(a), EvDist(b)]) => { + let x = GenericDist_GenericOperation.Output.toDistR( + GenericDist_GenericOperation.run(~env, FromDist(ToDistCombination(Algebraic, #Add, #Dist(b)), a)) + ) + switch x { + | Ok(thing) => Ok(EvDist(thing)) + | Error(err) => Error(Reducer_ErrorValue.RETodo("")) // TODO: + } + } + | ("add", [EvNumber(a), EvDist(b)]) => { + let x = GenericDist_GenericOperation.Output.toDistR( + GenericDist_GenericOperation.run(~env, FromDist(ToDistCombination(Algebraic, #Add, #Dist(b)), a)) + ) + switch x { + | Ok(thing) => Ok(EvDist(thing)) + | Error(err) => Error(Reducer_ErrorValue.RETodo("")) // TODO: + } + } | call => chain(call) /* From 5ece2994ba612a9969834e964c9a023a8b8d1690 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Thu, 31 Mar 2022 19:58:08 -0400 Subject: [PATCH 02/10] Full attempt at getting genericDist into Reducer External Lib --- .../GenericDist_GenericOperation.res | 12 ++ .../GenericDist_GenericOperation.resi | 2 + .../ReducerInterface_ExpressionValue.res | 4 +- .../ReducerInterface_ExternalLibrary.res | 147 ++++++++++++++---- 4 files changed, 135 insertions(+), 30 deletions(-) diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res index 67db34e1..351389ae 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.res @@ -48,12 +48,24 @@ module OutputLocal = { | _ => None } + let toFloatR = (t: t): result => + switch t { + | Float(r) => Ok(r) + | e => Error(toErrorOrUnreachable(e)) + } + let toString = (t: t) => switch t { | String(d) => Some(d) | _ => None } + let toStringR = (t: t): result => + switch t { + | String(r) => Ok(r) + | e => Error(toErrorOrUnreachable(e)) + } + //This is used to catch errors in other switch statements. let fromResult = (r: result): outputType => switch r { diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi index abbd713e..3c3e132a 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_GenericOperation.resi @@ -26,7 +26,9 @@ module Output: { let toDist: t => option let toDistR: t => result let toFloat: t => option + let toFloatR: t => result let toString: t => option + let toStringR: t => result let toError: t => option let fmap: (~env: env, t, GenericDist_Types.Operation.singleParamaterFunction) => t } diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExpressionValue.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExpressionValue.res index 2ad3a402..633e6a3c 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExpressionValue.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExpressionValue.res @@ -36,7 +36,7 @@ let rec toString = aValue => ->Js.String.concatMany("") `{${pairs}}` } - // | Dist() => + | EvDist(dist) => `${GenericDist.toString(dist)}` } let toStringWithType = aValue => @@ -47,7 +47,7 @@ let toStringWithType = aValue => | EvSymbol(_) => `Symbol::${toString(aValue)}` | EvArray(_) => `Array::${toString(aValue)}` | EvRecord(_) => `Record::${toString(aValue)}` - // | Dist(_) => + | EvDist(_) => `Distribution::${toString(aValue)}` } let argsToString = (args: array): string => { diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalLibrary.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalLibrary.res index 608c9792..d4586978 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalLibrary.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalLibrary.res @@ -10,38 +10,130 @@ module Sample = { let customAdd = (a: float, b: float): float => {a +. b} } +module Dist = { + let env: GenericDist_GenericOperation.env = { + sampleCount: 1000, + xyPointLength: 1000, + } + + let {toDistR, toFloatR} = module(GenericDist_GenericOperation.Output) + let runGenericOperation = GenericDist_GenericOperation.run(~env) + + let genericDistReturnToEvReturn = x => + switch x { + | Ok(thing) => Ok(ReducerInterface_ExpressionValue.EvDist(thing)) + | Error(err) => Error(Reducer_ErrorValue.RETodo("")) // TODO: + } + + let numberReturnToEvReturn = x => + switch x { + | Ok(n) => Ok(ReducerInterface_ExpressionValue.EvNumber(n)) + | Error(err) => Error(Reducer_ErrorValue.RETodo("")) // TODO: + } + + let arithmeticMap = r => + switch r { + | "add" => #Add + | "dotAdd" => #Add + | "subtract" => #Subtract + | "dotSubtract" => #Subtract + | "divide" => #Divide + | "logarithm" => #Divide + | "dotDivide" => #Divide + | "exponentiate" => #Exponentiate + | "dotExponentiate" => #Exponentiate + | "multiply" => #Multiply + | "dotMultiply" => #Multiply + | "dotLogarithm" => #Divide + | _ => #Multiply + } + + let catchAndConvertTwoArgsToDists = (args: array): option<( + GenericDist_Types.genericDist, + GenericDist_Types.genericDist, + )> => { + switch args { + | [EvDist(a), EvDist(b)] => Some((a, b)) + | [EvNumber(a), EvDist(b)] => Some((GenericDist.fromFloat(a), b)) + | [EvDist(a), EvNumber(b)] => Some((a, GenericDist.fromFloat(b))) + | _ => None + } + } + + let toFloatFn = (fnCall: GenericDist_Types.Operation.toFloat, dist) => { + FromDist(GenericDist_Types.Operation.ToFloat(fnCall), dist) + ->runGenericOperation + ->toFloatR + ->numberReturnToEvReturn + ->Some + } + + let toDistFn = (fnCall: GenericDist_Types.Operation.toDist, dist) => { + FromDist(GenericDist_Types.Operation.ToDist(fnCall), dist) + ->runGenericOperation + ->toDistR + ->genericDistReturnToEvReturn + ->Some + } + + let twoDiststoDistFn = (direction, arithmetic, dist1, dist2) => { + FromDist( + GenericDist_Types.Operation.ToDistCombination( + direction, + arithmeticMap(arithmetic), + #Dist(dist2), + ), + dist1, + ) + ->runGenericOperation + ->toDistR + ->genericDistReturnToEvReturn + } + + let dispatch = (call: ExpressionValue.functionCall): option> => { + let (fnName, args) = call + switch (fnName, args) { + | ("cdf", [EvDist(dist), EvNumber(float)]) => toFloatFn(#Cdf(float), dist) + | ("pdf", [EvDist(dist), EvNumber(float)]) => toFloatFn(#Pdf(float), dist) + | ("inv", [EvDist(dist), EvNumber(float)]) => toFloatFn(#Inv(float), dist) + | ("mean", [EvDist(dist)]) => toFloatFn(#Mean, dist) + | ("normalize", [EvDist(dist)]) => toDistFn(Normalize, dist) + | ("toPointSet", [EvDist(dist)]) => toDistFn(ToPointSet, dist) + | ("toSampleSet", [EvDist(dist), EvNumber(float)]) => + toDistFn(ToSampleSet(Belt.Int.fromFloat(float)), dist) + | ("truncateLeft", [EvDist(dist), EvNumber(float)]) => + toDistFn(Truncate(Some(float), None), dist) + | ("truncateRight", [EvDist(dist), EvNumber(float)]) => + toDistFn(Truncate(None, Some(float)), dist) + | ("truncate", [EvDist(dist), EvNumber(float1), EvNumber(float2)]) => + toDistFn(Truncate(Some(float1), Some(float2)), dist) + | ("sample", [EvDist(dist)]) => toFloatFn(#Sample, dist) + | ( + ("add" | "multiply" | "subtract" | "divide" | "exponentiate") as arithmetic, + [a, b] as args, + ) => + catchAndConvertTwoArgsToDists(args) |> E.O.fmap(((fst, snd)) => + twoDiststoDistFn(Algebraic, arithmetic, fst, snd) + ) + | ( + ("dotAdd" | "dotSubtract" | "dotDivide" | "dotExponentiate" | "dotMultiply") as arithmetic, + [a, b] as args, + ) => + catchAndConvertTwoArgsToDists(args) |> E.O.fmap(((fst, snd)) => + twoDiststoDistFn(Pointwise, arithmetic, fst, snd) + ) + | _ => None + } + } +} + /* Map external calls of Reducer */ -let env: GenericDist_GenericOperation.env = { - sampleCount: 100, - xyPointLength: 100, -} let dispatch = (call: ExpressionValue.functionCall, chain): result => - switch call { - | ("add", [EvNumber(a), EvNumber(b)]) => Sample.customAdd(a, b)->EvNumber->Ok - | ("add", [EvDist(a), EvDist(b)]) => { - let x = GenericDist_GenericOperation.Output.toDistR( - GenericDist_GenericOperation.run(~env, FromDist(ToDistCombination(Algebraic, #Add, #Dist(b)), a)) - ) - switch x { - | Ok(thing) => Ok(EvDist(thing)) - | Error(err) => Error(Reducer_ErrorValue.RETodo("")) // TODO: - } - } - | ("add", [EvNumber(a), EvDist(b)]) => { - let x = GenericDist_GenericOperation.Output.toDistR( - GenericDist_GenericOperation.run(~env, FromDist(ToDistCombination(Algebraic, #Add, #Dist(b)), a)) - ) - switch x { - | Ok(thing) => Ok(EvDist(thing)) - | Error(err) => Error(Reducer_ErrorValue.RETodo("")) // TODO: - } - } - | call => chain(call) - - /* + Dist.dispatch(call) |> E.O.default(chain(call)) +/* If your dispatch is too big you can divide it into smaller dispatches and pass the call so that it gets called finally. The final chain(call) invokes the builtin default functions of the interpreter. @@ -57,4 +149,3 @@ Remember from the users point of view, there are no different modules: // "doSth( constructorType2 )" doSth gets dispatched to the correct module because of the type signature. You get function and operator abstraction for free. You don't need to combine different implementations into one type. That would be duplicating the repsonsibility of the dispatcher. */ - } From 47a574ba8aaa3ebe10106161df41515c69f50d29 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Thu, 31 Mar 2022 20:24:59 -0400 Subject: [PATCH 03/10] Added GenericDistribution Reducer Interface --- .../ReducerInterface_ExternalLibrary.res | 119 +----------------- .../ReducerInterface_GenericDistribution.res | 113 +++++++++++++++++ .../ReducerInterface_GenericDistribution.resi | 3 + .../squiggle-lang/src/rescript/utility/E.res | 1 + 4 files changed, 118 insertions(+), 118 deletions(-) create mode 100644 packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res create mode 100644 packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.resi diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalLibrary.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalLibrary.res index d4586978..063fd472 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalLibrary.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalLibrary.res @@ -10,129 +10,12 @@ module Sample = { let customAdd = (a: float, b: float): float => {a +. b} } -module Dist = { - let env: GenericDist_GenericOperation.env = { - sampleCount: 1000, - xyPointLength: 1000, - } - - let {toDistR, toFloatR} = module(GenericDist_GenericOperation.Output) - let runGenericOperation = GenericDist_GenericOperation.run(~env) - - let genericDistReturnToEvReturn = x => - switch x { - | Ok(thing) => Ok(ReducerInterface_ExpressionValue.EvDist(thing)) - | Error(err) => Error(Reducer_ErrorValue.RETodo("")) // TODO: - } - - let numberReturnToEvReturn = x => - switch x { - | Ok(n) => Ok(ReducerInterface_ExpressionValue.EvNumber(n)) - | Error(err) => Error(Reducer_ErrorValue.RETodo("")) // TODO: - } - - let arithmeticMap = r => - switch r { - | "add" => #Add - | "dotAdd" => #Add - | "subtract" => #Subtract - | "dotSubtract" => #Subtract - | "divide" => #Divide - | "logarithm" => #Divide - | "dotDivide" => #Divide - | "exponentiate" => #Exponentiate - | "dotExponentiate" => #Exponentiate - | "multiply" => #Multiply - | "dotMultiply" => #Multiply - | "dotLogarithm" => #Divide - | _ => #Multiply - } - - let catchAndConvertTwoArgsToDists = (args: array): option<( - GenericDist_Types.genericDist, - GenericDist_Types.genericDist, - )> => { - switch args { - | [EvDist(a), EvDist(b)] => Some((a, b)) - | [EvNumber(a), EvDist(b)] => Some((GenericDist.fromFloat(a), b)) - | [EvDist(a), EvNumber(b)] => Some((a, GenericDist.fromFloat(b))) - | _ => None - } - } - - let toFloatFn = (fnCall: GenericDist_Types.Operation.toFloat, dist) => { - FromDist(GenericDist_Types.Operation.ToFloat(fnCall), dist) - ->runGenericOperation - ->toFloatR - ->numberReturnToEvReturn - ->Some - } - - let toDistFn = (fnCall: GenericDist_Types.Operation.toDist, dist) => { - FromDist(GenericDist_Types.Operation.ToDist(fnCall), dist) - ->runGenericOperation - ->toDistR - ->genericDistReturnToEvReturn - ->Some - } - - let twoDiststoDistFn = (direction, arithmetic, dist1, dist2) => { - FromDist( - GenericDist_Types.Operation.ToDistCombination( - direction, - arithmeticMap(arithmetic), - #Dist(dist2), - ), - dist1, - ) - ->runGenericOperation - ->toDistR - ->genericDistReturnToEvReturn - } - - let dispatch = (call: ExpressionValue.functionCall): option> => { - let (fnName, args) = call - switch (fnName, args) { - | ("cdf", [EvDist(dist), EvNumber(float)]) => toFloatFn(#Cdf(float), dist) - | ("pdf", [EvDist(dist), EvNumber(float)]) => toFloatFn(#Pdf(float), dist) - | ("inv", [EvDist(dist), EvNumber(float)]) => toFloatFn(#Inv(float), dist) - | ("mean", [EvDist(dist)]) => toFloatFn(#Mean, dist) - | ("normalize", [EvDist(dist)]) => toDistFn(Normalize, dist) - | ("toPointSet", [EvDist(dist)]) => toDistFn(ToPointSet, dist) - | ("toSampleSet", [EvDist(dist), EvNumber(float)]) => - toDistFn(ToSampleSet(Belt.Int.fromFloat(float)), dist) - | ("truncateLeft", [EvDist(dist), EvNumber(float)]) => - toDistFn(Truncate(Some(float), None), dist) - | ("truncateRight", [EvDist(dist), EvNumber(float)]) => - toDistFn(Truncate(None, Some(float)), dist) - | ("truncate", [EvDist(dist), EvNumber(float1), EvNumber(float2)]) => - toDistFn(Truncate(Some(float1), Some(float2)), dist) - | ("sample", [EvDist(dist)]) => toFloatFn(#Sample, dist) - | ( - ("add" | "multiply" | "subtract" | "divide" | "exponentiate") as arithmetic, - [a, b] as args, - ) => - catchAndConvertTwoArgsToDists(args) |> E.O.fmap(((fst, snd)) => - twoDiststoDistFn(Algebraic, arithmetic, fst, snd) - ) - | ( - ("dotAdd" | "dotSubtract" | "dotDivide" | "dotExponentiate" | "dotMultiply") as arithmetic, - [a, b] as args, - ) => - catchAndConvertTwoArgsToDists(args) |> E.O.fmap(((fst, snd)) => - twoDiststoDistFn(Pointwise, arithmetic, fst, snd) - ) - | _ => None - } - } -} - /* Map external calls of Reducer */ let dispatch = (call: ExpressionValue.functionCall, chain): result => - Dist.dispatch(call) |> E.O.default(chain(call)) + ReducerInterface_GenericDistribution.dispatch(call) |> E.O.default(chain(call)) /* If your dispatch is too big you can divide it into smaller dispatches and pass the call so that it gets called finally. diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res new file mode 100644 index 00000000..eb5704ae --- /dev/null +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res @@ -0,0 +1,113 @@ +module ExpressionValue = ReducerInterface_ExpressionValue +type expressionValue = ReducerInterface_ExpressionValue.expressionValue + +let env: GenericDist_GenericOperation.env = { + sampleCount: 1000, + xyPointLength: 1000, +} + +let runGenericOperation = GenericDist_GenericOperation.run(~env) + +let arithmeticMap = r => + switch r { + | "add" => #Add + | "dotAdd" => #Add + | "subtract" => #Subtract + | "dotSubtract" => #Subtract + | "divide" => #Divide + | "logarithm" => #Divide + | "dotDivide" => #Divide + | "exponentiate" => #Exponentiate + | "dotExponentiate" => #Exponentiate + | "multiply" => #Multiply + | "dotMultiply" => #Multiply + | "dotLogarithm" => #Divide + | _ => #Multiply + } + +let catchAndConvertTwoArgsToDists = (args: array): option<( + GenericDist_Types.genericDist, + GenericDist_Types.genericDist, +)> => { + switch args { + | [EvDist(a), EvDist(b)] => Some((a, b)) + | [EvNumber(a), EvDist(b)] => Some((GenericDist.fromFloat(a), b)) + | [EvDist(a), EvNumber(b)] => Some((a, GenericDist.fromFloat(b))) + | _ => None + } +} + +let toFloatFn = ( + fnCall: GenericDist_Types.Operation.toFloat, + dist: GenericDist_Types.genericDist, +) => { + FromDist(GenericDist_Types.Operation.ToFloat(fnCall), dist)->runGenericOperation->Some +} + +let toDistFn = (fnCall: GenericDist_Types.Operation.toDist, dist) => { + FromDist(GenericDist_Types.Operation.ToDist(fnCall), dist)->runGenericOperation->Some +} + +let twoDiststoDistFn = (direction, arithmetic, dist1, dist2) => { + FromDist( + GenericDist_Types.Operation.ToDistCombination( + direction, + arithmeticMap(arithmetic), + #Dist(dist2), + ), + dist1, + )->runGenericOperation +} + +let genericOutputToReducerValue = (o: GenericDist_GenericOperation.outputType): result< + expressionValue, + Reducer_ErrorValue.errorValue, +> => + switch o { + | Dist(d) => Ok(ReducerInterface_ExpressionValue.EvDist(d)) + | Float(d) => Ok(EvNumber(d)) + | String(d) => Ok(EvString(d)) + | GenDistError(NotYetImplemented) => Error(RETodo("Function not yet implemented")) + | GenDistError(Unreachable) => Error(RETodo("Unreachable")) + | GenDistError(DistributionVerticalShiftIsInvalid) => + Error(RETodo("Distribution Vertical Shift is Invalid")) + | GenDistError(Other(s)) => Error(RETodo(s)) + } + +let dispatchToGenericOutput = (call: ExpressionValue.functionCall): option< + GenericDist_GenericOperation.outputType, +> => { + let (fnName, args) = call + switch (fnName, args) { + | ("sample", [EvDist(dist)]) => toFloatFn(#Sample, dist) + | ("mean", [EvDist(dist)]) => toFloatFn(#Mean, dist) + | ("normalize", [EvDist(dist)]) => toDistFn(Normalize, dist) + | ("toPointSet", [EvDist(dist)]) => toDistFn(ToPointSet, dist) + | ("cdf", [EvDist(dist), EvNumber(float)]) => toFloatFn(#Cdf(float), dist) + | ("pdf", [EvDist(dist), EvNumber(float)]) => toFloatFn(#Pdf(float), dist) + | ("inv", [EvDist(dist), EvNumber(float)]) => toFloatFn(#Inv(float), dist) + | ("toSampleSet", [EvDist(dist), EvNumber(float)]) => + toDistFn(ToSampleSet(Belt.Int.fromFloat(float)), dist) + | ("truncateLeft", [EvDist(dist), EvNumber(float)]) => toDistFn(Truncate(Some(float), None), dist) + | ("truncateRight", [EvDist(dist), EvNumber(float)]) => + toDistFn(Truncate(None, Some(float)), dist) + | ("truncate", [EvDist(dist), EvNumber(float1), EvNumber(float2)]) => + toDistFn(Truncate(Some(float1), Some(float2)), dist) + | (("add" | "multiply" | "subtract" | "divide" | "exponentiate") as arithmetic, [a, b] as args) => + catchAndConvertTwoArgsToDists(args) -> E.O2.fmap(((fst, snd)) => + twoDiststoDistFn(Algebraic, arithmetic, fst, snd) + ) + | ( + ("dotAdd" | "dotMultiply" | "dotSubtract" | "dotDivide" | "dotExponentiate") as arithmetic, + [a, b] as args, + ) => + catchAndConvertTwoArgsToDists(args) -> E.O2.fmap(((fst, snd)) => + twoDiststoDistFn(Pointwise, arithmetic, fst, snd) + ) + | _ => None + } +} + +let dispatch = call => { + dispatchToGenericOutput(call) -> E.O2.fmap(genericOutputToReducerValue) +} diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.resi b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.resi new file mode 100644 index 00000000..fc7ebabc --- /dev/null +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.resi @@ -0,0 +1,3 @@ +let dispatch: ReducerInterface_ExpressionValue.functionCall => option< + result, +> diff --git a/packages/squiggle-lang/src/rescript/utility/E.res b/packages/squiggle-lang/src/rescript/utility/E.res index 636cc600..e36b6124 100644 --- a/packages/squiggle-lang/src/rescript/utility/E.res +++ b/packages/squiggle-lang/src/rescript/utility/E.res @@ -100,6 +100,7 @@ module O = { module O2 = { let default = (a, b) => O.default(b, a) let toExn = (a, b) => O.toExn(b, a) + let fmap = (a, b) => O.fmap(b, a) } /* Functions */ From 28eba9fa744de6e42ec631a500732256bee520fa Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Thu, 31 Mar 2022 20:28:48 -0400 Subject: [PATCH 04/10] EvDist -> EvDistribution --- .../ReducerInterface_ExpressionValue.res | 6 ++-- .../ReducerInterface_GenericDistribution.res | 30 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExpressionValue.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExpressionValue.res index 633e6a3c..bfb79df1 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExpressionValue.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExpressionValue.res @@ -12,7 +12,7 @@ type rec expressionValue = | EvSymbol(string) | EvArray(array) | EvRecord(Js.Dict.t) - | EvDist(GenericDist_Types.genericDist) + | EvDistribution(GenericDist_Types.genericDist) type functionCall = (string, array) @@ -36,7 +36,7 @@ let rec toString = aValue => ->Js.String.concatMany("") `{${pairs}}` } - | EvDist(dist) => `${GenericDist.toString(dist)}` + | EvDistribution(dist) => `${GenericDist.toString(dist)}` } let toStringWithType = aValue => @@ -47,7 +47,7 @@ let toStringWithType = aValue => | EvSymbol(_) => `Symbol::${toString(aValue)}` | EvArray(_) => `Array::${toString(aValue)}` | EvRecord(_) => `Record::${toString(aValue)}` - | EvDist(_) => `Distribution::${toString(aValue)}` + | EvDistribution(_) => `Distribution::${toString(aValue)}` } let argsToString = (args: array): string => { diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res index eb5704ae..18f152d4 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res @@ -30,9 +30,9 @@ let catchAndConvertTwoArgsToDists = (args: array): option<( GenericDist_Types.genericDist, )> => { switch args { - | [EvDist(a), EvDist(b)] => Some((a, b)) - | [EvNumber(a), EvDist(b)] => Some((GenericDist.fromFloat(a), b)) - | [EvDist(a), EvNumber(b)] => Some((a, GenericDist.fromFloat(b))) + | [EvDistribution(a), EvDistribution(b)] => Some((a, b)) + | [EvNumber(a), EvDistribution(b)] => Some((GenericDist.fromFloat(a), b)) + | [EvDistribution(a), EvNumber(b)] => Some((a, GenericDist.fromFloat(b))) | _ => None } } @@ -64,7 +64,7 @@ let genericOutputToReducerValue = (o: GenericDist_GenericOperation.outputType): Reducer_ErrorValue.errorValue, > => switch o { - | Dist(d) => Ok(ReducerInterface_ExpressionValue.EvDist(d)) + | Dist(d) => Ok(ReducerInterface_ExpressionValue.EvDistribution(d)) | Float(d) => Ok(EvNumber(d)) | String(d) => Ok(EvString(d)) | GenDistError(NotYetImplemented) => Error(RETodo("Function not yet implemented")) @@ -79,19 +79,19 @@ let dispatchToGenericOutput = (call: ExpressionValue.functionCall): option< > => { let (fnName, args) = call switch (fnName, args) { - | ("sample", [EvDist(dist)]) => toFloatFn(#Sample, dist) - | ("mean", [EvDist(dist)]) => toFloatFn(#Mean, dist) - | ("normalize", [EvDist(dist)]) => toDistFn(Normalize, dist) - | ("toPointSet", [EvDist(dist)]) => toDistFn(ToPointSet, dist) - | ("cdf", [EvDist(dist), EvNumber(float)]) => toFloatFn(#Cdf(float), dist) - | ("pdf", [EvDist(dist), EvNumber(float)]) => toFloatFn(#Pdf(float), dist) - | ("inv", [EvDist(dist), EvNumber(float)]) => toFloatFn(#Inv(float), dist) - | ("toSampleSet", [EvDist(dist), EvNumber(float)]) => + | ("sample", [EvDistribution(dist)]) => toFloatFn(#Sample, dist) + | ("mean", [EvDistribution(dist)]) => toFloatFn(#Mean, dist) + | ("normalize", [EvDistribution(dist)]) => toDistFn(Normalize, dist) + | ("toPointSet", [EvDistribution(dist)]) => toDistFn(ToPointSet, dist) + | ("cdf", [EvDistribution(dist), EvNumber(float)]) => toFloatFn(#Cdf(float), dist) + | ("pdf", [EvDistribution(dist), EvNumber(float)]) => toFloatFn(#Pdf(float), dist) + | ("inv", [EvDistribution(dist), EvNumber(float)]) => toFloatFn(#Inv(float), dist) + | ("toSampleSet", [EvDistribution(dist), EvNumber(float)]) => toDistFn(ToSampleSet(Belt.Int.fromFloat(float)), dist) - | ("truncateLeft", [EvDist(dist), EvNumber(float)]) => toDistFn(Truncate(Some(float), None), dist) - | ("truncateRight", [EvDist(dist), EvNumber(float)]) => + | ("truncateLeft", [EvDistribution(dist), EvNumber(float)]) => toDistFn(Truncate(Some(float), None), dist) + | ("truncateRight", [EvDistribution(dist), EvNumber(float)]) => toDistFn(Truncate(None, Some(float)), dist) - | ("truncate", [EvDist(dist), EvNumber(float1), EvNumber(float2)]) => + | ("truncate", [EvDistribution(dist), EvNumber(float1), EvNumber(float2)]) => toDistFn(Truncate(Some(float1), Some(float2)), dist) | (("add" | "multiply" | "subtract" | "divide" | "exponentiate") as arithmetic, [a, b] as args) => catchAndConvertTwoArgsToDists(args) -> E.O2.fmap(((fst, snd)) => From 18d742b63c1cccdf1d9cef54be14a166ba15d2f8 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Fri, 1 Apr 2022 13:21:24 -0400 Subject: [PATCH 05/10] Added symbolic functions and tests for reducer interface distribution code --- .../ReducerInterface_Distribution_test.res | 26 ++++++++++ .../ReducerInterface_GenericDistribution.res | 52 +++++++++++++++++-- 2 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res diff --git a/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res b/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res new file mode 100644 index 00000000..8fd6f9da --- /dev/null +++ b/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res @@ -0,0 +1,26 @@ +open Jest +open Reducer_TestHelpers + +let makeTest = (str, result) => test(str, () => expectEvalToBe(str, result)) + +describe("eval", () => { + Only.describe("expressions", () => { + makeTest("normal(5,2)", "Ok(Normal(5,2))") + makeTest("lognormal(5,2)", "Ok(Lognormal(5,2))") + makeTest("mean(normal(5,2))", "Ok(5)") + makeTest("mean(lognormal(1,2))", "Ok(20.085536923187668)") + makeTest("normalize(normal(5,2))", "Ok(Normal(5,2))") + makeTest("toPointSet(normal(5,2))", "Ok(Point Set Distribution)") + makeTest("toSampleSet(normal(5,2), 100)", "Ok(Sample Set Distribution)") + makeTest("add(normal(5,2), normal(10,2))", "Ok(Normal(15,2.8284271247461903))") + makeTest("add(normal(5,2), lognormal(10,2))", "Ok(Sample Set Distribution)") + makeTest("dotAdd(normal(5,2), lognormal(10,2))", "Ok(Point Set Distribution)") + makeTest("dotAdd(normal(5,2), 3)", "Ok(Point Set Distribution)") + makeTest("add(normal(5,2), 3)", "Ok(Point Set Distribution)") + makeTest("add(3, normal(5,2))", "Ok(Point Set Distribution)") + makeTest("3+normal(5,2)", "Ok(Point Set Distribution)") + makeTest("add(3, 3)", "Ok(6)") + makeTest("truncateLeft(normal(5,2), 3)", "Ok(Point Set Distribution)") + makeTest("mean(add(3, normal(5,2)))", "Ok(8.004619792609384)") + }) +}) diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res index 18f152d4..0bc7e23f 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res @@ -74,11 +74,54 @@ let genericOutputToReducerValue = (o: GenericDist_GenericOperation.outputType): | GenDistError(Other(s)) => Error(RETodo(s)) } +module SymbolicConstructor = { + let oneFloat = name => + switch name { + | "exponential" => Ok(SymbolicDist.Exponential.make) + | _ => Error("impossible path") + } + + let twoFloat = name => + switch name { + | "normal" => Ok(SymbolicDist.Normal.make) + | "uniform" => Ok(SymbolicDist.Uniform.make) + | "beta" => Ok(SymbolicDist.Beta.make) + | "lognormal" => Ok(SymbolicDist.Lognormal.make) + | _ => Error("impossible path") + } + + let threeFloat = name => + switch name { + | "triangular" => Ok(SymbolicDist.Triangular.make) + | _ => Error("impossible path") + } + + let symbolicResultToOutput = ( + symbolicResult: result, + ): option => + switch symbolicResult { + | Ok(r) => Some(Dist(Symbolic(r))) + | Error(r) => Some(GenDistError(Other(r))) + } +} + let dispatchToGenericOutput = (call: ExpressionValue.functionCall): option< GenericDist_GenericOperation.outputType, > => { let (fnName, args) = call switch (fnName, args) { + | ("exponential" as fnName, [EvNumber(f1)]) => + SymbolicConstructor.oneFloat(fnName) + ->E.R.bind(r => r(f1)) + ->SymbolicConstructor.symbolicResultToOutput + | (("normal" | "uniform" | "beta" | "lognormal") as fnName, [EvNumber(f1), EvNumber(f2)]) => + SymbolicConstructor.twoFloat(fnName) + ->E.R.bind(r => r(f1, f2)) + ->SymbolicConstructor.symbolicResultToOutput + | ("triangular" as fnName, [EvNumber(f1), EvNumber(f2), EvNumber(f3)]) => + SymbolicConstructor.threeFloat(fnName) + ->E.R.bind(r => r(f1, f2, f3)) + ->SymbolicConstructor.symbolicResultToOutput | ("sample", [EvDistribution(dist)]) => toFloatFn(#Sample, dist) | ("mean", [EvDistribution(dist)]) => toFloatFn(#Mean, dist) | ("normalize", [EvDistribution(dist)]) => toDistFn(Normalize, dist) @@ -88,20 +131,21 @@ let dispatchToGenericOutput = (call: ExpressionValue.functionCall): option< | ("inv", [EvDistribution(dist), EvNumber(float)]) => toFloatFn(#Inv(float), dist) | ("toSampleSet", [EvDistribution(dist), EvNumber(float)]) => toDistFn(ToSampleSet(Belt.Int.fromFloat(float)), dist) - | ("truncateLeft", [EvDistribution(dist), EvNumber(float)]) => toDistFn(Truncate(Some(float), None), dist) + | ("truncateLeft", [EvDistribution(dist), EvNumber(float)]) => + toDistFn(Truncate(Some(float), None), dist) | ("truncateRight", [EvDistribution(dist), EvNumber(float)]) => toDistFn(Truncate(None, Some(float)), dist) | ("truncate", [EvDistribution(dist), EvNumber(float1), EvNumber(float2)]) => toDistFn(Truncate(Some(float1), Some(float2)), dist) | (("add" | "multiply" | "subtract" | "divide" | "exponentiate") as arithmetic, [a, b] as args) => - catchAndConvertTwoArgsToDists(args) -> E.O2.fmap(((fst, snd)) => + catchAndConvertTwoArgsToDists(args)->E.O2.fmap(((fst, snd)) => twoDiststoDistFn(Algebraic, arithmetic, fst, snd) ) | ( ("dotAdd" | "dotMultiply" | "dotSubtract" | "dotDivide" | "dotExponentiate") as arithmetic, [a, b] as args, ) => - catchAndConvertTwoArgsToDists(args) -> E.O2.fmap(((fst, snd)) => + catchAndConvertTwoArgsToDists(args)->E.O2.fmap(((fst, snd)) => twoDiststoDistFn(Pointwise, arithmetic, fst, snd) ) | _ => None @@ -109,5 +153,5 @@ let dispatchToGenericOutput = (call: ExpressionValue.functionCall): option< } let dispatch = call => { - dispatchToGenericOutput(call) -> E.O2.fmap(genericOutputToReducerValue) + dispatchToGenericOutput(call)->E.O2.fmap(genericOutputToReducerValue) } From e7c2a7db01a0d4105e0838222785066e0e748b74 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Fri, 1 Apr 2022 15:41:11 -0400 Subject: [PATCH 06/10] Light cleanup of ReducerInterface_GenericDistribution --- .../ReducerInterface_Distribution_test.res | 4 +- .../src/rescript/GenericDist/GenericDist.res | 2 +- .../GenericDist/GenericDist_Types.res | 4 +- .../ReducerInterface_GenericDistribution.res | 188 +++++++++--------- .../typeSystem/HardcodedFunctions.res | 2 +- .../AlgebraicShapeCombination.res | 4 +- .../rescript/symbolicDist/SymbolicDist.res | 40 ++-- .../src/rescript/utility/Operation.res | 16 +- 8 files changed, 138 insertions(+), 122 deletions(-) diff --git a/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res b/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res index 8fd6f9da..ada50f3f 100644 --- a/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res +++ b/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res @@ -6,6 +6,9 @@ let makeTest = (str, result) => test(str, () => expectEvalToBe(str, result)) describe("eval", () => { Only.describe("expressions", () => { makeTest("normal(5,2)", "Ok(Normal(5,2))") + makeTest("5 to 2", "Error(TODO: Low value must be less than high value.)") + makeTest("to(2,5)", "Ok(Lognormal(1.1512925464970227,0.278507821238345))") + makeTest("to(-2,2)", "Ok(Normal(0,1.215913388057542))") makeTest("lognormal(5,2)", "Ok(Lognormal(5,2))") makeTest("mean(normal(5,2))", "Ok(5)") makeTest("mean(lognormal(1,2))", "Ok(20.085536923187668)") @@ -21,6 +24,5 @@ describe("eval", () => { makeTest("3+normal(5,2)", "Ok(Point Set Distribution)") makeTest("add(3, 3)", "Ok(6)") makeTest("truncateLeft(normal(5,2), 3)", "Ok(Point Set Distribution)") - makeTest("mean(add(3, normal(5,2)))", "Ok(8.004619792609384)") }) }) diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res index bb2f8d71..f71d9b93 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist.res @@ -228,7 +228,7 @@ let pointwiseCombinationFloat = ( ): result => { let m = switch arithmeticOperation { | #Add | #Subtract => Error(GenericDist_Types.DistributionVerticalShiftIsInvalid) - | (#Multiply | #Divide | #Exponentiate | #Log) as arithmeticOperation => + | (#Multiply | #Divide | #Exponentiate | #Logarithm) as arithmeticOperation => toPointSetFn(t)->E.R2.fmap(t => { //TODO: Move to PointSet codebase let fn = (secondary, main) => Operation.Scale.toFn(arithmeticOperation, main, secondary) diff --git a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res index 98c0da25..43ce5d74 100644 --- a/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res +++ b/packages/squiggle-lang/src/rescript/GenericDist/GenericDist_Types.res @@ -20,7 +20,7 @@ module Operation = { | #Subtract | #Divide | #Exponentiate - | #Log + | #Logarithm ] let arithmeticToFn = (arithmetic: arithmeticOperation) => @@ -30,7 +30,7 @@ module Operation = { | #Subtract => \"-." | #Exponentiate => \"**" | #Divide => \"/." - | #Log => (a, b) => log(a) /. log(b) + | #Logarithm => (a, b) => log(a) /. log(b) } type toFloat = [ diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res index 0bc7e23f..9970c340 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res @@ -1,80 +1,67 @@ module ExpressionValue = ReducerInterface_ExpressionValue type expressionValue = ReducerInterface_ExpressionValue.expressionValue -let env: GenericDist_GenericOperation.env = { - sampleCount: 1000, - xyPointLength: 1000, -} +let runGenericOperation = GenericDist_GenericOperation.run( + ~env={ + sampleCount: 1000, + xyPointLength: 1000, + }, +) -let runGenericOperation = GenericDist_GenericOperation.run(~env) +module Helpers = { + let arithmeticMap = r => + switch r { + | "add" => #Add + | "dotAdd" => #Add + | "subtract" => #Subtract + | "dotSubtract" => #Subtract + | "divide" => #Divide + | "logarithm" => #Logarithm + | "dotDivide" => #Divide + | "exponentiate" => #Exponentiate + | "dotExponentiate" => #Exponentiate + | "multiply" => #Multiply + | "dotMultiply" => #Multiply + | "dotLogarithm" => #Logarithm + | _ => #Multiply + } -let arithmeticMap = r => - switch r { - | "add" => #Add - | "dotAdd" => #Add - | "subtract" => #Subtract - | "dotSubtract" => #Subtract - | "divide" => #Divide - | "logarithm" => #Divide - | "dotDivide" => #Divide - | "exponentiate" => #Exponentiate - | "dotExponentiate" => #Exponentiate - | "multiply" => #Multiply - | "dotMultiply" => #Multiply - | "dotLogarithm" => #Divide - | _ => #Multiply + let catchAndConvertTwoArgsToDists = (args: array): option<( + GenericDist_Types.genericDist, + GenericDist_Types.genericDist, + )> => { + switch args { + | [EvDistribution(a), EvDistribution(b)] => Some((a, b)) + | [EvNumber(a), EvDistribution(b)] => Some((GenericDist.fromFloat(a), b)) + | [EvDistribution(a), EvNumber(b)] => Some((a, GenericDist.fromFloat(b))) + | _ => None + } } -let catchAndConvertTwoArgsToDists = (args: array): option<( - GenericDist_Types.genericDist, - GenericDist_Types.genericDist, -)> => { - switch args { - | [EvDistribution(a), EvDistribution(b)] => Some((a, b)) - | [EvNumber(a), EvDistribution(b)] => Some((GenericDist.fromFloat(a), b)) - | [EvDistribution(a), EvNumber(b)] => Some((a, GenericDist.fromFloat(b))) - | _ => None + let toFloatFn = ( + fnCall: GenericDist_Types.Operation.toFloat, + dist: GenericDist_Types.genericDist, + ) => { + FromDist(GenericDist_Types.Operation.ToFloat(fnCall), dist)->runGenericOperation->Some + } + + let toDistFn = (fnCall: GenericDist_Types.Operation.toDist, dist) => { + FromDist(GenericDist_Types.Operation.ToDist(fnCall), dist)->runGenericOperation->Some + } + + let twoDiststoDistFn = (direction, arithmetic, dist1, dist2) => { + FromDist( + GenericDist_Types.Operation.ToDistCombination( + direction, + arithmeticMap(arithmetic), + #Dist(dist2), + ), + dist1, + )->runGenericOperation } } -let toFloatFn = ( - fnCall: GenericDist_Types.Operation.toFloat, - dist: GenericDist_Types.genericDist, -) => { - FromDist(GenericDist_Types.Operation.ToFloat(fnCall), dist)->runGenericOperation->Some -} - -let toDistFn = (fnCall: GenericDist_Types.Operation.toDist, dist) => { - FromDist(GenericDist_Types.Operation.ToDist(fnCall), dist)->runGenericOperation->Some -} - -let twoDiststoDistFn = (direction, arithmetic, dist1, dist2) => { - FromDist( - GenericDist_Types.Operation.ToDistCombination( - direction, - arithmeticMap(arithmetic), - #Dist(dist2), - ), - dist1, - )->runGenericOperation -} - -let genericOutputToReducerValue = (o: GenericDist_GenericOperation.outputType): result< - expressionValue, - Reducer_ErrorValue.errorValue, -> => - switch o { - | Dist(d) => Ok(ReducerInterface_ExpressionValue.EvDistribution(d)) - | Float(d) => Ok(EvNumber(d)) - | String(d) => Ok(EvString(d)) - | GenDistError(NotYetImplemented) => Error(RETodo("Function not yet implemented")) - | GenDistError(Unreachable) => Error(RETodo("Unreachable")) - | GenDistError(DistributionVerticalShiftIsInvalid) => - Error(RETodo("Distribution Vertical Shift is Invalid")) - | GenDistError(Other(s)) => Error(RETodo(s)) - } - -module SymbolicConstructor = { +module SymbolicConstructors = { let oneFloat = name => switch name { | "exponential" => Ok(SymbolicDist.Exponential.make) @@ -87,6 +74,7 @@ module SymbolicConstructor = { | "uniform" => Ok(SymbolicDist.Uniform.make) | "beta" => Ok(SymbolicDist.Beta.make) | "lognormal" => Ok(SymbolicDist.Lognormal.make) + | "to" => Ok(SymbolicDist.From90thPercentile.make) | _ => Error("impossible path") } @@ -111,47 +99,65 @@ let dispatchToGenericOutput = (call: ExpressionValue.functionCall): option< let (fnName, args) = call switch (fnName, args) { | ("exponential" as fnName, [EvNumber(f1)]) => - SymbolicConstructor.oneFloat(fnName) + SymbolicConstructors.oneFloat(fnName) ->E.R.bind(r => r(f1)) - ->SymbolicConstructor.symbolicResultToOutput - | (("normal" | "uniform" | "beta" | "lognormal") as fnName, [EvNumber(f1), EvNumber(f2)]) => - SymbolicConstructor.twoFloat(fnName) + ->SymbolicConstructors.symbolicResultToOutput + | ( + ("normal" | "uniform" | "beta" | "lognormal" | "to") as fnName, + [EvNumber(f1), EvNumber(f2)], + ) => + SymbolicConstructors.twoFloat(fnName) ->E.R.bind(r => r(f1, f2)) - ->SymbolicConstructor.symbolicResultToOutput + ->SymbolicConstructors.symbolicResultToOutput | ("triangular" as fnName, [EvNumber(f1), EvNumber(f2), EvNumber(f3)]) => - SymbolicConstructor.threeFloat(fnName) + SymbolicConstructors.threeFloat(fnName) ->E.R.bind(r => r(f1, f2, f3)) - ->SymbolicConstructor.symbolicResultToOutput - | ("sample", [EvDistribution(dist)]) => toFloatFn(#Sample, dist) - | ("mean", [EvDistribution(dist)]) => toFloatFn(#Mean, dist) - | ("normalize", [EvDistribution(dist)]) => toDistFn(Normalize, dist) - | ("toPointSet", [EvDistribution(dist)]) => toDistFn(ToPointSet, dist) - | ("cdf", [EvDistribution(dist), EvNumber(float)]) => toFloatFn(#Cdf(float), dist) - | ("pdf", [EvDistribution(dist), EvNumber(float)]) => toFloatFn(#Pdf(float), dist) - | ("inv", [EvDistribution(dist), EvNumber(float)]) => toFloatFn(#Inv(float), dist) + ->SymbolicConstructors.symbolicResultToOutput + | ("sample", [EvDistribution(dist)]) => Helpers.toFloatFn(#Sample, dist) + | ("mean", [EvDistribution(dist)]) => Helpers.toFloatFn(#Mean, dist) + | ("normalize", [EvDistribution(dist)]) => Helpers.toDistFn(Normalize, dist) + | ("toPointSet", [EvDistribution(dist)]) => Helpers.toDistFn(ToPointSet, dist) + | ("cdf", [EvDistribution(dist), EvNumber(float)]) => Helpers.toFloatFn(#Cdf(float), dist) + | ("pdf", [EvDistribution(dist), EvNumber(float)]) => Helpers.toFloatFn(#Pdf(float), dist) + | ("inv", [EvDistribution(dist), EvNumber(float)]) => Helpers.toFloatFn(#Inv(float), dist) | ("toSampleSet", [EvDistribution(dist), EvNumber(float)]) => - toDistFn(ToSampleSet(Belt.Int.fromFloat(float)), dist) + Helpers.toDistFn(ToSampleSet(Belt.Int.fromFloat(float)), dist) | ("truncateLeft", [EvDistribution(dist), EvNumber(float)]) => - toDistFn(Truncate(Some(float), None), dist) + Helpers.toDistFn(Truncate(Some(float), None), dist) | ("truncateRight", [EvDistribution(dist), EvNumber(float)]) => - toDistFn(Truncate(None, Some(float)), dist) + Helpers.toDistFn(Truncate(None, Some(float)), dist) | ("truncate", [EvDistribution(dist), EvNumber(float1), EvNumber(float2)]) => - toDistFn(Truncate(Some(float1), Some(float2)), dist) - | (("add" | "multiply" | "subtract" | "divide" | "exponentiate") as arithmetic, [a, b] as args) => - catchAndConvertTwoArgsToDists(args)->E.O2.fmap(((fst, snd)) => - twoDiststoDistFn(Algebraic, arithmetic, fst, snd) + Helpers.toDistFn(Truncate(Some(float1), Some(float2)), dist) + | (("add" | "multiply" | "subtract" | "divide" | "exponentiate" | "log") as arithmetic, [a, b] as args) => + Helpers.catchAndConvertTwoArgsToDists(args)->E.O2.fmap(((fst, snd)) => + Helpers.twoDiststoDistFn(Algebraic, arithmetic, fst, snd) ) | ( - ("dotAdd" | "dotMultiply" | "dotSubtract" | "dotDivide" | "dotExponentiate") as arithmetic, + ("dotAdd" | "dotMultiply" | "dotSubtract" | "dotDivide" | "dotExponentiate" | "dotLogarithm") as arithmetic, [a, b] as args, ) => - catchAndConvertTwoArgsToDists(args)->E.O2.fmap(((fst, snd)) => - twoDiststoDistFn(Pointwise, arithmetic, fst, snd) + Helpers.catchAndConvertTwoArgsToDists(args)->E.O2.fmap(((fst, snd)) => + Helpers.twoDiststoDistFn(Pointwise, arithmetic, fst, snd) ) | _ => None } } +let genericOutputToReducerValue = (o: GenericDist_GenericOperation.outputType): result< + expressionValue, + Reducer_ErrorValue.errorValue, +> => + switch o { + | Dist(d) => Ok(ReducerInterface_ExpressionValue.EvDistribution(d)) + | Float(d) => Ok(EvNumber(d)) + | String(d) => Ok(EvString(d)) + | GenDistError(NotYetImplemented) => Error(RETodo("Function not yet implemented")) + | GenDistError(Unreachable) => Error(RETodo("Unreachable")) + | GenDistError(DistributionVerticalShiftIsInvalid) => + Error(RETodo("Distribution Vertical Shift is Invalid")) + | GenDistError(Other(s)) => Error(RETodo(s)) + } + let dispatch = call => { dispatchToGenericOutput(call)->E.O2.fmap(genericOutputToReducerValue) } diff --git a/packages/squiggle-lang/src/rescript/interpreter/typeSystem/HardcodedFunctions.res b/packages/squiggle-lang/src/rescript/interpreter/typeSystem/HardcodedFunctions.res index f6741bab..cf8fe470 100644 --- a/packages/squiggle-lang/src/rescript/interpreter/typeSystem/HardcodedFunctions.res +++ b/packages/squiggle-lang/src/rescript/interpreter/typeSystem/HardcodedFunctions.res @@ -229,6 +229,6 @@ let all = [ ), makeRenderedDistFloat("scaleExp", (dist, float) => verticalScaling(#Exponentiate, dist, float)), makeRenderedDistFloat("scaleMultiply", (dist, float) => verticalScaling(#Multiply, dist, float)), - makeRenderedDistFloat("scaleLog", (dist, float) => verticalScaling(#Log, dist, float)), + makeRenderedDistFloat("scaleLog", (dist, float) => verticalScaling(#Logarithm, dist, float)), Multimodal._function, ] diff --git a/packages/squiggle-lang/src/rescript/pointSetDist/AlgebraicShapeCombination.res b/packages/squiggle-lang/src/rescript/pointSetDist/AlgebraicShapeCombination.res index 3c298b18..9cfdc66d 100644 --- a/packages/squiggle-lang/src/rescript/pointSetDist/AlgebraicShapeCombination.res +++ b/packages/squiggle-lang/src/rescript/pointSetDist/AlgebraicShapeCombination.res @@ -115,7 +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) + | #Logarithm => (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 @@ -233,7 +233,7 @@ let combineShapesContinuousDiscrete = ( } | #Multiply | #Exponentiate - | #Log + | #Logarithm | #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/symbolicDist/SymbolicDist.res b/packages/squiggle-lang/src/rescript/symbolicDist/SymbolicDist.res index ffeec12f..cd4132b3 100644 --- a/packages/squiggle-lang/src/rescript/symbolicDist/SymbolicDist.res +++ b/packages/squiggle-lang/src/rescript/symbolicDist/SymbolicDist.res @@ -2,7 +2,7 @@ open SymbolicDistTypes module Normal = { type t = normal - let make = (mean: float, stdev: float): result => + let make = (mean: float, stdev: float): result => stdev > 0.0 ? Ok(#Normal({mean: mean, stdev: stdev})) : Error("Standard deviation of normal distribution must be larger than 0") @@ -48,11 +48,13 @@ module Normal = { module Exponential = { type t = exponential - let make = (rate: float): result => + let make = (rate: float): result => rate > 0.0 - ? Ok(#Exponential({ - rate: rate, - })) + ? Ok( + #Exponential({ + rate: rate, + }), + ) : Error("Exponential distributions mean must be larger than 0") let pdf = (x, t: t) => Jstat.Exponential.pdf(x, t.rate) let cdf = (x, t: t) => Jstat.Exponential.cdf(x, t.rate) @@ -89,7 +91,7 @@ module Triangular = { module Beta = { type t = beta - let make = (alpha, beta) => + let make = (alpha, beta) => alpha > 0.0 && beta > 0.0 ? Ok(#Beta({alpha: alpha, beta: beta})) : Error("Beta distribution parameters must be positive") @@ -103,10 +105,10 @@ module Beta = { module Lognormal = { type t = lognormal - let make = (mu, sigma) => - sigma > 0.0 - ? Ok(#Lognormal({mu: mu, sigma: sigma})) - : Error("Lognormal standard deviation must be larger than 0") + let make = (mu, sigma) => + sigma > 0.0 + ? Ok(#Lognormal({mu: mu, sigma: sigma})) + : Error("Lognormal standard deviation must be larger than 0") let pdf = (x, t: t) => Jstat.Lognormal.pdf(x, t.mu, t.sigma) let cdf = (x, t: t) => Jstat.Lognormal.cdf(x, t.mu, t.sigma) let inv = (p, t: t) => Jstat.Lognormal.inv(p, t.mu, t.sigma) @@ -127,8 +129,7 @@ module Lognormal = { let mu = Js.Math.log(mean) -. 0.5 *. Js.Math.log(variance /. meanSquared +. 1.0) let sigma = Js.Math.pow_float(~base=Js.Math.log(variance /. meanSquared +. 1.0), ~exp=0.5) Ok(#Lognormal({mu: mu, sigma: sigma})) - } - else { + } else { Error("Lognormal standard deviation must be larger than 0") } } @@ -154,9 +155,7 @@ module Lognormal = { module Uniform = { type t = uniform let make = (low, high) => - high > low - ? Ok(#Uniform({low: low, high: high})) - : Error("High must be larger than low") + high > low ? Ok(#Uniform({low: low, high: high})) : Error("High must be larger than low") let pdf = (x, t: t) => Jstat.Uniform.pdf(x, t.low, t.high) let cdf = (x, t: t) => Jstat.Uniform.cdf(x, t.low, t.high) @@ -165,7 +164,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 + //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} @@ -183,6 +182,15 @@ module Float = { let toString = Js.Float.toString } +module From90thPercentile = { + let make = (low, high) => + switch (low, high) { + | (low, high) if low <= 0.0 && low < high => Ok(Normal.from90PercentCI(low, high)) + | (low, high) if low < high => Ok(Lognormal.from90PercentCI(low, high)) + | (_, _) => Error("Low value must be less than high value.") + } +} + module T = { let minCdfValue = 0.0001 let maxCdfValue = 0.9999 diff --git a/packages/squiggle-lang/src/rescript/utility/Operation.res b/packages/squiggle-lang/src/rescript/utility/Operation.res index 540bd08c..55e0b42f 100644 --- a/packages/squiggle-lang/src/rescript/utility/Operation.res +++ b/packages/squiggle-lang/src/rescript/utility/Operation.res @@ -7,11 +7,11 @@ type algebraicOperation = [ | #Subtract | #Divide | #Exponentiate - | #Log + | #Logarithm ] @genType type pointwiseOperation = [#Add | #Multiply | #Exponentiate] -type scaleOperation = [#Multiply | #Exponentiate | #Log | #Divide] +type scaleOperation = [#Multiply | #Exponentiate | #Logarithm | #Divide] type distToFloatOperation = [ | #Pdf(float) | #Cdf(float) @@ -29,7 +29,7 @@ module Algebraic = { | #Multiply => \"*." | #Exponentiate => \"**" | #Divide => \"/." - | #Log => (a, b) => log(a) /. log(b) + | #Logarithm => (a, b) => log(a) /. log(b) } let applyFn = (t, f1, f2) => @@ -45,7 +45,7 @@ module Algebraic = { | #Multiply => "*" | #Exponentiate => "**" | #Divide => "/" - | #Log => "log" + | #Logarithm => "log" } let format = (a, b, c) => b ++ (" " ++ (toString(a) ++ (" " ++ c))) @@ -84,7 +84,7 @@ module Scale = { | #Multiply => \"*." | #Divide => \"/." | #Exponentiate => \"**" - | #Log => (a, b) => log(a) /. log(b) + | #Logarithm => (a, b) => log(a) /. log(b) } let format = (operation: t, value, scaleBy) => @@ -92,7 +92,7 @@ module Scale = { | #Multiply => j`verticalMultiply($value, $scaleBy) ` | #Divide => j`verticalDivide($value, $scaleBy) ` | #Exponentiate => j`verticalExponentiate($value, $scaleBy) ` - | #Log => j`verticalLog($value, $scaleBy) ` + | #Logarithm => j`verticalLog($value, $scaleBy) ` } let toIntegralSumCacheFn = x => @@ -100,7 +100,7 @@ module Scale = { | #Multiply => (a, b) => Some(a *. b) | #Divide => (a, b) => Some(a /. b) | #Exponentiate => (_, _) => None - | #Log => (_, _) => None + | #Logarithm => (_, _) => None } let toIntegralCacheFn = x => @@ -108,7 +108,7 @@ module Scale = { | #Multiply => (_, _) => None // TODO: this could probably just be multiplied out (using Continuous.scaleBy) | #Divide => (_, _) => None | #Exponentiate => (_, _) => None - | #Log => (_, _) => None + | #Logarithm => (_, _) => None } } From 688cf0b19e3e141b839239a704716ab9049bba22 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Sat, 2 Apr 2022 11:01:53 -0400 Subject: [PATCH 07/10] Changes in response to CR --- .../ReducerInterface_Distribution_test.res | 40 +++++++++---------- .../ReducerInterface_GenericDistribution.res | 30 +++++++++----- 2 files changed, 39 insertions(+), 31 deletions(-) diff --git a/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res b/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res index ada50f3f..f57a4d7b 100644 --- a/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res +++ b/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res @@ -1,28 +1,28 @@ open Jest open Reducer_TestHelpers -let makeTest = (str, result) => test(str, () => expectEvalToBe(str, result)) +let testEval = (str, result) => test(str, () => expectEvalToBe(str, result)) describe("eval", () => { Only.describe("expressions", () => { - makeTest("normal(5,2)", "Ok(Normal(5,2))") - makeTest("5 to 2", "Error(TODO: Low value must be less than high value.)") - makeTest("to(2,5)", "Ok(Lognormal(1.1512925464970227,0.278507821238345))") - makeTest("to(-2,2)", "Ok(Normal(0,1.215913388057542))") - makeTest("lognormal(5,2)", "Ok(Lognormal(5,2))") - makeTest("mean(normal(5,2))", "Ok(5)") - makeTest("mean(lognormal(1,2))", "Ok(20.085536923187668)") - makeTest("normalize(normal(5,2))", "Ok(Normal(5,2))") - makeTest("toPointSet(normal(5,2))", "Ok(Point Set Distribution)") - makeTest("toSampleSet(normal(5,2), 100)", "Ok(Sample Set Distribution)") - makeTest("add(normal(5,2), normal(10,2))", "Ok(Normal(15,2.8284271247461903))") - makeTest("add(normal(5,2), lognormal(10,2))", "Ok(Sample Set Distribution)") - makeTest("dotAdd(normal(5,2), lognormal(10,2))", "Ok(Point Set Distribution)") - makeTest("dotAdd(normal(5,2), 3)", "Ok(Point Set Distribution)") - makeTest("add(normal(5,2), 3)", "Ok(Point Set Distribution)") - makeTest("add(3, normal(5,2))", "Ok(Point Set Distribution)") - makeTest("3+normal(5,2)", "Ok(Point Set Distribution)") - makeTest("add(3, 3)", "Ok(6)") - makeTest("truncateLeft(normal(5,2), 3)", "Ok(Point Set Distribution)") + testEval("normal(5,2)", "Ok(Normal(5,2))") + testEval("5 to 2", "Error(TODO: Low value must be less than high value.)") + testEval("to(2,5)", "Ok(Lognormal(1.1512925464970227,0.278507821238345))") + testEval("to(-2,2)", "Ok(Normal(0,1.215913388057542))") + testEval("lognormal(5,2)", "Ok(Lognormal(5,2))") + testEval("mean(normal(5,2))", "Ok(5)") + testEval("mean(lognormal(1,2))", "Ok(20.085536923187668)") + testEval("normalize(normal(5,2))", "Ok(Normal(5,2))") + testEval("toPointSet(normal(5,2))", "Ok(Point Set Distribution)") + testEval("toSampleSet(normal(5,2), 100)", "Ok(Sample Set Distribution)") + testEval("add(normal(5,2), normal(10,2))", "Ok(Normal(15,2.8284271247461903))") + testEval("add(normal(5,2), lognormal(10,2))", "Ok(Sample Set Distribution)") + testEval("dotAdd(normal(5,2), lognormal(10,2))", "Ok(Point Set Distribution)") + testEval("dotAdd(normal(5,2), 3)", "Ok(Point Set Distribution)") + testEval("add(normal(5,2), 3)", "Ok(Point Set Distribution)") + testEval("add(3, normal(5,2))", "Ok(Point Set Distribution)") + testEval("3+normal(5,2)", "Ok(Point Set Distribution)") + testEval("add(3, 3)", "Ok(6)") + testEval("truncateLeft(normal(5,2), 3)", "Ok(Point Set Distribution)") }) }) diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res index 9970c340..12d0ddb4 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res @@ -12,17 +12,17 @@ module Helpers = { let arithmeticMap = r => switch r { | "add" => #Add - | "dotAdd" => #Add + | "pointwiseAdd" => #Add | "subtract" => #Subtract - | "dotSubtract" => #Subtract + | "pointwiseSubtract" => #Subtract | "divide" => #Divide | "logarithm" => #Logarithm - | "dotDivide" => #Divide + | "pointwiseDivide" => #Divide | "exponentiate" => #Exponentiate - | "dotExponentiate" => #Exponentiate + | "pointwiseExponentiate" => #Exponentiate | "multiply" => #Multiply - | "dotMultiply" => #Multiply - | "dotLogarithm" => #Logarithm + | "pointwiseMultiply" => #Multiply + | "pointwiseLogarithm" => #Logarithm | _ => #Multiply } @@ -65,7 +65,7 @@ module SymbolicConstructors = { let oneFloat = name => switch name { | "exponential" => Ok(SymbolicDist.Exponential.make) - | _ => Error("impossible path") + | _ => Error("Unreachable state") } let twoFloat = name => @@ -75,13 +75,13 @@ module SymbolicConstructors = { | "beta" => Ok(SymbolicDist.Beta.make) | "lognormal" => Ok(SymbolicDist.Lognormal.make) | "to" => Ok(SymbolicDist.From90thPercentile.make) - | _ => Error("impossible path") + | _ => Error("Unreachable state") } let threeFloat = name => switch name { | "triangular" => Ok(SymbolicDist.Triangular.make) - | _ => Error("impossible path") + | _ => Error("Unreachable state") } let symbolicResultToOutput = ( @@ -128,12 +128,20 @@ let dispatchToGenericOutput = (call: ExpressionValue.functionCall): option< Helpers.toDistFn(Truncate(None, Some(float)), dist) | ("truncate", [EvDistribution(dist), EvNumber(float1), EvNumber(float2)]) => Helpers.toDistFn(Truncate(Some(float1), Some(float2)), dist) - | (("add" | "multiply" | "subtract" | "divide" | "exponentiate" | "log") as arithmetic, [a, b] as args) => + | ( + ("add" | "multiply" | "subtract" | "divide" | "exponentiate" | "log") as arithmetic, + [a, b] as args, + ) => Helpers.catchAndConvertTwoArgsToDists(args)->E.O2.fmap(((fst, snd)) => Helpers.twoDiststoDistFn(Algebraic, arithmetic, fst, snd) ) | ( - ("dotAdd" | "dotMultiply" | "dotSubtract" | "dotDivide" | "dotExponentiate" | "dotLogarithm") as arithmetic, + ("pointwiseAdd" + | "pointwiseMultiply" + | "pointwiseSubtract" + | "pointwiseDivide" + | "pointwiseExponentiate" + | "pointwiseLogarithm") as arithmetic, [a, b] as args, ) => Helpers.catchAndConvertTwoArgsToDists(args)->E.O2.fmap(((fst, snd)) => From 46af9233a19f0f559f2cbfde54d9b40429c8c66f Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Sat, 2 Apr 2022 11:06:09 -0400 Subject: [PATCH 08/10] Fixed tests to use pointwiseAdd instead of .add --- .../ReducerInterface/ReducerInterface_Distribution_test.res | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res b/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res index f57a4d7b..55ae387c 100644 --- a/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res +++ b/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res @@ -17,8 +17,8 @@ describe("eval", () => { testEval("toSampleSet(normal(5,2), 100)", "Ok(Sample Set Distribution)") testEval("add(normal(5,2), normal(10,2))", "Ok(Normal(15,2.8284271247461903))") testEval("add(normal(5,2), lognormal(10,2))", "Ok(Sample Set Distribution)") - testEval("dotAdd(normal(5,2), lognormal(10,2))", "Ok(Point Set Distribution)") - testEval("dotAdd(normal(5,2), 3)", "Ok(Point Set Distribution)") + testEval("pointwiseAdd(normal(5,2), lognormal(10,2))", "Ok(Point Set Distribution)") + testEval("pointwiseAdd(normal(5,2), 3)", "Ok(Point Set Distribution)") testEval("add(normal(5,2), 3)", "Ok(Point Set Distribution)") testEval("add(3, normal(5,2))", "Ok(Point Set Distribution)") testEval("3+normal(5,2)", "Ok(Point Set Distribution)") From 06285dbdc10fd6efd97c253cab6766e41451a64f Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Sat, 2 Apr 2022 16:25:41 -0400 Subject: [PATCH 09/10] Additional testing for GenericDist-Reducer interface, plus getting log, exp to work with 1 param --- .../ReducerInterface_Distribution_test.res | 88 +++++++++++++++++-- .../ReducerInterface_GenericDistribution.res | 48 ++++++---- 2 files changed, 113 insertions(+), 23 deletions(-) diff --git a/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res b/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res index 55ae387c..b514232a 100644 --- a/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res +++ b/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res @@ -1,10 +1,18 @@ open Jest -open Reducer_TestHelpers -let testEval = (str, result) => test(str, () => expectEvalToBe(str, result)) +let testSkip: (bool, string, unit => assertion) => unit = (skip: bool) => + if skip { + Skip.test + } else { + test + } +let testEval = (~skip=false, str, result) => + testSkip(skip)(str, () => Reducer_TestHelpers.expectEvalToBe(str, result)) +let testParse = (~skip=false, str, result) => + testSkip(skip)(str, () => Reducer_TestHelpers.expectParseToBe(str, result)) describe("eval", () => { - Only.describe("expressions", () => { + describe("expressions", () => { testEval("normal(5,2)", "Ok(Normal(5,2))") testEval("5 to 2", "Error(TODO: Low value must be less than high value.)") testEval("to(2,5)", "Ok(Lognormal(1.1512925464970227,0.278507821238345))") @@ -17,12 +25,82 @@ describe("eval", () => { testEval("toSampleSet(normal(5,2), 100)", "Ok(Sample Set Distribution)") testEval("add(normal(5,2), normal(10,2))", "Ok(Normal(15,2.8284271247461903))") testEval("add(normal(5,2), lognormal(10,2))", "Ok(Sample Set Distribution)") - testEval("pointwiseAdd(normal(5,2), lognormal(10,2))", "Ok(Point Set Distribution)") - testEval("pointwiseAdd(normal(5,2), 3)", "Ok(Point Set Distribution)") testEval("add(normal(5,2), 3)", "Ok(Point Set Distribution)") testEval("add(3, normal(5,2))", "Ok(Point Set Distribution)") testEval("3+normal(5,2)", "Ok(Point Set Distribution)") + testEval("normal(5,2)+3", "Ok(Point Set Distribution)") testEval("add(3, 3)", "Ok(6)") testEval("truncateLeft(normal(5,2), 3)", "Ok(Point Set Distribution)") + testEval("truncateRight(normal(5,2), 3)", "Ok(Point Set Distribution)") + testEval("truncate(normal(5,2), 3, 8)", "Ok(Point Set Distribution)") + }) + + describe("exp", () => { + testEval("exp(normal(5,2))", "Ok(Point Set Distribution)") + }) + + describe("pow", () => { + testEval("pow(3, uniform(5,8))", "Ok(Point Set Distribution)") + testEval("pow(uniform(5,8), 3)", "Ok(Point Set Distribution)") + testEval("pow(uniform(5,8), uniform(9, 10))", "Ok(Sample Set Distribution)") + }) + + describe("log", () => { + testEval("log(2, uniform(5,8))", "Ok(Point Set Distribution)") + testEval("log(normal(5,2), 3)", "Ok(Point Set Distribution)") + testEval("log(normal(5,2), normal(10,1))", "Ok(Sample Set Distribution)") + testEval("log(uniform(5,8))", "Ok(Point Set Distribution)") + testEval("log10(uniform(5,8))", "Ok(Point Set Distribution)") + }) + + describe("dotLog", () => { + testEval("dotLog(normal(5,2), 3)", "Ok(Point Set Distribution)") + testEval("dotLog(normal(5,2), 3)", "Ok(Point Set Distribution)") + testEval("dotLog(normal(5,2), normal(10,1))", "Ok(Point Set Distribution)") + }) + + describe("dotAdd", () => { + testEval("dotAdd(normal(5,2), lognormal(10,2))", "Ok(Point Set Distribution)") + testEval("dotAdd(normal(5,2), 3)", "Ok(Point Set Distribution)") + }) + + describe("equality", () => { + testEval(~skip=true, "normal(5,2) == normal(5,2)", "Ok(true)") + }) + + describe("mixture", () => { + testEval( + ~skip=true, + "mx(normal(5,2), normal(10,1), normal(15, 1))", + "Ok(Point Set Distribution)", + ) + testEval( + ~skip=true, + "mixture(normal(5,2), normal(10,1), [.2,, .4])", + "Ok(Point Set Distribution)", + ) + }) +}) + +describe("MathJs parse", () => { + describe("literals operators paranthesis", () => { + testParse("mean(normal(5,2) + normal(5,1))", "Ok((:mean (:add (:normal 5 2) (:normal 5 1))))") + testParse("normal(5,2) .* normal(5,1)", "Ok((:dotMultiply (:normal 5 2) (:normal 5 1)))") + testParse("normal(5,2) ./ normal(5,1)", "Ok((:dotDivide (:normal 5 2) (:normal 5 1)))") + testParse("normal(5,2) .^ normal(5,1)", "Ok((:dotPow (:normal 5 2) (:normal 5 1)))") + testParse("normal(5,2) ^ normal(5,1)", "Ok((:pow (:normal 5 2) (:normal 5 1)))") + testParse("3 ^ normal(5,1)", "Ok((:pow 3 (:normal 5 1)))") + testParse("normal(5,2) ^ 3", "Ok((:pow (:normal 5 2) 3))") + testParse("5 == normal(5,2)", "Ok((:equal 5 (:normal 5 2)))") + describe("adding two normals", () => { + testParse( + ~skip=true, + "normal(5,2) .+ normal(5,1)", + "Ok((:dotAdd (:normal 5 2) (:normal 5 1)))", + ) + }) + describe("exponential of one distribution", () => { + testParse(~skip=true, "exp(normal(5,2)", "Ok((:pow (:normal 5 2) 3))") + }) }) }) diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res index 12d0ddb4..11376001 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res @@ -12,17 +12,17 @@ module Helpers = { let arithmeticMap = r => switch r { | "add" => #Add - | "pointwiseAdd" => #Add + | "dotAdd" => #Add | "subtract" => #Subtract - | "pointwiseSubtract" => #Subtract + | "dotSubtract" => #Subtract | "divide" => #Divide - | "logarithm" => #Logarithm - | "pointwiseDivide" => #Divide - | "exponentiate" => #Exponentiate - | "pointwiseExponentiate" => #Exponentiate + | "log" => #Logarithm + | "dotDivide" => #Divide + | "pow" => #Exponentiate + | "dotPow" => #Exponentiate | "multiply" => #Multiply - | "pointwiseMultiply" => #Multiply - | "pointwiseLogarithm" => #Logarithm + | "dotMultiply" => #Multiply + | "dotLog" => #Logarithm | _ => #Multiply } @@ -93,6 +93,10 @@ module SymbolicConstructors = { } } +module Math = { + let e = 2.718281828459 +} + let dispatchToGenericOutput = (call: ExpressionValue.functionCall): option< GenericDist_GenericOperation.outputType, > => { @@ -115,6 +119,9 @@ let dispatchToGenericOutput = (call: ExpressionValue.functionCall): option< ->SymbolicConstructors.symbolicResultToOutput | ("sample", [EvDistribution(dist)]) => Helpers.toFloatFn(#Sample, dist) | ("mean", [EvDistribution(dist)]) => Helpers.toFloatFn(#Mean, dist) + | ("exp", [EvDistribution(a)]) => + // https://mathjs.org/docs/reference/functions/exp.html + Helpers.twoDiststoDistFn(Algebraic, "pow", GenericDist.fromFloat(Math.e), a)->Some | ("normalize", [EvDistribution(dist)]) => Helpers.toDistFn(Normalize, dist) | ("toPointSet", [EvDistribution(dist)]) => Helpers.toDistFn(ToPointSet, dist) | ("cdf", [EvDistribution(dist), EvNumber(float)]) => Helpers.toFloatFn(#Cdf(float), dist) @@ -128,25 +135,30 @@ let dispatchToGenericOutput = (call: ExpressionValue.functionCall): option< Helpers.toDistFn(Truncate(None, Some(float)), dist) | ("truncate", [EvDistribution(dist), EvNumber(float1), EvNumber(float2)]) => Helpers.toDistFn(Truncate(Some(float1), Some(float2)), dist) - | ( - ("add" | "multiply" | "subtract" | "divide" | "exponentiate" | "log") as arithmetic, - [a, b] as args, - ) => + | ("log", [EvDistribution(a)]) => + Helpers.twoDiststoDistFn(Algebraic, "log", a, GenericDist.fromFloat(Math.e))->Some + | ("log10", [EvDistribution(a)]) => + Helpers.twoDiststoDistFn(Algebraic, "log", a, GenericDist.fromFloat(10.0))->Some + | (("add" | "multiply" | "subtract" | "divide" | "pow" | "log") as arithmetic, [a, b] as args) => Helpers.catchAndConvertTwoArgsToDists(args)->E.O2.fmap(((fst, snd)) => Helpers.twoDiststoDistFn(Algebraic, arithmetic, fst, snd) ) | ( - ("pointwiseAdd" - | "pointwiseMultiply" - | "pointwiseSubtract" - | "pointwiseDivide" - | "pointwiseExponentiate" - | "pointwiseLogarithm") as arithmetic, + ("dotAdd" + | "dotMultiply" + | "dotSubtract" + | "dotDivide" + | "dotPow" + | "dotLog") as arithmetic, [a, b] as args, ) => Helpers.catchAndConvertTwoArgsToDists(args)->E.O2.fmap(((fst, snd)) => Helpers.twoDiststoDistFn(Pointwise, arithmetic, fst, snd) ) + | ("dotLog", [EvDistribution(a)]) => + Helpers.twoDiststoDistFn(Pointwise, "dotLog", a, GenericDist.fromFloat(Math.e))->Some + | ("dotExp", [EvDistribution(a)]) => + Helpers.twoDiststoDistFn(Pointwise, "dotPow", GenericDist.fromFloat(Math.e), a)->Some | _ => None } } From e4f563fa0813b41f5295ef7c6425eaed293b1bc8 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Sat, 2 Apr 2022 17:06:57 -0400 Subject: [PATCH 10/10] Added unary minus function support to distributions, cleaned tests --- .../ReducerInterface_Distribution_test.res | 59 ++++++++++++------- .../ReducerInterface_GenericDistribution.res | 2 + 2 files changed, 41 insertions(+), 20 deletions(-) diff --git a/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res b/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res index b514232a..85d80919 100644 --- a/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res +++ b/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res @@ -11,25 +11,43 @@ let testEval = (~skip=false, str, result) => let testParse = (~skip=false, str, result) => testSkip(skip)(str, () => Reducer_TestHelpers.expectParseToBe(str, result)) -describe("eval", () => { - describe("expressions", () => { +describe("eval on distribution functions", () => { + describe("normal distribution", () => { testEval("normal(5,2)", "Ok(Normal(5,2))") + }) + describe("lognormal distribution", () => { + testEval("lognormal(5,2)", "Ok(Lognormal(5,2))") + }) + describe("unaryMinus", () => { + testEval("mean(-normal(5,2))", "Ok(-5.002887370380851)") + }) + describe("to", () => { testEval("5 to 2", "Error(TODO: Low value must be less than high value.)") testEval("to(2,5)", "Ok(Lognormal(1.1512925464970227,0.278507821238345))") testEval("to(-2,2)", "Ok(Normal(0,1.215913388057542))") - testEval("lognormal(5,2)", "Ok(Lognormal(5,2))") + }) + describe("mean", () => { testEval("mean(normal(5,2))", "Ok(5)") testEval("mean(lognormal(1,2))", "Ok(20.085536923187668)") + }) + describe("normalize", () => { testEval("normalize(normal(5,2))", "Ok(Normal(5,2))") + }) + describe("toPointSet", () => { testEval("toPointSet(normal(5,2))", "Ok(Point Set Distribution)") + }) + describe("toSampleSet", () => { testEval("toSampleSet(normal(5,2), 100)", "Ok(Sample Set Distribution)") + }) + describe("add", () => { testEval("add(normal(5,2), normal(10,2))", "Ok(Normal(15,2.8284271247461903))") testEval("add(normal(5,2), lognormal(10,2))", "Ok(Sample Set Distribution)") testEval("add(normal(5,2), 3)", "Ok(Point Set Distribution)") testEval("add(3, normal(5,2))", "Ok(Point Set Distribution)") testEval("3+normal(5,2)", "Ok(Point Set Distribution)") testEval("normal(5,2)+3", "Ok(Point Set Distribution)") - testEval("add(3, 3)", "Ok(6)") + }) + describe("truncate", () => { testEval("truncateLeft(normal(5,2), 3)", "Ok(Point Set Distribution)") testEval("truncateRight(normal(5,2), 3)", "Ok(Point Set Distribution)") testEval("truncate(normal(5,2), 3, 8)", "Ok(Point Set Distribution)") @@ -82,25 +100,26 @@ describe("eval", () => { }) }) -describe("MathJs parse", () => { - describe("literals operators paranthesis", () => { - testParse("mean(normal(5,2) + normal(5,1))", "Ok((:mean (:add (:normal 5 2) (:normal 5 1))))") - testParse("normal(5,2) .* normal(5,1)", "Ok((:dotMultiply (:normal 5 2) (:normal 5 1)))") - testParse("normal(5,2) ./ normal(5,1)", "Ok((:dotDivide (:normal 5 2) (:normal 5 1)))") - testParse("normal(5,2) .^ normal(5,1)", "Ok((:dotPow (:normal 5 2) (:normal 5 1)))") +describe("parse on distribution functions", () => { + describe("power", () => { testParse("normal(5,2) ^ normal(5,1)", "Ok((:pow (:normal 5 2) (:normal 5 1)))") testParse("3 ^ normal(5,1)", "Ok((:pow 3 (:normal 5 1)))") testParse("normal(5,2) ^ 3", "Ok((:pow (:normal 5 2) 3))") + }) + describe("pointwise arithmetic expressions", () => { + testParse(~skip=true, "normal(5,2) .+ normal(5,1)", "Ok((:dotAdd (:normal 5 2) (:normal 5 1)))") + testParse(~skip=true, "normal(5,2) .- normal(5,1)", "Ok((:dotSubtract (:normal 5 2) (:normal 5 1)))") + testParse("normal(5,2) .* normal(5,1)", "Ok((:dotMultiply (:normal 5 2) (:normal 5 1)))") + testParse("normal(5,2) ./ normal(5,1)", "Ok((:dotDivide (:normal 5 2) (:normal 5 1)))") + testParse("normal(5,2) .^ normal(5,1)", "Ok((:dotPow (:normal 5 2) (:normal 5 1)))") + }) + describe("equality", () => { testParse("5 == normal(5,2)", "Ok((:equal 5 (:normal 5 2)))") - describe("adding two normals", () => { - testParse( - ~skip=true, - "normal(5,2) .+ normal(5,1)", - "Ok((:dotAdd (:normal 5 2) (:normal 5 1)))", - ) - }) - describe("exponential of one distribution", () => { - testParse(~skip=true, "exp(normal(5,2)", "Ok((:pow (:normal 5 2) 3))") - }) + }) + describe("pointwise adding two normals", () => { + testParse(~skip=true, "normal(5,2) .+ normal(5,1)", "Ok((:dotAdd (:normal 5 2) (:normal 5 1)))") + }) + describe("exponential of one distribution", () => { + testParse(~skip=true, "exp(normal(5,2)", "Ok((:pow (:normal 5 2) 3))") }) }) diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res index 11376001..8fabdfa8 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res @@ -139,6 +139,8 @@ let dispatchToGenericOutput = (call: ExpressionValue.functionCall): option< Helpers.twoDiststoDistFn(Algebraic, "log", a, GenericDist.fromFloat(Math.e))->Some | ("log10", [EvDistribution(a)]) => Helpers.twoDiststoDistFn(Algebraic, "log", a, GenericDist.fromFloat(10.0))->Some + | ("unaryMinus", [EvDistribution(a)]) => + Helpers.twoDiststoDistFn(Algebraic, "multiply", a, GenericDist.fromFloat(-1.0))->Some | (("add" | "multiply" | "subtract" | "divide" | "pow" | "log") as arithmetic, [a, b] as args) => Helpers.catchAndConvertTwoArgsToDists(args)->E.O2.fmap(((fst, snd)) => Helpers.twoDiststoDistFn(Algebraic, arithmetic, fst, snd)