Compare commits
3 Commits
develop
...
symbolic-e
Author | SHA1 | Date | |
---|---|---|---|
|
c4ccd6ee72 | ||
|
93ed0e6a5d | ||
|
47a65f0544 |
|
@ -3,19 +3,67 @@ open SymbolicDistTypes
|
||||||
let normal95confidencePoint = 1.6448536269514722
|
let normal95confidencePoint = 1.6448536269514722
|
||||||
// explained in website/docs/internal/ProcessingConfidenceIntervals
|
// explained in website/docs/internal/ProcessingConfidenceIntervals
|
||||||
|
|
||||||
|
type normalError = NormalStandardDeviationGreaterThanZero(float)
|
||||||
|
|
||||||
|
@genType
|
||||||
|
type rec error =
|
||||||
|
| NotFinite(string, string, float)
|
||||||
|
| DivideByZero(string)
|
||||||
|
| NormalError(normalError)
|
||||||
|
| SafeMath(SafeMath.error)
|
||||||
|
| MultipleErrors(array<error>)
|
||||||
|
| NinetiethPercentileShouldBeOrdered
|
||||||
|
|
||||||
|
module Error = {
|
||||||
|
let mapErrorArrayToError = (errors: array<error>): option<error> => {
|
||||||
|
switch errors {
|
||||||
|
| [] => None
|
||||||
|
| [error] => Some(error)
|
||||||
|
| _ => Some(MultipleErrors(errors))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let checkIsFinite = (value, fnName, propertyName) =>
|
||||||
|
E.Float.isFinite(value) ? None : Some(NotFinite(fnName, propertyName, value))
|
||||||
|
|
||||||
|
let divideByZero = (value, fnName) => 0.0 == value ? None : Some(DivideByZero(fnName))
|
||||||
|
}
|
||||||
|
|
||||||
|
let ifNoErrorsThanDo = (errors, fn) =>
|
||||||
|
errors->E.A.O.concatSomes->Error.mapErrorArrayToError->E.O.errorToResult(fn)
|
||||||
|
|
||||||
module Normal = {
|
module Normal = {
|
||||||
type t = normal
|
type t = normal
|
||||||
let make = (mean: float, stdev: float): result<symbolicDist, string> =>
|
let dangerouslyMake = (~mean: float, ~stdev: float) => #Normal({mean: mean, stdev: stdev})
|
||||||
stdev > 0.0
|
let inputValidation = (~mean, ~stdev) =>
|
||||||
? Ok(#Normal({mean: mean, stdev: stdev}))
|
[
|
||||||
: Error("Standard deviation of normal distribution must be larger than 0")
|
Error.checkIsFinite(mean, "Normal", "mean"),
|
||||||
|
Error.checkIsFinite(stdev, "Normal", "stdev"),
|
||||||
|
stdev <= 0.0 ? Some(NormalError(NormalStandardDeviationGreaterThanZero(stdev))) : None,
|
||||||
|
]
|
||||||
|
->E.A.O.concatSomes
|
||||||
|
->Error.mapErrorArrayToError
|
||||||
|
|
||||||
|
let make = (~mean: float, ~stdev: float): result<symbolicDist, error> =>
|
||||||
|
inputValidation(~mean, ~stdev)->E.O.errorToResult(() => Ok(dangerouslyMake(~mean, ~stdev)))
|
||||||
|
|
||||||
let pdf = (x, t: t) => Jstat.Normal.pdf(x, t.mean, t.stdev)
|
let pdf = (x, t: t) => Jstat.Normal.pdf(x, t.mean, t.stdev)
|
||||||
let cdf = (x, t: t) => Jstat.Normal.cdf(x, t.mean, t.stdev)
|
let cdf = (x, t: t) => Jstat.Normal.cdf(x, t.mean, t.stdev)
|
||||||
|
|
||||||
let from90PercentCI = (low, high) => {
|
let from90PercentCI = (low, high) => {
|
||||||
let mean = E.A.Floats.mean([low, high])
|
let construct = () => {
|
||||||
let stdev = (high -. low) /. (2. *. normal95confidencePoint)
|
let mean = E.A.Floats.mean([low, high])
|
||||||
#Normal({mean: mean, stdev: stdev})
|
let stdev =
|
||||||
|
SafeMath.F.divide(
|
||||||
|
~num=high -. low,
|
||||||
|
~denominator=2. *. normal95confidencePoint,
|
||||||
|
)->E.R2.errMap(r => SafeMath(r))
|
||||||
|
stdev->E.R.bind(stdev => make(~mean, ~stdev))
|
||||||
|
}
|
||||||
|
[
|
||||||
|
Error.checkIsFinite(low, "Normal", "low"),
|
||||||
|
Error.checkIsFinite(high, "Normal", "high"),
|
||||||
|
]->ifNoErrorsThanDo(construct)
|
||||||
}
|
}
|
||||||
let inv = (p, t: t) => Jstat.Normal.inv(p, t.mean, t.stdev)
|
let inv = (p, t: t) => Jstat.Normal.inv(p, t.mean, t.stdev)
|
||||||
let sample = (t: t) => Jstat.Normal.sample(t.mean, t.stdev)
|
let sample = (t: t) => Jstat.Normal.sample(t.mean, t.stdev)
|
||||||
|
@ -25,20 +73,20 @@ module Normal = {
|
||||||
let add = (n1: t, n2: t) => {
|
let add = (n1: t, n2: t) => {
|
||||||
let mean = n1.mean +. n2.mean
|
let mean = n1.mean +. n2.mean
|
||||||
let stdev = Js.Math.sqrt(n1.stdev ** 2. +. n2.stdev ** 2.)
|
let stdev = Js.Math.sqrt(n1.stdev ** 2. +. n2.stdev ** 2.)
|
||||||
#Normal({mean: mean, stdev: stdev})
|
dangerouslyMake(~mean, ~stdev)
|
||||||
}
|
}
|
||||||
let subtract = (n1: t, n2: t) => {
|
let subtract = (n1: t, n2: t) => {
|
||||||
let mean = n1.mean -. n2.mean
|
let mean = n1.mean -. n2.mean
|
||||||
let stdev = Js.Math.sqrt(n1.stdev ** 2. +. n2.stdev ** 2.)
|
let stdev = Js.Math.sqrt(n1.stdev ** 2. +. n2.stdev ** 2.)
|
||||||
#Normal({mean: mean, stdev: stdev})
|
dangerouslyMake(~mean, ~stdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: is this useful here at all? would need the integral as well ...
|
// Note: This isn't being used right now
|
||||||
let pointwiseProduct = (n1: t, n2: t) => {
|
let pointwiseProduct = (n1: t, n2: t) => {
|
||||||
let mean =
|
let mean =
|
||||||
(n1.mean *. n2.stdev ** 2. +. n2.mean *. n1.stdev ** 2.) /. (n1.stdev ** 2. +. n2.stdev ** 2.)
|
(n1.mean *. n2.stdev ** 2. +. n2.mean *. n1.stdev ** 2.) /. (n1.stdev ** 2. +. n2.stdev ** 2.)
|
||||||
let stdev = 1. /. (1. /. n1.stdev ** 2. +. 1. /. n2.stdev ** 2.)
|
let stdev = 1. /. (1. /. n1.stdev ** 2. +. 1. /. n2.stdev ** 2.)
|
||||||
#Normal({mean: mean, stdev: stdev})
|
make(~mean, ~stdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
let operate = (operation: Operation.Algebraic.t, n1: t, n2: t) =>
|
let operate = (operation: Operation.Algebraic.t, n1: t, n2: t) =>
|
||||||
|
@ -50,18 +98,27 @@ module Normal = {
|
||||||
|
|
||||||
let operateFloatFirst = (operation: Operation.Algebraic.t, n1: float, n2: t) =>
|
let operateFloatFirst = (operation: Operation.Algebraic.t, n1: float, n2: t) =>
|
||||||
switch operation {
|
switch operation {
|
||||||
| #Add => Some(#Normal({mean: n1 +. n2.mean, stdev: n2.stdev}))
|
| #Add => Some(make(~mean=n1 +. n2.mean, ~stdev=n2.stdev))
|
||||||
| #Subtract => Some(#Normal({mean: n1 -. n2.mean, stdev: n2.stdev}))
|
| #Subtract => Some(make(~mean=n1 -. n2.mean, ~stdev=n2.stdev))
|
||||||
| #Multiply => Some(#Normal({mean: n1 *. n2.mean, stdev: Js.Math.abs_float(n1) *. n2.stdev}))
|
| #Multiply => Some(make(~mean=n1 *. n2.mean, ~stdev=Js.Math.abs_float(n1) *. n2.stdev))
|
||||||
| _ => None
|
| _ => None
|
||||||
}
|
}
|
||||||
|
|
||||||
let operateFloatSecond = (operation: Operation.Algebraic.t, n1: t, n2: float) =>
|
let operateFloatSecond = (operation: Operation.Algebraic.t, n1: t, n2: float) =>
|
||||||
switch operation {
|
switch operation {
|
||||||
| #Add => Some(#Normal({mean: n1.mean +. n2, stdev: n1.stdev}))
|
| #Add => Some(make(~mean=n1.mean +. n2, ~stdev=n1.stdev))
|
||||||
| #Subtract => Some(#Normal({mean: n1.mean -. n2, stdev: n1.stdev}))
|
| #Subtract => Some(make(~mean=n1.mean -. n2, ~stdev=n1.stdev))
|
||||||
| #Multiply => Some(#Normal({mean: n1.mean *. n2, stdev: n1.stdev *. Js.Math.abs_float(n2)}))
|
| #Multiply => Some(make(~mean=n1.mean *. n2, ~stdev=n1.stdev *. Js.Math.abs_float(n2)))
|
||||||
| #Divide => Some(#Normal({mean: n1.mean /. n2, stdev: n1.stdev /. Js.Math.abs_float(n2)}))
|
| #Divide =>
|
||||||
|
{
|
||||||
|
let mean = SafeMath.F.divide(~num=n1.mean, ~denominator=n2)->E.R2.errMap(r => SafeMath(r))
|
||||||
|
let stdev =
|
||||||
|
SafeMath.F.divide(
|
||||||
|
~num=n1.stdev,
|
||||||
|
~denominator=Js.Math.abs_float(n2),
|
||||||
|
)->E.R2.errMap(r => SafeMath(r))
|
||||||
|
E.R.merge(mean, stdev)->E.R.bind(((mean, stdev)) => make(~mean, ~stdev))
|
||||||
|
}->Some
|
||||||
| _ => None
|
| _ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -230,9 +287,9 @@ module Float = {
|
||||||
module From90thPercentile = {
|
module From90thPercentile = {
|
||||||
let make = (low, high) =>
|
let make = (low, high) =>
|
||||||
switch (low, high) {
|
switch (low, high) {
|
||||||
| (low, high) if low <= 0.0 && low < high => Ok(Normal.from90PercentCI(low, high))
|
| (low, high) if low <= 0.0 && low < high => Normal.from90PercentCI(low, high)
|
||||||
| (low, high) if low < high => Ok(Lognormal.from90PercentCI(low, high))
|
| (low, high) if low < high => Ok(Lognormal.from90PercentCI(low, high))
|
||||||
| (_, _) => Error("Low value must be less than high value.")
|
| (_, _) => Error(NinetiethPercentileShouldBeOrdered)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -136,6 +136,13 @@ module O = {
|
||||||
| None => Error(error)
|
| None => Error(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let errorToResult = (error: option<'a>, fn) => {
|
||||||
|
switch error {
|
||||||
|
| None => fn()
|
||||||
|
| Some(e) => Error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let compare = (compare, f1: option<float>, f2: option<float>) =>
|
let compare = (compare, f1: option<float>, f2: option<float>) =>
|
||||||
switch (f1, f2) {
|
switch (f1, f2) {
|
||||||
| (Some(f1), Some(f2)) => Some(compare(f1, f2) ? f1 : f2)
|
| (Some(f1), Some(f2)) => Some(compare(f1, f2) ? f1 : f2)
|
||||||
|
@ -198,6 +205,13 @@ module Float = {
|
||||||
let with3DigitsPrecision = Js.Float.toPrecisionWithPrecision(_, ~digits=3)
|
let with3DigitsPrecision = Js.Float.toPrecisionWithPrecision(_, ~digits=3)
|
||||||
let toFixed = Js.Float.toFixed
|
let toFixed = Js.Float.toFixed
|
||||||
let toString = Js.Float.toString
|
let toString = Js.Float.toString
|
||||||
|
let isFinite = Js.Float.isFinite
|
||||||
|
let safeDivision = (num: float, denom: float) =>
|
||||||
|
if denom == 0.0 {
|
||||||
|
Error("Division by zero")
|
||||||
|
} else {
|
||||||
|
Ok(num /. denom)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module I = {
|
module I = {
|
||||||
|
|
38
packages/squiggle-lang/src/rescript/Utility/SafeMath.res
Normal file
38
packages/squiggle-lang/src/rescript/Utility/SafeMath.res
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
@genType
|
||||||
|
type rec error =
|
||||||
|
| DivideByZero
|
||||||
|
| IsNaN
|
||||||
|
| IsInfinite
|
||||||
|
| NotFinite(string, float)
|
||||||
|
|
||||||
|
module Error = {
|
||||||
|
let checkIsFinite = (value, fnName) =>
|
||||||
|
E.Float.isFinite(value) ? None : Some(NotFinite(fnName, value))
|
||||||
|
|
||||||
|
let divideByZero = value => 0.0 == value ? None : Some(DivideByZero)
|
||||||
|
}
|
||||||
|
|
||||||
|
module Float = {
|
||||||
|
type t = float
|
||||||
|
let make = (v: t) =>
|
||||||
|
switch Error.checkIsFinite(v, "toSafe") {
|
||||||
|
| None => Ok(v)
|
||||||
|
| Some(e) => Error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module F = {
|
||||||
|
type t = float
|
||||||
|
let divide = (~num: t, ~denominator: t) => {
|
||||||
|
if denominator == 0.0 {
|
||||||
|
Error(DivideByZero)
|
||||||
|
} else {
|
||||||
|
let result = num /. denominator
|
||||||
|
if E.Float.isFinite(result) {
|
||||||
|
Ok(result)
|
||||||
|
} else {
|
||||||
|
Error(NotFinite("divide", result))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user