Merge pull request #379 from quantified-uncertainty/issue326
Issue326 - `asMode` type
This commit is contained in:
commit
bd5dce4829
|
@ -38,6 +38,7 @@
|
|||
"pdfast": "^0.2.0",
|
||||
"rationale": "0.2.0",
|
||||
"rescript": "^9.1.4",
|
||||
"rescript-fast-check": "^1.1.1",
|
||||
"@glennsl/rescript-jest": "^0.9.0",
|
||||
"@istanbuljs/nyc-config-typescript": "^1.0.2",
|
||||
"@types/jest": "^27.4.0",
|
||||
|
|
|
@ -154,10 +154,16 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => {
|
|||
->GenericDist.toPointSet(~xyPointLength, ~sampleCount, ())
|
||||
->E.R2.fmap(r => Dist(PointSet(r)))
|
||||
->OutputLocal.fromResult
|
||||
| ToDistCombination(Algebraic, _, #Float(_)) => GenDistError(NotYetImplemented)
|
||||
| ToDistCombination(Algebraic, arithmeticOperation, #Dist(t2)) =>
|
||||
| ToDistCombination(Algebraic(_), _, #Float(_)) => GenDistError(NotYetImplemented)
|
||||
| ToDistCombination(Algebraic(strategy), arithmeticOperation, #Dist(t2)) =>
|
||||
dist
|
||||
->GenericDist.algebraicCombination(~toPointSetFn, ~toSampleSetFn, ~arithmeticOperation, ~t2)
|
||||
->GenericDist.algebraicCombination(
|
||||
~strategy,
|
||||
~toPointSetFn,
|
||||
~toSampleSetFn,
|
||||
~arithmeticOperation,
|
||||
~t2,
|
||||
)
|
||||
->E.R2.fmap(r => Dist(r))
|
||||
->OutputLocal.fromResult
|
||||
| ToDistCombination(Pointwise, algebraicCombination, #Dist(t2)) =>
|
||||
|
|
|
@ -4,6 +4,8 @@ type genericDist =
|
|||
| SampleSet(SampleSetDist.t)
|
||||
| Symbolic(SymbolicDistTypes.symbolicDist)
|
||||
|
||||
type asAlgebraicCombinationStrategy = AsDefault | AsSymbolic | AsMonteCarlo | AsConvolution
|
||||
|
||||
@genType
|
||||
type error =
|
||||
| NotYetImplemented
|
||||
|
@ -14,6 +16,7 @@ type error =
|
|||
| OperationError(Operation.Error.t)
|
||||
| PointSetConversionError(SampleSetDist.pointsetConversionError)
|
||||
| SparklineError(PointSetTypes.sparklineError) // This type of error is for when we find a sparkline of a discrete distribution. This should probably at some point be actually implemented
|
||||
| RequestedStrategyInvalidError(string)
|
||||
| LogarithmOfDistributionError(string)
|
||||
| OtherError(string)
|
||||
|
||||
|
@ -35,6 +38,7 @@ module Error = {
|
|||
| OperationError(err) => Operation.Error.toString(err)
|
||||
| PointSetConversionError(err) => SampleSetDist.pointsetConversionErrorToString(err)
|
||||
| SparklineError(err) => PointSetTypes.sparklineErrorToString(err)
|
||||
| RequestedStrategyInvalidError(err) => `Requested strategy invalid: ${err}`
|
||||
| OtherError(s) => s
|
||||
}
|
||||
|
||||
|
@ -53,7 +57,7 @@ module DistributionOperation = {
|
|||
type pointsetXSelection = [#Linear | #ByWeight]
|
||||
|
||||
type direction =
|
||||
| Algebraic
|
||||
| Algebraic(asAlgebraicCombinationStrategy)
|
||||
| Pointwise
|
||||
|
||||
type toFloat = [
|
||||
|
@ -110,7 +114,7 @@ module DistributionOperation = {
|
|||
| ToString(ToString) => `toString`
|
||||
| ToString(ToSparkline(n)) => `toSparkline(${E.I.toString(n)})`
|
||||
| ToBool(IsNormalized) => `isNormalized`
|
||||
| ToDistCombination(Algebraic, _, _) => `algebraic`
|
||||
| ToDistCombination(Algebraic(_), _, _) => `algebraic`
|
||||
| ToDistCombination(Pointwise, _, _) => `pointwise`
|
||||
}
|
||||
|
||||
|
@ -139,27 +143,27 @@ module Constructors = {
|
|||
let toString = (dist): t => FromDist(ToString(ToString), dist)
|
||||
let toSparkline = (dist, n): t => FromDist(ToString(ToSparkline(n)), dist)
|
||||
let algebraicAdd = (dist1, dist2: genericDist): t => FromDist(
|
||||
ToDistCombination(Algebraic, #Add, #Dist(dist2)),
|
||||
ToDistCombination(Algebraic(AsDefault), #Add, #Dist(dist2)),
|
||||
dist1,
|
||||
)
|
||||
let algebraicMultiply = (dist1, dist2): t => FromDist(
|
||||
ToDistCombination(Algebraic, #Multiply, #Dist(dist2)),
|
||||
ToDistCombination(Algebraic(AsDefault), #Multiply, #Dist(dist2)),
|
||||
dist1,
|
||||
)
|
||||
let algebraicDivide = (dist1, dist2): t => FromDist(
|
||||
ToDistCombination(Algebraic, #Divide, #Dist(dist2)),
|
||||
ToDistCombination(Algebraic(AsDefault), #Divide, #Dist(dist2)),
|
||||
dist1,
|
||||
)
|
||||
let algebraicSubtract = (dist1, dist2): t => FromDist(
|
||||
ToDistCombination(Algebraic, #Subtract, #Dist(dist2)),
|
||||
ToDistCombination(Algebraic(AsDefault), #Subtract, #Dist(dist2)),
|
||||
dist1,
|
||||
)
|
||||
let algebraicLogarithm = (dist1, dist2): t => FromDist(
|
||||
ToDistCombination(Algebraic, #Logarithm, #Dist(dist2)),
|
||||
ToDistCombination(Algebraic(AsDefault), #Logarithm, #Dist(dist2)),
|
||||
dist1,
|
||||
)
|
||||
let algebraicPower = (dist1, dist2): t => FromDist(
|
||||
ToDistCombination(Algebraic, #Power, #Dist(dist2)),
|
||||
ToDistCombination(Algebraic(AsDefault), #Power, #Dist(dist2)),
|
||||
dist1,
|
||||
)
|
||||
let pointwiseAdd = (dist1, dist2): t => FromDist(
|
||||
|
|
|
@ -147,21 +147,6 @@ let truncate = Truncate.run
|
|||
TODO: It would be useful to be able to pass in a paramater to get this to run either with convolution or monte carlo.
|
||||
*/
|
||||
module AlgebraicCombination = {
|
||||
let tryAnalyticalSimplification = (
|
||||
arithmeticOperation: Operation.algebraicOperation,
|
||||
t1: t,
|
||||
t2: t,
|
||||
): option<result<SymbolicDistTypes.symbolicDist, Operation.Error.t>> =>
|
||||
switch (arithmeticOperation, t1, t2) {
|
||||
| (arithmeticOperation, Symbolic(d1), Symbolic(d2)) =>
|
||||
switch SymbolicDist.T.tryAnalyticalSimplification(d1, d2, arithmeticOperation) {
|
||||
| #AnalyticalSolution(symbolicDist) => Some(Ok(symbolicDist))
|
||||
| #Error(er) => Some(Error(er))
|
||||
| #NoSolution => None
|
||||
}
|
||||
| _ => None
|
||||
}
|
||||
|
||||
let runConvolution = (
|
||||
toPointSet: toPointSetFn,
|
||||
arithmeticOperation: Operation.convolutionOperation,
|
||||
|
@ -240,25 +225,37 @@ module AlgebraicCombination = {
|
|||
| _ => 1000
|
||||
}
|
||||
|
||||
type calculationMethod = MonteCarlo | Convolution(Operation.convolutionOperation)
|
||||
type calculationStrategy = MonteCarloStrat | ConvolutionStrat(Operation.convolutionOperation)
|
||||
|
||||
let chooseConvolutionOrMonteCarlo = (
|
||||
let chooseConvolutionOrMonteCarloDefault = (
|
||||
op: Operation.algebraicOperation,
|
||||
t2: t,
|
||||
t1: t,
|
||||
): calculationMethod =>
|
||||
): calculationStrategy =>
|
||||
switch op {
|
||||
| #Divide
|
||||
| #Power
|
||||
| #Logarithm =>
|
||||
MonteCarlo
|
||||
MonteCarloStrat
|
||||
| (#Add | #Subtract | #Multiply) as convOp =>
|
||||
expectedConvolutionCost(t1) * expectedConvolutionCost(t2) > 10000
|
||||
? MonteCarlo
|
||||
: Convolution(convOp)
|
||||
? MonteCarloStrat
|
||||
: ConvolutionStrat(convOp)
|
||||
}
|
||||
|
||||
let run = (
|
||||
let tryAnalyticalSimplification = (
|
||||
arithmeticOperation: Operation.algebraicOperation,
|
||||
t1: t,
|
||||
t2: t,
|
||||
): option<SymbolicDistTypes.analyticalSimplificationResult> => {
|
||||
switch (t1, t2) {
|
||||
| (DistributionTypes.Symbolic(d1), DistributionTypes.Symbolic(d2)) =>
|
||||
Some(SymbolicDist.T.tryAnalyticalSimplification(d1, d2, arithmeticOperation))
|
||||
| _ => None
|
||||
}
|
||||
}
|
||||
|
||||
let runDefault = (
|
||||
t1: t,
|
||||
~toPointSetFn: toPointSetFn,
|
||||
~toSampleSetFn: toSampleSetFn,
|
||||
|
@ -266,15 +263,16 @@ module AlgebraicCombination = {
|
|||
~t2: t,
|
||||
): result<t, error> => {
|
||||
switch tryAnalyticalSimplification(arithmeticOperation, t1, t2) {
|
||||
| Some(Ok(symbolicDist)) => Ok(Symbolic(symbolicDist))
|
||||
| Some(Error(e)) => Error(OperationError(e))
|
||||
| Some(#AnalyticalSolution(symbolicDist)) => Ok(Symbolic(symbolicDist))
|
||||
| Some(#Error(e)) => Error(OperationError(e))
|
||||
| Some(#NoSolution)
|
||||
| None =>
|
||||
switch getInvalidOperationError(t1, t2, ~toPointSetFn, ~arithmeticOperation) {
|
||||
| Some(e) => Error(e)
|
||||
| None =>
|
||||
switch chooseConvolutionOrMonteCarlo(arithmeticOperation, t1, t2) {
|
||||
| MonteCarlo => runMonteCarlo(toSampleSetFn, arithmeticOperation, t1, t2)
|
||||
| Convolution(convOp) =>
|
||||
switch chooseConvolutionOrMonteCarloDefault(arithmeticOperation, t1, t2) {
|
||||
| MonteCarloStrat => runMonteCarlo(toSampleSetFn, arithmeticOperation, t1, t2)
|
||||
| ConvolutionStrat(convOp) =>
|
||||
runConvolution(toPointSetFn, convOp, t1, t2)->E.R2.fmap(r => DistributionTypes.PointSet(
|
||||
r,
|
||||
))
|
||||
|
@ -282,6 +280,38 @@ module AlgebraicCombination = {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
let run = (
|
||||
~strategy: DistributionTypes.asAlgebraicCombinationStrategy,
|
||||
t1: t,
|
||||
~toPointSetFn: toPointSetFn,
|
||||
~toSampleSetFn: toSampleSetFn,
|
||||
~arithmeticOperation: Operation.algebraicOperation,
|
||||
~t2: t,
|
||||
): result<t, error> => {
|
||||
switch strategy {
|
||||
| AsDefault => runDefault(t1, ~toPointSetFn, ~toSampleSetFn, ~arithmeticOperation, ~t2)
|
||||
| AsSymbolic =>
|
||||
switch tryAnalyticalSimplification(arithmeticOperation, t1, t2) {
|
||||
| Some(#AnalyticalSolution(symbolicDist)) => Ok(Symbolic(symbolicDist))
|
||||
| Some(#NoSolution) => Error(RequestedStrategyInvalidError(`No analytical solution`))
|
||||
| None => Error(RequestedStrategyInvalidError("Inputs were not even symbolic"))
|
||||
| Some(#Error(err)) => Error(OperationError(err))
|
||||
}
|
||||
| AsConvolution => {
|
||||
let errString = opString => `Can't convolve on ${opString}`
|
||||
switch arithmeticOperation {
|
||||
| (#Add | #Subtract | #Multiply) as convOp =>
|
||||
runConvolution(toPointSetFn, convOp, t1, t2)->E.R2.fmap(r => DistributionTypes.PointSet(
|
||||
r,
|
||||
))
|
||||
| (#Divide | #Power | #Logarithm) as op =>
|
||||
op->Operation.Algebraic.toString->errString->RequestedStrategyInvalidError->Error
|
||||
}
|
||||
}
|
||||
| AsMonteCarlo => runMonteCarlo(toSampleSetFn, arithmeticOperation, t1, t2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let algebraicCombination = AlgebraicCombination.run
|
||||
|
|
|
@ -42,6 +42,7 @@ let truncate: (
|
|||
) => result<t, error>
|
||||
|
||||
let algebraicCombination: (
|
||||
~strategy: DistributionTypes.asAlgebraicCombinationStrategy,
|
||||
t,
|
||||
~toPointSetFn: toPointSetFn,
|
||||
~toSampleSetFn: toSampleSetFn,
|
||||
|
|
|
@ -208,7 +208,7 @@ let dispatchToGenericOutput = (call: ExpressionValue.functionCall): option<
|
|||
Helpers.toStringFn(ToSparkline(Belt.Float.toInt(n)), dist)
|
||||
| ("exp", [EvDistribution(a)]) =>
|
||||
// https://mathjs.org/docs/reference/functions/exp.html
|
||||
Helpers.twoDiststoDistFn(Algebraic, "pow", GenericDist.fromFloat(Math.e), a)->Some
|
||||
Helpers.twoDiststoDistFn(Algebraic(AsDefault), "pow", GenericDist.fromFloat(Math.e), a)->Some
|
||||
| ("normalize", [EvDistribution(dist)]) => Helpers.toDistFn(Normalize, dist)
|
||||
| ("isNormalized", [EvDistribution(dist)]) => Helpers.toBoolFn(IsNormalized, dist)
|
||||
| ("toPointSet", [EvDistribution(dist)]) => Helpers.toDistFn(ToPointSet, dist)
|
||||
|
@ -228,14 +228,14 @@ let dispatchToGenericOutput = (call: ExpressionValue.functionCall): option<
|
|||
Helpers.toDistFn(Truncate(Some(float1), Some(float2)), dist)
|
||||
| ("mx" | "mixture", args) => Helpers.mixture(args)->Some
|
||||
| ("log", [EvDistribution(a)]) =>
|
||||
Helpers.twoDiststoDistFn(Algebraic, "log", a, GenericDist.fromFloat(Math.e))->Some
|
||||
Helpers.twoDiststoDistFn(Algebraic(AsDefault), "log", a, GenericDist.fromFloat(Math.e))->Some
|
||||
| ("log10", [EvDistribution(a)]) =>
|
||||
Helpers.twoDiststoDistFn(Algebraic, "log", a, GenericDist.fromFloat(10.0))->Some
|
||||
Helpers.twoDiststoDistFn(Algebraic(AsDefault), "log", a, GenericDist.fromFloat(10.0))->Some
|
||||
| ("unaryMinus", [EvDistribution(a)]) =>
|
||||
Helpers.twoDiststoDistFn(Algebraic, "multiply", a, GenericDist.fromFloat(-1.0))->Some
|
||||
Helpers.twoDiststoDistFn(Algebraic(AsDefault), "multiply", a, GenericDist.fromFloat(-1.0))->Some
|
||||
| (("add" | "multiply" | "subtract" | "divide" | "pow" | "log") as arithmetic, [_, _] as args) =>
|
||||
Helpers.catchAndConvertTwoArgsToDists(args)->E.O2.fmap(((fst, snd)) =>
|
||||
Helpers.twoDiststoDistFn(Algebraic, arithmetic, fst, snd)
|
||||
Helpers.twoDiststoDistFn(Algebraic(AsDefault), arithmetic, fst, snd)
|
||||
)
|
||||
| (
|
||||
("dotAdd"
|
||||
|
|
14
yarn.lock
14
yarn.lock
|
@ -8479,6 +8479,13 @@ fast-check@2.25.0:
|
|||
dependencies:
|
||||
pure-rand "^5.0.1"
|
||||
|
||||
fast-check@^2.17.0:
|
||||
version "2.25.0"
|
||||
resolved "https://registry.yarnpkg.com/fast-check/-/fast-check-2.25.0.tgz#5146601851bf3be0953bd17eb2b7d547936c6561"
|
||||
integrity sha512-wRUT2KD2lAmT75WNIJIHECawoUUMHM0I5jrlLXGtGeqmPL8jl/EldUDjY1VCp6fDY8yflyfUeIOsOBrIbIiArg==
|
||||
dependencies:
|
||||
pure-rand "^5.0.1"
|
||||
|
||||
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3, fast-deep-equal@~3.1.3:
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
|
||||
|
@ -14970,6 +14977,13 @@ requires-port@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
|
||||
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
|
||||
|
||||
rescript-fast-check@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/rescript-fast-check/-/rescript-fast-check-1.1.1.tgz#ef153cb01254b2f01a738faf85b73327d423d5e1"
|
||||
integrity sha512-wxeW0TsL/prkRvEYGbhEiLaLKmYJaECyDcKEWh65hDqP2i76lARzVW3QmYujSYK4OnjAC70dln3X6UC/2m2Huw==
|
||||
dependencies:
|
||||
fast-check "^2.17.0"
|
||||
|
||||
rescript@^9.1.4:
|
||||
version "9.1.4"
|
||||
resolved "https://registry.yarnpkg.com/rescript/-/rescript-9.1.4.tgz#1eb126f98d6c16942c0bf0df67c050198e580515"
|
||||
|
|
Loading…
Reference in New Issue
Block a user