2020-03-24 17:48:46 +00:00
type normal = {
mean : float ,
stdev : float ,
} ;
type lognormal = {
mu : float ,
sigma : float ,
} ;
type uniform = {
low : float ,
high : float ,
} ;
type beta = {
alpha : float ,
beta : float ,
} ;
2020-03-26 16:01:52 +00:00
type exponential = { rate : float } ;
type cauchy = {
local : float ,
scale : float ,
} ;
type triangular = {
low : float ,
medium : float ,
high : float ,
} ;
2020-04-30 10:18:33 +00:00
type continuousShape = {
pdf : DistTypes . continuousShape ,
cdf : DistTypes . continuousShape ,
} ;
2020-04-11 13:22:13 +00:00
2020-04-01 13:52:13 +00:00
type contType = [ | ` Continuous | ` Discrete ] ;
2020-03-24 17:48:46 +00:00
type dist = [
| ` Normal ( normal )
| ` Beta ( beta )
| ` Lognormal ( lognormal )
| ` Uniform ( uniform )
2020-03-26 16:01:52 +00:00
| ` Exponential ( exponential )
| ` Cauchy ( cauchy )
| ` Triangular ( triangular )
2020-04-11 13:22:13 +00:00
| ` ContinuousShape ( continuousShape )
2020-06-10 04:28:03 +00:00
| ` Float ( float ) // Dirac delta at x. Practically useful only in the context of multimodals.
2020-03-24 17:48:46 +00:00
] ;
2020-06-13 06:30:51 +00:00
type integral = float ;
type cutoffX = float ;
type operation = [
| ` AddOperation
| ` SubtractOperation
| ` MultiplyOperation
| ` DivideOperation
| ` ExponentiateOperation
] ;
2020-06-10 04:28:03 +00:00
2020-06-13 06:30:51 +00:00
type distTree = [
| ` Distribution ( dist )
| ` Combination ( distTree , distTree , operation )
| ` PointwiseSum ( distTree , distTree )
| ` PointwiseProduct ( distTree , distTree )
| ` VerticalScaling ( distTree , distTree )
| ` Normalize ( distTree )
| ` LeftTruncate ( distTree , cutoffX )
| ` RightTruncate ( distTree , cutoffX )
| ` Render ( distTree )
]
and weightedDists = array ( ( distTree , float ) ) ;
2020-03-24 17:48:46 +00:00
2020-04-11 13:22:13 +00:00
module ContinuousShape = {
type t = continuousShape ;
2020-04-30 10:18:33 +00:00
let make = ( pdf , cdf ) : t = > { pdf , cdf } ;
let pdf = ( x , t : t ) = >
Distributions . Continuous . T . xToY ( x , t . pdf ) . continuous ;
let inv = ( p , t : t ) = >
Distributions . Continuous . T . xToY ( p , t . pdf ) . continuous ;
2020-04-11 13:22:13 +00:00
// TODO: Fix the sampling, to have it work correctly.
2020-04-30 10:18:33 +00:00
let sample = ( t : t ) = > 3 . 0 ;
let toString = t = > { j | CustomContinuousShape | j } ;
2020-04-11 13:22:13 +00:00
let contType : contType = ` Continuous ;
} ;
2020-03-26 16:01:52 +00:00
module Exponential = {
type t = exponential ;
let pdf = ( x , t : t ) = > Jstat . exponential # # pdf ( x , t . rate ) ;
let inv = ( p , t : t ) = > Jstat . exponential # # inv ( p , t . rate ) ;
let sample = ( t : t ) = > Jstat . exponential # # sample ( t . rate ) ;
let toString = ( { rate } : t ) = > { j | Exponential ( $ rate ) | j } ;
2020-04-01 13:52:13 +00:00
let contType : contType = ` Continuous ;
2020-03-26 16:01:52 +00:00
} ;
module Cauchy = {
type t = cauchy ;
let pdf = ( x , t : t ) = > Jstat . cauchy # # pdf ( x , t . local , t . scale ) ;
let inv = ( p , t : t ) = > Jstat . cauchy # # inv ( p , t . local , t . scale ) ;
let sample = ( t : t ) = > Jstat . cauchy # # sample ( t . local , t . scale ) ;
let toString = ( { local , scale } : t ) = > { j | Cauchy ( $ local , $ scale ) | j } ;
2020-04-01 13:52:13 +00:00
let contType : contType = ` Continuous ;
2020-03-26 16:01:52 +00:00
} ;
module Triangular = {
type t = triangular ;
let pdf = ( x , t : t ) = > Jstat . triangular # # pdf ( x , t . low , t . high , t . medium ) ;
let inv = ( p , t : t ) = > Jstat . triangular # # inv ( p , t . low , t . high , t . medium ) ;
let sample = ( t : t ) = > Jstat . triangular # # sample ( t . low , t . high , t . medium ) ;
let toString = ( { low , medium , high } : t ) = > { j | Triangular ( $ low , $ medium , $ high ) | j } ;
2020-04-01 13:52:13 +00:00
let contType : contType = ` Continuous ;
2020-03-26 16:01:52 +00:00
} ;
2020-03-24 17:48:46 +00:00
module Normal = {
type t = normal ;
let pdf = ( x , t : t ) = > Jstat . normal # # pdf ( x , t . mean , t . stdev ) ;
2020-04-30 10:18:33 +00:00
let from90PercentCI = ( low , high ) = > {
let mean = E . A . Floats . mean ( [| low , high |] ) ;
2020-06-02 22:24:09 +00:00
let stdev = ( high -. low ) /. ( 2 . * . 1 . 644854 ) ;
2020-04-30 10:18:33 +00:00
` Normal ( { mean , stdev } ) ;
} ;
2020-03-24 17:48:46 +00:00
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 toString = ( { mean , stdev } : t ) = > { j | Normal ( $ mean , $ stdev ) | j } ;
2020-04-01 13:52:13 +00:00
let contType : contType = ` Continuous ;
2020-03-24 17:48:46 +00:00
} ;
module Beta = {
type t = beta ;
let pdf = ( x , t : t ) = > Jstat . beta # # pdf ( x , t . alpha , t . beta ) ;
let inv = ( p , t : t ) = > Jstat . beta # # inv ( p , t . alpha , t . beta ) ;
let sample = ( t : t ) = > Jstat . beta # # sample ( t . alpha , t . beta ) ;
let toString = ( { alpha , beta } : t ) = > { j | Beta ( $ alpha , $ beta ) | j } ;
2020-04-01 13:52:13 +00:00
let contType : contType = ` Continuous ;
2020-03-24 17:48:46 +00:00
} ;
module Lognormal = {
type t = lognormal ;
let pdf = ( x , t : t ) = > Jstat . lognormal # # pdf ( x , t . mu , t . sigma ) ;
let inv = ( p , t : t ) = > Jstat . lognormal # # inv ( p , t . mu , t . sigma ) ;
let sample = ( t : t ) = > Jstat . lognormal # # sample ( t . mu , t . sigma ) ;
let toString = ( { mu , sigma } : t ) = > { j | Lognormal ( $ mu , $ sigma ) | j } ;
2020-04-01 13:52:13 +00:00
let contType : contType = ` Continuous ;
2020-03-24 17:48:46 +00:00
let from90PercentCI = ( low , high ) = > {
let logLow = Js . Math . log ( low ) ;
let logHigh = Js . Math . log ( high ) ;
2020-03-26 23:18:19 +00:00
let mu = E . A . Floats . mean ( [| logLow , logHigh |] ) ;
2020-03-24 17:48:46 +00:00
let sigma = ( logHigh -. logLow ) /. ( 2 . 0 * . 1 . 645 ) ;
` Lognormal ( { mu , sigma } ) ;
} ;
let fromMeanAndStdev = ( mean , stdev ) = > {
let variance = Js . Math . pow_float ( ~ base = stdev , ~ exp = 2 . 0 ) ;
let meanSquared = Js . Math . pow_float ( ~ base = mean , ~ exp = 2 . 0 ) ;
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 ,
) ;
` Lognormal ( { mu , sigma } ) ;
} ;
} ;
module Uniform = {
type t = uniform ;
let pdf = ( x , t : t ) = > Jstat . uniform # # pdf ( x , t . low , t . high ) ;
let inv = ( p , t : t ) = > Jstat . uniform # # inv ( p , t . low , t . high ) ;
let sample = ( t : t ) = > Jstat . uniform # # sample ( t . low , t . high ) ;
let toString = ( { low , high } : t ) = > { j | Uniform ( $ low , $ high ) | j } ;
2020-04-01 13:52:13 +00:00
let contType : contType = ` Continuous ;
} ;
module Float = {
type t = float ;
let pdf = ( x , t : t ) = > x = = t ? 1 . 0 : 0 . 0 ;
let inv = ( p , t : t ) = > p < t ? 0 . 0 : 1 . 0 ;
let sample = ( t : t ) = > t ;
let toString = Js . Float . toString ;
let contType : contType = ` Discrete ;
2020-03-24 17:48:46 +00:00
} ;
module GenericSimple = {
let minCdfValue = 0 . 0001 ;
let maxCdfValue = 0 . 9999 ;
let pdf = ( x , dist ) = >
switch ( dist ) {
| ` Normal ( n ) = > Normal . pdf ( x , n )
2020-03-26 16:01:52 +00:00
| ` Triangular ( n ) = > Triangular . pdf ( x , n )
| ` Exponential ( n ) = > Exponential . pdf ( x , n )
| ` Cauchy ( n ) = > Cauchy . pdf ( x , n )
2020-03-24 17:48:46 +00:00
| ` Lognormal ( n ) = > Lognormal . pdf ( x , n )
| ` Uniform ( n ) = > Uniform . pdf ( x , n )
| ` Beta ( n ) = > Beta . pdf ( x , n )
2020-04-01 13:52:13 +00:00
| ` Float ( n ) = > Float . pdf ( x , n )
2020-04-30 10:18:33 +00:00
| ` ContinuousShape ( n ) = > ContinuousShape . pdf ( x , n )
2020-04-01 13:52:13 +00:00
} ;
2020-04-30 10:18:33 +00:00
let contType = ( dist : dist ) : contType = >
2020-04-01 13:52:13 +00:00
switch ( dist ) {
| ` Normal ( _ ) = > Normal . contType
| ` Triangular ( _ ) = > Triangular . contType
| ` Exponential ( _ ) = > Exponential . contType
| ` Cauchy ( _ ) = > Cauchy . contType
| ` Lognormal ( _ ) = > Lognormal . contType
| ` Uniform ( _ ) = > Uniform . contType
| ` Beta ( _ ) = > Beta . contType
| ` Float ( _ ) = > Float . contType
2020-04-11 13:22:13 +00:00
| ` ContinuousShape ( _ ) = > ContinuousShape . contType
2020-03-24 17:48:46 +00:00
} ;
let inv = ( x , dist ) = >
switch ( dist ) {
| ` Normal ( n ) = > Normal . inv ( x , n )
2020-03-26 16:01:52 +00:00
| ` Triangular ( n ) = > Triangular . inv ( x , n )
| ` Exponential ( n ) = > Exponential . inv ( x , n )
| ` Cauchy ( n ) = > Cauchy . inv ( x , n )
2020-03-24 17:48:46 +00:00
| ` Lognormal ( n ) = > Lognormal . inv ( x , n )
| ` Uniform ( n ) = > Uniform . inv ( x , n )
| ` Beta ( n ) = > Beta . inv ( x , n )
2020-04-01 13:52:13 +00:00
| ` Float ( n ) = > Float . inv ( x , n )
2020-04-30 10:18:33 +00:00
| ` ContinuousShape ( n ) = > ContinuousShape . inv ( x , n )
2020-03-24 17:48:46 +00:00
} ;
2020-03-26 16:01:52 +00:00
let sample : dist = > float =
fun
2020-03-24 17:48:46 +00:00
| ` Normal ( n ) = > Normal . sample ( n )
2020-03-26 16:01:52 +00:00
| ` Triangular ( n ) = > Triangular . sample ( n )
| ` Exponential ( n ) = > Exponential . sample ( n )
| ` Cauchy ( n ) = > Cauchy . sample ( n )
2020-03-24 17:48:46 +00:00
| ` Lognormal ( n ) = > Lognormal . sample ( n )
| ` Uniform ( n ) = > Uniform . sample ( n )
2020-04-01 13:52:13 +00:00
| ` Beta ( n ) = > Beta . sample ( n )
2020-04-11 13:22:13 +00:00
| ` Float ( n ) = > Float . sample ( n )
2020-04-30 10:18:33 +00:00
| ` ContinuousShape ( n ) = > ContinuousShape . sample ( n ) ;
2020-03-24 17:48:46 +00:00
2020-03-26 16:01:52 +00:00
let toString : dist = > string =
fun
| ` Triangular ( n ) = > Triangular . toString ( n )
| ` Exponential ( n ) = > Exponential . toString ( n )
| ` Cauchy ( n ) = > Cauchy . toString ( n )
2020-03-24 17:48:46 +00:00
| ` Normal ( n ) = > Normal . toString ( n )
| ` Lognormal ( n ) = > Lognormal . toString ( n )
| ` Uniform ( n ) = > Uniform . toString ( n )
2020-04-01 13:52:13 +00:00
| ` Beta ( n ) = > Beta . toString ( n )
2020-04-11 13:22:13 +00:00
| ` Float ( n ) = > Float . toString ( n )
2020-04-30 10:18:33 +00:00
| ` ContinuousShape ( n ) = > ContinuousShape . toString ( n ) ;
2020-03-24 17:48:46 +00:00
2020-03-26 16:01:52 +00:00
let min : dist = > float =
fun
| ` Triangular ( { low } ) = > low
| ` Exponential ( n ) = > Exponential . inv ( minCdfValue , n )
| ` Cauchy ( n ) = > Cauchy . inv ( minCdfValue , n )
2020-03-24 17:48:46 +00:00
| ` Normal ( n ) = > Normal . inv ( minCdfValue , n )
| ` Lognormal ( n ) = > Lognormal . inv ( minCdfValue , n )
| ` Uniform ( { low } ) = > low
2020-04-01 13:52:13 +00:00
| ` Beta ( n ) = > Beta . inv ( minCdfValue , n )
2020-04-30 10:18:33 +00:00
| ` ContinuousShape ( n ) = > ContinuousShape . inv ( minCdfValue , n )
2020-04-01 13:52:13 +00:00
| ` Float ( n ) = > n ;
2020-03-24 17:48:46 +00:00
2020-03-26 16:01:52 +00:00
let max : dist = > float =
fun
| ` Triangular ( n ) = > n . high
| ` Exponential ( n ) = > Exponential . inv ( maxCdfValue , n )
| ` Cauchy ( n ) = > Cauchy . inv ( maxCdfValue , n )
2020-03-24 17:48:46 +00:00
| ` Normal ( n ) = > Normal . inv ( maxCdfValue , n )
| ` Lognormal ( n ) = > Lognormal . inv ( maxCdfValue , n )
| ` Beta ( n ) = > Beta . inv ( maxCdfValue , n )
2020-04-30 10:18:33 +00:00
| ` ContinuousShape ( n ) = > ContinuousShape . inv ( maxCdfValue , n )
2020-04-01 13:52:13 +00:00
| ` Uniform ( { high } ) = > high
| ` Float ( n ) = > n ;
2020-03-24 17:48:46 +00:00
2020-06-02 22:08:41 +00:00
/* This function returns a list of x's at which to evaluate the overall distribution (for rendering).
2020-06-10 04:28:03 +00:00
This function is called separately for each individual distribution .
2020-06-02 22:08:41 +00:00
2020-06-10 04:28:03 +00:00
When called with xSelection = ` Linear , this function will return ( sampleCount ) x's , evenly
distributed between the min and max of the distribution ( whatever those are defined to be above ) .
2020-06-02 22:08:41 +00:00
2020-06-10 04:28:03 +00:00
When called with xSelection = ` ByWeight , this function will distribute the x's such as to
match the cumulative shape of the distribution . This is slower but may give better results .
* /
2020-03-25 15:12:39 +00:00
let interpolateXs =
( ~ xSelection : [ | ` Linear | ` ByWeight ] = ` Linear , dist : dist , sampleCount ) = > {
2020-06-03 16:24:55 +00:00
switch ( xSelection , dist ) {
| ( ` Linear , _ ) = > E . A . Floats . range ( min ( dist ) , max ( dist ) , sampleCount )
| ( ` ByWeight , ` Uniform ( n ) ) = >
2020-06-02 22:08:41 +00:00
// In `ByWeight mode, uniform distributions get special treatment because we need two x's
// on either side for proper rendering (just left and right of the discontinuities).
2020-06-03 16:24:55 +00:00
let dx = 0 . 00001 * . ( n . high -. n . low ) ;
2020-06-10 04:28:03 +00:00
[| n . low -. dx , n . low + . dx , n . high -. dx , n . high + . dx |] ;
| ( ` ByWeight , _ ) = >
let ys = E . A . Floats . range ( minCdfValue , maxCdfValue , sampleCount ) ;
ys | > E . A . fmap ( y = > inv ( y , dist ) ) ;
2020-03-25 15:12:39 +00:00
} ;
} ;
2020-03-24 17:48:46 +00:00
let toShape =
2020-04-01 13:52:13 +00:00
( ~ xSelection : [ | ` Linear | ` ByWeight ] = ` Linear , dist : dist , sampleCount )
: DistTypes . shape = > {
2020-04-30 10:18:33 +00:00
switch ( dist ) {
| ` ContinuousShape ( n ) = > n . pdf | > Distributions . Continuous . T . toShape
| dist = >
let xs = interpolateXs ( ~ xSelection , dist , sampleCount ) ;
let ys = xs | > E . A . fmap ( r = > pdf ( r , dist ) ) ;
XYShape . T . fromArrays ( xs , ys )
| > Distributions . Continuous . make ( ` Linear , _ )
| > Distributions . Continuous . T . toShape ;
} ;
2020-03-24 17:48:46 +00:00
} ;
} ;
2020-06-13 06:30:51 +00:00
module DistTree = {
type nodeResult = [
| ` Distribution ( dist )
// RenderedShape: continuous xyShape, discrete xyShape, total value.
| ` RenderedShape ( DistTypes . continuousShape , DistTypes . discreteShape , integral )
] ;
2020-03-24 17:48:46 +00:00
2020-06-13 06:30:51 +00:00
let evaluateDistribution = ( d : dist ) : nodeResult = > {
// certain distributions we may want to evaluate to RenderedShapes right away, e.g. discrete
` Distribution ( d )
2020-03-24 17:48:46 +00:00
} ;
2020-06-13 06:30:51 +00:00
// This is a performance bottleneck!
// Using raw JS here so we can use native for loops and access array elements
// directly, without option checks.
let jsCombinationConvolve : ( array ( float ) , array ( float ) , array ( float ) , array ( float ) , float = > float = > float ) = > ( array ( float ) , array ( float ) ) = [ % bs . raw
{ |
function ( s1xs , s1ys , s2xs , s2ys , func ) {
const r = new Map () ;
// To convolve, add the xs and multiply the ys:
for ( let i = 0 ; i < s1xs . length ; i + + ) {
for ( let j = 0 ; j < s2xs . length ; j + + ) {
const x = func ( s1xs [ i ] , s2xs [ j ] ) ;
const cv = r . get ( x ) | 0 ;
r . set ( x , cv + s1ys [ i ] * s2ys [ j ] ) ; // add up the ys, if same x
}
}
const rxys = [ ... r . entries () ] ;
rxys . sort ( ( [ x1 , y1 ] , [ x2 , y2 ] ) = > x1 - x2 ) ;
const rxs = new Array ( rxys . length ) ;
const rys = new Array ( rxys . length ) ;
for ( let i = 0 ; i < rxys . length ; i + + ) {
rxs [ i ] = rxys [ i ] [ 0 ] ;
rys [ i ] = rxys [ i ] [ 1 ] ;
}
return [ rxs , rys ] ;
}
| } ] ;
let funcFromOp = ( op : operation ) = > {
switch ( op ) {
| ` AddOperation = > ( + . )
| ` SubtractOperation = > ( -. )
| ` MultiplyOperation = > ( * . )
| ` DivideOperation = > ( /. )
| ` ExponentiateOperation = > ( * * )
}
}
let renderDistributionToXYShape = ( d : dist , sampleCount : int ) : ( DistTypes . continuousShape , DistTypes . discreteShape ) = > {
// render the distribution into an XY shape
switch ( d ) {
| ` Float ( v ) = > ( Distributions . Continuous . empty , { xs : [| v |] , ys : [| 1 . 0 |] } )
| _ = > {
let xs = GenericSimple . interpolateXs ( ~ xSelection = ` ByWeight , d , sampleCount ) ;
let ys = xs | > E . A . fmap ( x = > GenericSimple . pdf ( x , d ) ) ;
( Distributions . Continuous . make ( ` Linear , { xs : xs , ys : ys } ) , XYShape . T . empty )
}
}
2020-04-30 10:18:33 +00:00
} ;
2020-06-13 06:30:51 +00:00
let combinationDistributionOfXYShapes = ( sc1 : DistTypes . continuousShape , // continuous shape
sd1 : DistTypes . discreteShape , // discrete shape
sc2 : DistTypes . continuousShape ,
sd2 : DistTypes . discreteShape , func ) : ( DistTypes . continuousShape , DistTypes . discreteShape ) = > {
2020-06-10 04:28:03 +00:00
2020-06-13 06:30:51 +00:00
let ( ccxs , ccys ) = jsCombinationConvolve ( sc1 . xyShape . xs , sc1 . xyShape . ys , sc2 . xyShape . xs , sc2 . xyShape . ys , func ) ;
let ( dcxs , dcys ) = jsCombinationConvolve ( sd1 . xs , sd1 . ys , sc2 . xyShape . xs , sc2 . xyShape . ys , func ) ;
let ( cdxs , cdys ) = jsCombinationConvolve ( sc1 . xyShape . xs , sc1 . xyShape . ys , sd2 . xs , sd2 . ys , func ) ;
let ( ddxs , ddys ) = jsCombinationConvolve ( sd1 . xs , sd1 . ys , sd2 . xs , sd2 . ys , func ) ;
2020-06-10 04:28:03 +00:00
2020-06-13 06:30:51 +00:00
let ccxy = Distributions . Continuous . make ( ` Linear , { xs : ccxs , ys : ccys } ) ;
let dcxy = Distributions . Continuous . make ( ` Linear , { xs : dcxs , ys : dcys } ) ;
let cdxy = Distributions . Continuous . make ( ` Linear , { xs : cdxs , ys : cdys } ) ;
// the continuous parts are added up; only the discrete-discrete sum is discrete
let continuousShapeSum = Distributions . Continuous . reduce ( ( + . ) , [| ccxy , dcxy , cdxy |] ) ;
2020-06-10 04:28:03 +00:00
2020-06-13 06:30:51 +00:00
let ddxy : DistTypes . discreteShape = { xs : cdxs , ys : cdys } ;
2020-06-10 04:28:03 +00:00
2020-06-13 06:30:51 +00:00
( continuousShapeSum , ddxy )
} ;
2020-06-10 04:28:03 +00:00
2020-06-13 06:30:51 +00:00
let evaluateCombinationDistribution = ( et1 : nodeResult , et2 : nodeResult , op : operation , sampleCount : int ) = > {
/* return either a Distribution or a RenderedShape. Must integrate to 1. */
let func = funcFromOp ( op ) ;
switch ( ( et1 , et2 , op ) ) {
/* Known cases: replace symbolic with symbolic distribution */
| ( ` Distribution ( ` Float ( v1 ) ) , ` Distribution ( ` Float ( v2 ) ) , _ ) = > {
` Distribution ( ` Float ( func ( v1 , v2 ) ) )
}
2020-06-13 06:53:01 +00:00
| ( ` Distribution ( ` Normal ( n2 ) ) , ` Distribution ( ` Float ( v1 ) ) , ` AddOperation )
2020-06-13 06:30:51 +00:00
| ( ` Distribution ( ` Float ( v1 ) ) , ` Distribution ( ` Normal ( n2 ) ) , ` AddOperation ) = > {
let n : normal = { mean : v1 + . n2 . mean , stdev : n2 . stdev } ;
` Distribution ( ` Normal ( n ) )
}
| ( ` Distribution ( ` Normal ( n1 ) ) , ` Distribution ( ` Normal ( n2 ) ) , ` AddOperation ) = > {
let n : normal = { mean : n1 . mean + . n2 . mean , stdev : sqrt ( n1 . stdev * * 2 . + . n2 . stdev * * 2 . ) } ;
` Distribution ( ` Normal ( n ) ) ;
}
2020-06-13 06:53:01 +00:00
| ( ` Distribution ( ` Normal ( n1 ) ) , ` Distribution ( ` Normal ( n2 ) ) , ` SubtractOperation ) = > {
let n : normal = { mean : n1 . mean -. n2 . mean , stdev : sqrt ( n1 . stdev * * 2 . + . n2 . stdev * * 2 . ) } ;
` Distribution ( ` Normal ( n ) ) ;
}
| ( ` Distribution ( ` Lognormal ( l1 ) ) , ` Distribution ( ` Lognormal ( l2 ) ) , ` MultiplyOperation ) = > {
let l : lognormal = { mu : l1 . mu + . l2 . mu , sigma : l1 . sigma + . l2 . sigma } ;
` Distribution ( ` Lognormal ( l ) ) ;
}
| ( ` Distribution ( ` Lognormal ( l1 ) ) , ` Distribution ( ` Lognormal ( l2 ) ) , ` DivideOperation ) = > {
let l : lognormal = { mu : l1 . mu -. l2 . mu , sigma : l1 . sigma + . l2 . sigma } ;
` Distribution ( ` Lognormal ( l ) ) ;
}
2020-06-13 06:30:51 +00:00
/* General cases: convolve the XYShapes */
| ( ` Distribution ( d1 ) , ` Distribution ( d2 ) , _ ) = > {
let ( sc1 , sd1 ) = renderDistributionToXYShape ( d1 , sampleCount ) ;
let ( sc2 , sd2 ) = renderDistributionToXYShape ( d2 , sampleCount ) ;
let ( sc , sd ) = combinationDistributionOfXYShapes ( sc1 , sd1 , sc2 , sd2 , func ) ;
` RenderedShape ( sc , sd , 1 . 0 )
}
| ( ` Distribution ( d1 ) , ` RenderedShape ( sc2 , sd2 , i2 ) , _ ) = > {
let ( sc1 , sd1 ) = renderDistributionToXYShape ( d1 , sampleCount ) ;
let ( sc , sd ) = combinationDistributionOfXYShapes ( sc1 , sd1 , sc2 , sd2 , func ) ;
` RenderedShape ( sc , sd , i2 )
}
| ( ` RenderedShape ( sc1 , sd1 , i1 ) , ` Distribution ( d2 ) , _ ) = > {
let ( sc2 , sd2 ) = renderDistributionToXYShape ( d2 , sampleCount ) ;
let ( sc , sd ) = combinationDistributionOfXYShapes ( sc1 , sd1 , sc2 , sd2 , func ) ;
` RenderedShape ( sc , sd , i1 ) ;
}
| ( ` RenderedShape ( sc1 , sd1 , i1 ) , ` RenderedShape ( sc2 , sd2 , i2 ) , _ ) = > {
// sum of two multimodals that have a continuous and discrete each.
let ( sc , sd ) = combinationDistributionOfXYShapes ( sc1 , sd1 , sc2 , sd2 , func ) ;
` RenderedShape ( sc , sd , i1 ) ;
}
}
} ;
2020-06-10 04:28:03 +00:00
2020-06-13 06:30:51 +00:00
let evaluatePointwiseSum = ( et1 : nodeResult , et2 : nodeResult , sampleCount : int ) = > {
switch ( ( et1 , et2 ) ) {
/* Known cases: */
| ( ` Distribution ( ` Float ( v1 ) ) , ` Distribution ( ` Float ( v2 ) ) ) = > {
v1 = = v2
? ` RenderedShape ( Distributions . Continuous . empty , Distributions . Discrete . make ( { xs : [| v1 |] , ys : [| 2 . |] } ) , 2 . )
: ` RenderedShape ( Distributions . Continuous . empty , Distributions . Discrete . empty , 0 . ) // TODO: add warning: shouldn't pointwise add scalars.
}
| ( ` Distribution ( ` Float ( v1 ) ) , ` Distribution ( d2 ) ) = > {
let sd1 : DistTypes . xyShape = { xs : [| v1 |] , ys : [| 1 . |] } ;
let ( sc2 , sd2 ) = renderDistributionToXYShape ( d2 , sampleCount ) ;
` RenderedShape ( sc2 , Distributions . Discrete . reduce ( ( + . ) , [| sd1 , sd2 |] ) , 2 . )
}
| ( ` Distribution ( d1 ) , ` Distribution ( ` Float ( v2 ) ) ) = > {
let ( sc1 , sd1 ) = renderDistributionToXYShape ( d1 , sampleCount ) ;
let sd2 : DistTypes . xyShape = { xs : [| v2 |] , ys : [| 1 . |] } ;
` RenderedShape ( sc1 , Distributions . Discrete . reduce ( ( + . ) , [| sd1 , sd2 |] ) , 2 . )
}
| ( ` Distribution ( d1 ) , ` Distribution ( d2 ) ) = > {
let ( sc1 , sd1 ) = renderDistributionToXYShape ( d1 , sampleCount ) ;
let ( sc2 , sd2 ) = renderDistributionToXYShape ( d2 , sampleCount ) ;
` RenderedShape ( Distributions . Continuous . reduce ( ( + . ) , [| sc1 , sc2 |] ) , Distributions . Discrete . reduce ( ( + . ) , [| sd1 , sd2 |] ) , 2 . )
}
| ( ` Distribution ( d1 ) , ` RenderedShape ( sc2 , sd2 , i2 ) )
| ( ` RenderedShape ( sc2 , sd2 , i2 ) , ` Distribution ( d1 ) ) = > {
let ( sc1 , sd1 ) = renderDistributionToXYShape ( d1 , sampleCount ) ;
` RenderedShape ( Distributions . Continuous . reduce ( ( + . ) , [| sc1 , sc2 |] ) , Distributions . Discrete . reduce ( ( + . ) , [| sd1 , sd2 |] ) , 1 . + . i2 )
}
| ( ` RenderedShape ( sc1 , sd1 , i1 ) , ` RenderedShape ( sc2 , sd2 , i2 ) ) = > {
Js . log3 ( " Reducing continuous rr " , sc1 , sc2 ) ;
Js . log2 ( " Continuous reduction: " , Distributions . Continuous . reduce ( ( + . ) , [| sc1 , sc2 |] ) ) ;
Js . log2 ( " Discrete reduction: " , Distributions . Discrete . reduce ( ( + . ) , [| sd1 , sd2 |] ) ) ;
` RenderedShape ( Distributions . Continuous . reduce ( ( + . ) , [| sc1 , sc2 |] ) , Distributions . Discrete . reduce ( ( + . ) , [| sd1 , sd2 |] ) , i1 + . i2 )
}
}
} ;
let evaluatePointwiseProduct = ( et1 : nodeResult , et2 : nodeResult , sampleCount : int ) = > {
switch ( ( et1 , et2 ) ) {
/* Known cases: */
| ( ` Distribution ( ` Float ( v1 ) ) , ` Distribution ( ` Float ( v2 ) ) ) = > {
v1 = = v2
? ` RenderedShape ( Distributions . Continuous . empty , Distributions . Discrete . make ( { xs : [| v1 |] , ys : [| 1 . |] } ) , 1 . )
: ` RenderedShape ( Distributions . Continuous . empty , Distributions . Discrete . empty , 0 . ) // TODO: add warning: shouldn't pointwise multiply scalars.
}
| ( ` Distribution ( ` Float ( v1 ) ) , ` Distribution ( d2 ) ) = > {
// evaluate d2 at v1
let y = GenericSimple . pdf ( v1 , d2 ) ;
` RenderedShape ( Distributions . Continuous . empty , Distributions . Discrete . make ( { xs : [| v1 |] , ys : [| y |] } ) , y )
}
| ( ` Distribution ( d1 ) , ` Distribution ( ` Float ( v2 ) ) ) = > {
// evaluate d1 at v2
let y = GenericSimple . pdf ( v2 , d1 ) ;
` RenderedShape ( Distributions . Continuous . empty , Distributions . Discrete . make ( { xs : [| v2 |] , ys : [| y |] } ) , y )
}
| ( ` Distribution ( ` Normal ( n1 ) ) , ` Distribution ( ` Normal ( n2 ) ) ) = > {
let mean = ( 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 integral = 0 ; // TODO
` RenderedShape ( Distributions . Continuous . empty , Distributions . Discrete . empty , 0 . )
}
/* General cases */
| ( ` Distribution ( d1 ) , ` Distribution ( d2 ) ) = > {
// NOT IMPLEMENTED YET
// TODO: evaluate integral properly
let ( sc1 , sd1 ) = renderDistributionToXYShape ( d1 , sampleCount ) ;
let ( sc2 , sd2 ) = renderDistributionToXYShape ( d2 , sampleCount ) ;
` RenderedShape ( Distributions . Continuous . empty , Distributions . Discrete . empty , 0 . )
}
| ( ` Distribution ( d1 ) , ` RenderedShape ( sc2 , sd2 , i2 ) ) = > {
// NOT IMPLEMENTED YET
// TODO: evaluate integral properly
let ( sc1 , sd1 ) = renderDistributionToXYShape ( d1 , sampleCount ) ;
` RenderedShape ( Distributions . Continuous . empty , Distributions . Discrete . empty , 0 . )
}
| ( ` RenderedShape ( sc1 , sd1 , i1 ) , ` Distribution ( d1 ) ) = > {
// NOT IMPLEMENTED YET
// TODO: evaluate integral properly
let ( sc2 , sd2 ) = renderDistributionToXYShape ( d1 , sampleCount ) ;
` RenderedShape ( Distributions . Continuous . empty , Distributions . Discrete . empty , 0 . )
}
| ( ` RenderedShape ( sc1 , sd1 , i1 ) , ` RenderedShape ( sc2 , sd2 , i2 ) ) = > {
` RenderedShape ( Distributions . Continuous . empty , Distributions . Discrete . empty , 0 . )
}
}
} ;
2020-06-10 04:28:03 +00:00
2020-06-13 06:30:51 +00:00
let evaluateNormalize = ( et : nodeResult , sampleCount : int ) = > {
// just divide everything by the integral.
switch ( et ) {
2020-06-13 06:53:01 +00:00
| ` RenderedShape ( sc , sd , 0 . ) = > {
` RenderedShape ( Distributions . Continuous . empty , Distributions . Discrete . empty , 0 . )
}
2020-06-13 06:30:51 +00:00
| ` RenderedShape ( sc , sd , i ) = > {
// loop through all ys and divide them by i
let normalize = ( s : DistTypes . xyShape ) : DistTypes . xyShape = > { xs : s . xs , ys : s . ys | > E . A . fmap ( y = > y /. i ) } ;
let scn = sc | > Distributions . Continuous . shapeMap ( normalize ) ;
let sdn = sd | > normalize ;
` RenderedShape ( scn , sdn , 1 . )
}
| ` Distribution ( d ) = > ` Distribution ( d ) // any kind of atomic dist should already be normalized -- TODO: THIS IS ACTUALLY FALSE! E.g. pointwise product of normal * normal
}
} ;
let evaluateTruncate = ( et : nodeResult , xc : cutoffX , compareFunc : ( float , float ) = > bool , sampleCount : int ) = > {
let cut = ( s : DistTypes . xyShape ) : DistTypes . xyShape = > {
let ( xs , ys ) = s . ys
| > Belt . Array . zip ( s . xs )
| > E . A . filter ( ( ( x , y ) ) = > compareFunc ( x , xc ) )
| > Belt . Array . unzip
let cutShape : DistTypes . xyShape = { xs , ys } ;
cutShape ;
} ;
switch ( et ) {
| ` Distribution ( d ) = > {
let ( sc , sd ) = renderDistributionToXYShape ( d , sampleCount ) ;
let scc = sc | > Distributions . Continuous . shapeMap ( cut ) ;
let sdc = sd | > cut ;
let newIntegral = 1 . ; // TODO
` RenderedShape ( scc , sdc , newIntegral ) ;
}
| ` RenderedShape ( sc , sd , i ) = > {
let scc = sc | > Distributions . Continuous . shapeMap ( cut ) ;
let sdc = sd | > cut ;
let newIntegral = 1 . ; // TODO
` RenderedShape ( scc , sdc , newIntegral ) ;
}
}
2020-04-30 10:18:33 +00:00
} ;
2020-04-01 13:52:13 +00:00
2020-06-13 06:30:51 +00:00
let evaluateVerticalScaling = ( et1 : nodeResult , et2 : nodeResult , sampleCount : int ) = > {
let scale = ( i : float , s : DistTypes . xyShape ) : DistTypes . xyShape = > { xs : s . xs , ys : s . ys | > E . A . fmap ( y = > y * . i ) } ;
switch ( ( et1 , et2 ) ) {
| ( ` Distribution ( ` Float ( v ) ) , ` Distribution ( d ) )
| ( ` Distribution ( d ) , ` Distribution ( ` Float ( v ) ) ) = > {
let ( sc , sd ) = renderDistributionToXYShape ( d , sampleCount ) ;
let scc = sc | > Distributions . Continuous . shapeMap ( scale ( v ) ) ;
let sdc = sd | > scale ( v ) ;
2020-06-10 04:28:03 +00:00
2020-06-13 06:30:51 +00:00
let newIntegral = v ; // TODO
` RenderedShape ( scc , sdc , newIntegral ) ;
}
| ( ` Distribution ( ` Float ( v ) ) , ` RenderedShape ( sc , sd , i ) )
| ( ` RenderedShape ( sc , sd , i ) , ` Distribution ( ` Float ( v ) ) ) = > {
let scc = sc | > Distributions . Continuous . shapeMap ( scale ( v ) ) ;
let sdc = sd | > scale ( v ) ;
let newIntegral = v ; // TODO
` RenderedShape ( scc , sdc , newIntegral ) ;
}
| _ = > ` RenderedShape ( Distributions . Continuous . empty , Distributions . Discrete . empty , 0 . ) // TODO: give warning
}
}
let renderNode = ( et : nodeResult , sampleCount : int ) = > {
switch ( et ) {
| ` Distribution ( d ) = > {
let ( sc , sd ) = renderDistributionToXYShape ( d , sampleCount ) ;
` RenderedShape ( sc , sd , 1 . 0 ) ;
}
| s = > s
}
}
let rec evaluateNode = ( treeNode : distTree , sampleCount : int ) : nodeResult = > {
// returns either a new symbolic distribution
switch ( treeNode ) {
| ` Distribution ( d ) = > evaluateDistribution ( d )
| ` Combination ( t1 , t2 , op ) = > evaluateCombinationDistribution ( evaluateNode ( t1 , sampleCount ) , evaluateNode ( t2 , sampleCount ) , op , sampleCount )
| ` PointwiseSum ( t1 , t2 ) = > evaluatePointwiseSum ( evaluateNode ( t1 , sampleCount ) , evaluateNode ( t2 , sampleCount ) , sampleCount )
| ` PointwiseProduct ( t1 , t2 ) = > evaluatePointwiseProduct ( evaluateNode ( t1 , sampleCount ) , evaluateNode ( t2 , sampleCount ) , sampleCount )
| ` VerticalScaling ( t1 , t2 ) = > evaluateVerticalScaling ( evaluateNode ( t1 , sampleCount ) , evaluateNode ( t2 , sampleCount ) , sampleCount )
| ` Normalize ( t ) = > evaluateNormalize ( evaluateNode ( t , sampleCount ) , sampleCount )
| ` LeftTruncate ( t , x ) = > evaluateTruncate ( evaluateNode ( t , sampleCount ) , x , ( < = ) , sampleCount )
| ` RightTruncate ( t , x ) = > evaluateTruncate ( evaluateNode ( t , sampleCount ) , x , ( > = ) , sampleCount )
| ` Render ( t ) = > renderNode ( evaluateNode ( t , sampleCount ) , sampleCount )
}
} ;
let toShape = ( treeNode : distTree , sampleCount : int ) = > {
/* let continuousXs = findContinuousXs(dists, sampleCount);
2020-06-10 04:28:03 +00:00
continuousXs | > Array . fast_sort ( compare ) ;
let ( contShapes , distShapes ) = accumulateContAndDiscShapes ( dists , continuousXs , 1 . 0 ) ;
let combinedContinuous = contShapes
| > E . A . fold_left ( ( shapeAcc : DistTypes . xyShape , shape : DistTypes . xyShape ) = > {
let ys = E . A . fmapi ( ( i , y ) = > y + . shape . ys [ i ] , shapeAcc . ys ) ;
{ xs : continuousXs , ys : ys }
} , { xs : continuousXs , ys : Array . make ( Array . length ( continuousXs ) , 0 . 0 ) } )
| > Distributions . Continuous . make ( ` Linear ) ;
2020-06-13 06:30:51 +00:00
let combinedDiscrete = Distributions . Discrete . reduce ( ( + . ) , distShapes ) * /
2020-06-10 04:28:03 +00:00
2020-06-13 06:30:51 +00:00
let treeShape = evaluateNode ( ` Render ( ` Normalize ( treeNode ) ) , sampleCount ) ;
switch ( treeShape ) {
| ` Distribution ( _ ) = > E . O . toExn ( " No shape found! " , None )
| ` RenderedShape ( sc , sd , _ ) = > {
let shape = MixedShapeBuilder . buildSimple ( ~ continuous = Some ( sc ) , ~ discrete = sd ) ;
2020-06-10 04:28:03 +00:00
2020-06-13 06:30:51 +00:00
shape | > E . O . toExt ( " " ) ;
}
}
2020-03-24 17:48:46 +00:00
} ;
2020-06-13 06:30:51 +00:00
let rec toString = ( treeNode : distTree ) : string = > {
let stringFromOp = op = > switch ( op ) {
| ` AddOperation = > " + "
| ` SubtractOperation = > " - "
| ` MultiplyOperation = > " * "
| ` DivideOperation = > " / "
| ` ExponentiateOperation = > " ^ "
} ;
switch ( treeNode ) {
| ` Distribution ( d ) = > GenericSimple . toString ( d )
| ` Combination ( t1 , t2 , op ) = > toString ( t1 ) + + stringFromOp ( op ) + + toString ( t2 )
| ` PointwiseSum ( t1 , t2 ) = > toString ( t1 ) + + " .+ " + + toString ( t2 )
| ` PointwiseProduct ( t1 , t2 ) = > toString ( t1 ) + + " .* " + + toString ( t2 )
| ` VerticalScaling ( t1 , t2 ) = > toString ( t1 ) + + " @ " + + toString ( t2 )
| ` Normalize ( t ) = > " normalize( " + + toString ( t ) + + " ) "
| ` LeftTruncate ( t , x ) = > " leftTruncate( " + + toString ( t ) + + " , " + + string_of_float ( x ) + + " ) "
| ` RightTruncate ( t , x ) = > " rightTruncate( " + + toString ( t ) + + " , " + + string_of_float ( x ) + + " ) "
}
2020-03-24 17:48:46 +00:00
} ;
} ;
2020-06-13 06:30:51 +00:00
let toString = ( treeNode : distTree ) = > DistTree . toString ( treeNode )
2020-06-10 04:28:03 +00:00
2020-06-13 06:30:51 +00:00
let toShape = ( sampleCount : int , treeNode : distTree ) = >
DistTree . toShape ( treeNode , sampleCount ) //~xSelection=`ByWeight,