├── .circleci └── config.yml ├── .github └── workflows │ └── d.yml ├── .gitignore ├── LICENSE ├── README.md ├── dub.sdl ├── index.d ├── meson.build ├── source └── mir │ ├── math │ └── internal │ │ ├── benchmark.d │ │ ├── fp_powi.d │ │ ├── linearAlgebra.d │ │ ├── log1p.d │ │ ├── log_beta.d │ │ ├── log_binomial.d │ │ ├── powi.d │ │ └── xlogy.d │ └── stat │ ├── constant.d │ ├── descriptive │ ├── aliases.d │ ├── multivariate.d │ ├── package.d │ ├── univariate.d │ └── weighted.d │ ├── distribution │ ├── bernoulli.d │ ├── beta.d │ ├── beta_proportion.d │ ├── binomial.d │ ├── categorical.d │ ├── cauchy.d │ ├── cdf.d │ ├── chi2.d │ ├── cornish_fisher.d │ ├── exponential.d │ ├── f.d │ ├── gamma.d │ ├── generalized_pareto.d │ ├── geometric.d │ ├── gev.d │ ├── hypergeometric.d │ ├── invcdf.d │ ├── laplace.d │ ├── log_normal.d │ ├── logistic.d │ ├── negative_binomial.d │ ├── normal.d │ ├── package.d │ ├── pareto.d │ ├── pdf.d │ ├── poisson.d │ ├── rayleigh.d │ ├── students_t.d │ ├── uniform.d │ ├── uniform_discrete.d │ └── weibull.d │ ├── inference.d │ ├── package.d │ └── transform.d └── subprojects ├── cblas-d.wrap ├── lapack-d.wrap ├── mir-algorithm.wrap ├── mir-blas.wrap ├── mir-core.wrap ├── mir-lapack.wrap ├── mir-linux-kernel.wrap └── mir-random.wrap /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | orbs: 4 | mirci: libmir/upload_docs@0.1.4 5 | 6 | workflows: 7 | version: 2 8 | build-deploy: 9 | jobs: 10 | - mirci/test_and_build_docs: 11 | filters: 12 | tags: 13 | only: /^v(\d)+(\.(\d)+)+$/ 14 | - mirci/upload_docs: 15 | to: mir-stat.libmir.org 16 | requires: 17 | - mirci/test_and_build_docs 18 | filters: 19 | branches: 20 | ignore: /.*/ 21 | tags: 22 | only: /^v(\d)+(\.(\d)+)+$/ 23 | -------------------------------------------------------------------------------- /.github/workflows/d.yml: -------------------------------------------------------------------------------- 1 | name: Run all D Tests and Codecov 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | test: 11 | name: Dub Tests 12 | strategy: 13 | matrix: 14 | os: [ubuntu-latest, windows-latest] 15 | dc: [ldc-latest, ldc-beta, dmd-latest, dmd-beta] 16 | runs-on: ${{ matrix.os }} 17 | 18 | steps: 19 | - uses: actions/checkout@v3.3.0 20 | 21 | - name: D Compiler Installation 22 | uses: dlang-community/setup-dlang@v1.3.0 23 | with: 24 | compiler: ${{ matrix.dc }} 25 | 26 | - name: Run tests with coverage 27 | run: dub -q test --build=unittest-cov 28 | 29 | - name: Install Lapack 30 | if: contains( matrix.os, 'ubuntu') 31 | run: | 32 | sudo apt-get update 33 | sudo apt-get install libopenblas-dev 34 | shell: bash 35 | 36 | - name: Run Blas Tests with coverage 37 | if: contains( matrix.os, 'ubuntu') 38 | run: dub -q test --build=unittest-cov-blas --config=openblas 39 | 40 | - name: Codecov 41 | uses: codecov/codecov-action@v3.1.1 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.a 2 | *.log 3 | *.lst 4 | *.o 5 | *.pdf 6 | *.s 7 | *.exe 8 | *.sublime-project 9 | *.sublime-workspace 10 | .dub 11 | .generated 12 | .vscode 13 | __* 14 | dub.selections.json 15 | mir-algorithm 16 | mir-algorithm-test-library 17 | mir-internal 18 | web 19 | .DS_* 20 | docs 21 | *.html 22 | docs.json 23 | out/ 24 | /tests_extractor.d 25 | build/ 26 | builddir/ 27 | files 28 | mir-algorithm.lib 29 | mir-algorithm-test-default 30 | mir-algorithm-test-dips 31 | subprojects/mir-core 32 | mir-core 33 | mir-algorithm-test-dip1008 34 | test 35 | test.d 36 | docgen 37 | _build_dir_ 38 | .VSCodeCounter 39 | mir-stat-test-library 40 | *.pdb -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2022-3 Mir Stat Authors 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![codecov.io](https://codecov.io/github/libmir/mir-stat/coverage.svg?branch=master)](https://codecov.io/github/libmir/mir-stat?branch=master) 2 | [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/libmir/mir-stat/d.yml?branch=master)](https://github.com/libmir/mir-stat/actions) 3 | [![Circle CI](https://circleci.com/gh/libmir/mir-stat.svg?style=svg)](https://circleci.com/gh/libmir/mir-stat) 4 | 5 | [![Dub downloads](https://img.shields.io/dub/dt/mir-stat.svg)](http://code.dlang.org/packages/mir-stat) 6 | [![Dub downloads](https://img.shields.io/dub/dm/mir-stat.svg)](http://code.dlang.org/packages/mir-stat) 7 | [![License](https://img.shields.io/dub/l/mir-stat.svg)](http://code.dlang.org/packages/mir-stat) 8 | [![Latest version](https://img.shields.io/dub/v/mir-stat.svg)](http://code.dlang.org/packages/mir-stat) 9 | 10 | # Mir Stat 11 | 12 | ### Statistical algorithms for the D programming language (Dlang). 13 | 14 | This package includes statistical algorithms, including but not limited to: 15 | - [Descriptive statistics](http://mir-stat.libmir.org/mir_stat_descriptive.html) 16 | - [Probability distributions](http://mir-stat.libmir.org/mir_stat_distribution.html) 17 | - [Statistical inference](http://mir-stat.libmir.org/mir_stat_inference.html) 18 | - [Data Transformations](http://mir-stat.libmir.org/mir_stat_transform.html) 19 | 20 | #### Full Documentation 21 | [mir-stat.libmir.org](http://mir-stat.libmir.org/) 22 | 23 | #### Example 24 | ```d 25 | @safe pure nothrow 26 | void main() 27 | { 28 | import mir.algorithm.iteration: all; 29 | import mir.math.common: approxEqual, pow; 30 | import mir.test: shouldApprox; 31 | 32 | // mir.stat.descriptive 33 | import mir.stat.descriptive.univariate: mean, kurtosis; 34 | auto x = [1.0, 2, 3, 4]; 35 | x.mean.shouldApprox == 2.5; 36 | x.kurtosis.shouldApprox == -1.2; 37 | 38 | // mir.stat.distribution 39 | import mir.stat.distribution.binomial: binomialPMF; 40 | 4.binomialPMF(6, 2.0 / 3).shouldApprox == (15.0 * pow(2.0 / 3, 4) * pow(1.0 / 3, 2)); 41 | 42 | // mir.stat.transform 43 | import mir.stat.transform: zscore; 44 | assert(x.zscore.all!approxEqual([-1.161895, -0.387298, 0.387298, 1.161895])); 45 | 46 | // mir.stat.inference 47 | import mir.stat.inference: dAgostinoPearsonTest; 48 | auto y = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25, 49 | 2.0, 7.5, 5.0, 1.0, 1.5, 0.0]; 50 | double p; 51 | y.dAgostinoPearsonTest(p).shouldApprox == 4.151936053369771; 52 | } 53 | ``` 54 | -------------------------------------------------------------------------------- /dub.sdl: -------------------------------------------------------------------------------- 1 | name "mir-stat" 2 | description "Dlang Statistical Package" 3 | 4 | authors "John Michael Hall" "Ilya Yaroshenko" 5 | copyright "Copyright © 2022-3, Mir Stat Authors." 6 | license "Apache-2.0" 7 | 8 | dependency "mir-algorithm" version=">=3.20.1" 9 | 10 | toolchainRequirements frontend=">=2.097" 11 | 12 | configuration "library" { 13 | targetType "library" 14 | # default or user specified mir-lapack sub-configuration is used 15 | } 16 | buildType "unittest" { 17 | buildOptions "unittests" "debugMode" "debugInfo" 18 | versions "mir_stat_test" 19 | //versions "mir_stat_test_fp" 20 | //versions "mir_stat_test_binom_multi" 21 | dflags "-lowmem" 22 | } 23 | configuration "unittest-perf" { 24 | dependency "mir-random" version=">=2.2.19" 25 | } 26 | buildType "unittest-perf" { 27 | buildOptions "unittests" "releaseMode" "optimize" "inline" 28 | dflags "-mcpu=native" 29 | versions "mir_stat_test_skew_performance" 30 | versions "mir_stat_test_kurt_performance" 31 | versions "mir_stat_test_cov_performance" 32 | versions "mir_stat_test_cor_performance" 33 | } 34 | buildType "unittest-dip1008" { 35 | buildOptions "unittests" "debugMode" "debugInfo" 36 | versions "mir_stat_test" 37 | dflags "-lowmem" "-preview=dip1008" 38 | } 39 | buildType "unittest-dip1000" { 40 | buildOptions "unittests" "debugMode" "debugInfo" 41 | versions "mir_stat_test" 42 | dflags "-lowmem" "-preview=dip1000" 43 | } 44 | buildType "unittest-cov" { 45 | buildOptions "unittests" "coverage" "debugMode" "debugInfo" 46 | versions "mir_stat_test" 47 | //versions "mir_stat_test_fp" 48 | dflags "-lowmem" 49 | } 50 | buildType "unittest-release" { 51 | buildOptions "unittests" "releaseMode" "optimize" "inline" "noBoundsCheck" 52 | versions "mir_stat_test" 53 | dflags "-lowmem" 54 | } 55 | configuration "dips" { 56 | dflags "-preview=dip1000" "-preview=dip1008" 57 | } 58 | 59 | // lubeck-related configurations 60 | buildType "unittest-blas" { 61 | buildOptions "unittests" "debugMode" "debugInfo" 62 | versions "mir_stat_test_blas" 63 | dflags "-lowmem" 64 | } 65 | buildType "unittest-cov-blas" { 66 | buildOptions "unittests" "coverage" "debugMode" "debugInfo" 67 | versions "mir_stat_test_blas" 68 | dflags "-lowmem" 69 | } 70 | configuration "openblas" { 71 | dependency "mir-lapack" version=">=1.2.9" 72 | subConfiguration "mir-lapack" "openblas" 73 | } 74 | 75 | configuration "threelib" { 76 | dependency "mir-lapack" version=">=1.2.9" 77 | subConfiguration "mir-lapack" "threelib" 78 | } 79 | 80 | configuration "cblas" { 81 | dependency "mir-lapack" version=">=1.2.9" 82 | subConfiguration "mir-lapack" "cblas" 83 | } 84 | 85 | configuration "blas" { 86 | dependency "mir-lapack" version=">=1.2.9" 87 | subConfiguration "mir-lapack" "blas" 88 | } 89 | 90 | configuration "lapack" { 91 | dependency "mir-lapack" version=">=1.2.9" 92 | subConfiguration "mir-lapack" "lapack" 93 | } 94 | 95 | configuration "mkl-sequential" { 96 | dependency "mir-lapack" version=">=1.2.9" 97 | subConfiguration "mir-lapack" "mkl-sequential" 98 | } 99 | 100 | configuration "mkl-sequential-ilp" { 101 | dependency "mir-lapack" version=">=1.2.9" 102 | subConfiguration "mir-lapack" "mkl-sequential-ilp" 103 | } 104 | 105 | configuration "mkl-tbb-thread" { 106 | dependency "mir-lapack" version=">=1.2.9" 107 | subConfiguration "mir-lapack" "mkl-tbb-thread" 108 | } 109 | 110 | configuration "mkl-tbb-thread-ilp" { 111 | dependency "mir-lapack" version=">=1.2.9" 112 | subConfiguration "mir-lapack" "mkl-tbb-thread-ilp" 113 | } 114 | 115 | configuration "mkl-sequential-dll" { 116 | dependency "mir-lapack" version=">=1.2.9" 117 | subConfiguration "mir-lapack" "mkl-sequential-dll" 118 | } 119 | 120 | configuration "mkl-sequential-ilp-dll" { 121 | dependency "mir-lapack" version=">=1.2.9" 122 | subConfiguration "mir-lapack" "mkl-sequential-ilp-dll" 123 | } 124 | 125 | configuration "mkl-tbb-thread-dll" { 126 | dependency "mir-lapack" version=">=1.2.9" 127 | subConfiguration "mir-lapack" "mkl-tbb-thread-dll" 128 | } 129 | 130 | configuration "mkl-tbb-thread-ilp-dll" { 131 | dependency "mir-lapack" version=">=1.2.9" 132 | subConfiguration "mir-lapack" "mkl-tbb-thread-ilp-dll" 133 | } 134 | 135 | configuration "zerolib" { 136 | dependency "mir-lapack" version=">=1.2.9" 137 | subConfiguration "mir-lapack" "zerolib" 138 | } 139 | 140 | configuration "unittest-openblas" { 141 | dependency "mir-lapack" version=">=1.2.9" 142 | subConfiguration "mir-lapack" "openblas" 143 | } 144 | 145 | configuration "unittest-threelib" { 146 | dependency "mir-lapack" version=">=1.2.9" 147 | subConfiguration "mir-lapack" "threelib" 148 | } 149 | 150 | configuration "unittest-cblas" { 151 | dependency "mir-lapack" version=">=1.2.9" 152 | subConfiguration "mir-lapack" "cblas" 153 | } 154 | 155 | configuration "unittest-blas" { 156 | dependency "mir-lapack" version=">=1.2.9" 157 | subConfiguration "mir-lapack" "blas" 158 | } 159 | 160 | configuration "unittest-lapack" { 161 | dependency "mir-lapack" version=">=1.2.9" 162 | subConfiguration "mir-lapack" "lapack" 163 | } 164 | 165 | configuration "unittest-mkl-sequential" { 166 | dependency "mir-lapack" version=">=1.2.9" 167 | subConfiguration "mir-lapack" "mkl-sequential" 168 | } 169 | -------------------------------------------------------------------------------- /index.d: -------------------------------------------------------------------------------- 1 | Ddoc 2 | 3 | $(P Dlang Statistical Package.) 4 | 5 | $(P The following table is a quick reference guide for which Mir Stat modules to 6 | use for a given category of functionality.) 7 | 8 | 9 | $(BOOKTABLE , 10 | $(TR 11 | $(TH Modules) 12 | $(TH Description) 13 | ) 14 | $(TR $(TDNW $(MREF mir,stat)) $(TD Publicly imports `mir.stat.*` modules )) 15 | $(LEADINGROW Descriptive Statistics) 16 | $(TR $(TDNW $(MREF mir,stat,descriptive)) $(TD Descriptive statistics )) 17 | $(TR $(TDNW $(MREF mir,stat,descriptive,aliases)) $(TD Aliases for common functions )) 18 | $(TR $(TDNW $(MREF mir,stat,descriptive,multivariate)) $(TD Multivariate Descriptive statistics )) 19 | $(TR $(TDNW $(MREF mir,stat,descriptive,univariate)★) $(TD Univariate Descriptive statistics )) 20 | $(TR $(TDNW $(MREF mir,stat,descriptive,weighted)) $(TD Descriptive statistics with weights )) 21 | $(LEADINGROW Other Statistical Modules) 22 | $(TR $(TDNW $(MREF mir,stat,transform)) $(TD Algorithms for transforming data )) 23 | $(TR $(TDNW $(MREF mir,stat,inference)) $(TD Algorithms for statistical inference )) 24 | $(TR $(TDNW $(MREF mir,stat,constant)) $(TD Constants used in other statistical modules )) 25 | $(LEADINGROW Probability Distributions) 26 | $(TR $(TDNW $(MREF mir,stat,distribution)★) $(TD Statistical Distributions )) 27 | $(TR $(TDNW $(MREF mir,stat,distribution,pdf)) $(TD Probability Density Functions )) 28 | $(TR $(TDNW $(MREF mir,stat,distribution,cdf)) $(TD Cumulative Distribution Functions )) 29 | $(TR $(TDNW $(MREF mir,stat,distribution,invcdf)) $(TD Inverse Cumulative Distribution Functions )) 30 | ) 31 | 32 | Copyright: 2022-3 Mir Stat Authors. 33 | 34 | Macros: 35 | TITLE=Mir Stat 36 | WIKI=Mir Stat 37 | DDOC_BLANKLINE= 38 | _= -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('mir-stat', 'd', version : '0.3.1', license: 'Apache-2.0') 2 | 3 | description = 'Dlang Statistical Package' 4 | 5 | subprojects = [ 6 | 'cblas-d', 7 | 'lapack-d', 8 | 'mir-algorithm', 9 | 'mir-blas', 10 | 'mir-core', 11 | 'mir-lapack', 12 | 'mir-random', 13 | ] 14 | 15 | if target_machine.system() == 'linux' 16 | subprojects += 'mir-linux-kernel' 17 | endif 18 | 19 | has_cpp_headers = false 20 | 21 | sources_list = [ 22 | 'mir/math/internal/fp_powi', 23 | 'mir/math/internal/powi', 24 | 'mir/math/internal/log_beta', 25 | 'mir/math/internal/log_binomial', 26 | 'mir/math/internal/xlogy', 27 | 'mir/stat/descriptive/multivariate', 28 | 'mir/stat/descriptive/univariate', 29 | 'mir/stat/descriptive/weighted', 30 | 'mir/stat/descriptive/aliases', 31 | 'mir/stat/descriptive/package', 32 | 'mir/stat/constant', 33 | 'mir/stat/inference', 34 | 'mir/stat/transform', 35 | 'mir/stat/package', 36 | 'mir/stat/distribution/bernoulli', 37 | 'mir/stat/distribution/beta', 38 | 'mir/stat/distribution/beta_proportion', 39 | 'mir/stat/distribution/binomial', 40 | 'mir/stat/distribution/categorical', 41 | 'mir/stat/distribution/cauchy', 42 | 'mir/stat/distribution/chi2', 43 | 'mir/stat/distribution/cornish_fisher', 44 | 'mir/stat/distribution/exponential', 45 | 'mir/stat/distribution/f', 46 | 'mir/stat/distribution/gamma', 47 | 'mir/stat/distribution/generalized_pareto', 48 | 'mir/stat/distribution/geometric', 49 | 'mir/stat/distribution/gev', 50 | 'mir/stat/distribution/hypergeometric', 51 | 'mir/stat/distribution/laplace', 52 | 'mir/stat/distribution/log_normal', 53 | 'mir/stat/distribution/logistic', 54 | 'mir/stat/distribution/negative_binomial', 55 | 'mir/stat/distribution/normal', 56 | 'mir/stat/distribution/pareto', 57 | 'mir/stat/distribution/poisson', 58 | 'mir/stat/distribution/rayleigh', 59 | 'mir/stat/distribution/students_t', 60 | 'mir/stat/distribution/uniform', 61 | 'mir/stat/distribution/uniform_discrete', 62 | 'mir/stat/distribution/weibull', 63 | 'mir/stat/distribution/cdf', 64 | 'mir/stat/distribution/invcdf', 65 | 'mir/stat/distribution/pdf', 66 | 'mir/stat/distribution/package', 67 | ] 68 | 69 | sources = [] 70 | foreach s : sources_list 71 | sources += 'source/' + s + '.d' 72 | endforeach 73 | 74 | add_project_arguments([ 75 | '-preview=dip1008', 76 | '-lowmem', 77 | ], language: 'd') 78 | 79 | required_deps = [] 80 | 81 | foreach p : subprojects 82 | required_deps += dependency(p, fallback : [p, 'this_dep']) 83 | endforeach 84 | 85 | directories = ['source'] 86 | 87 | if has_cpp_headers 88 | directories += 'include' 89 | endif 90 | 91 | directories = include_directories(directories) 92 | 93 | this_lib = library(meson.project_name(), 94 | sources, 95 | include_directories: directories, 96 | install: true, 97 | version: meson.project_version(), 98 | dependencies: required_deps, 99 | ) 100 | 101 | this_dep = declare_dependency( 102 | link_with: [this_lib], 103 | include_directories: directories, 104 | dependencies: required_deps, 105 | ) 106 | 107 | test_versions = ['mir_stat_test'] 108 | 109 | if has_cpp_headers 110 | install_subdir('include/', 111 | strip_directory :true, 112 | install_dir: 'include/', 113 | ) 114 | endif 115 | 116 | install_subdir('source/', 117 | strip_directory : true, 118 | install_dir: 'include/d/' + meson.project_name(), 119 | ) 120 | 121 | import('pkgconfig').generate(this_lib, 122 | description: description, 123 | subdirs: 'd/' + meson.project_name(), 124 | ) 125 | 126 | test_subdirs = [] 127 | -------------------------------------------------------------------------------- /source/mir/math/internal/benchmark.d: -------------------------------------------------------------------------------- 1 | module mir.math.internal.benchmark; 2 | 3 | import core.time; 4 | import std.traits: isMutable; 5 | 6 | package(mir) 7 | template benchmarkValues(fun...) 8 | { 9 | Duration[fun.length] benchmarkValues(T)(size_t n, out T[fun.length] values) 10 | { 11 | import std.datetime.stopwatch: StopWatch, AutoStart; 12 | Duration[fun.length] result; 13 | auto sw = StopWatch(AutoStart.yes); 14 | 15 | foreach (i, unused; fun) { 16 | values[i] = 0; 17 | sw.reset(); 18 | foreach (size_t j; 1 .. n) { 19 | values[i] += fun[i](); 20 | } 21 | result[i] = sw.peek(); 22 | values[i] /= n; 23 | } 24 | 25 | return result; 26 | } 27 | } 28 | 29 | package(mir) 30 | template benchmarkRandom(fun...) 31 | { 32 | Duration[fun.length] benchmarkRandom(T)(size_t n, size_t m, out T[fun.length] values) 33 | if (isMutable!T) 34 | { 35 | import mir.ndslice.allocation: stdcFreeSlice, stdcUninitSlice; 36 | import mir.random.engine: Random, threadLocalPtr; 37 | import mir.random.variable: NormalVariable; 38 | import std.datetime.stopwatch: StopWatch, AutoStart; 39 | 40 | Random* gen = threadLocalPtr!Random; 41 | auto rv = NormalVariable!T(0, 1); 42 | 43 | Duration[fun.length] result; 44 | auto r = stdcUninitSlice!T(m); 45 | auto sw = StopWatch(AutoStart.yes); 46 | 47 | foreach (i, unused; fun) { 48 | values[i] = 0; 49 | sw.reset(); 50 | foreach (size_t j; 1 .. n) { 51 | sw.stop(); 52 | foreach (ref e; r) 53 | e = rv(gen); 54 | sw.start(); 55 | values[i] += fun[i](r); 56 | } 57 | result[i] = sw.peek(); 58 | values[i] /= n; 59 | } 60 | r.stdcFreeSlice; 61 | return result; 62 | } 63 | } 64 | 65 | package(mir) 66 | template benchmarkRandom2(fun...) 67 | { 68 | Duration[fun.length] benchmarkRandom2(T)(size_t n, size_t m, out T[fun.length] values) 69 | if (isMutable!T) 70 | { 71 | import mir.ndslice.allocation: stdcFreeSlice, stdcUninitSlice; 72 | import mir.random.engine: Random, threadLocalPtr; 73 | import mir.random.variable: NormalVariable; 74 | import std.datetime.stopwatch: StopWatch, AutoStart; 75 | 76 | Random* gen = threadLocalPtr!Random; 77 | auto rv = NormalVariable!T(0, 1); 78 | 79 | Duration[fun.length] result; 80 | auto r1 = stdcUninitSlice!T(m); 81 | auto r2 = stdcUninitSlice!T(m); 82 | auto sw = StopWatch(AutoStart.yes); 83 | 84 | foreach (i, unused; fun) { 85 | values[i] = 0; 86 | sw.reset(); 87 | foreach (size_t j; 1 .. n) { 88 | sw.stop(); 89 | foreach (size_t k; 0 .. m) { 90 | r1[k] = rv(gen); 91 | r2[k] = r1[k] + rv(gen); 92 | } 93 | sw.start(); 94 | values[i] += fun[i](r1, r2); 95 | } 96 | result[i] = sw.peek(); 97 | values[i] /= n; 98 | } 99 | r1.stdcFreeSlice; 100 | r2.stdcFreeSlice; 101 | return result; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /source/mir/math/internal/fp_powi.d: -------------------------------------------------------------------------------- 1 | /++ 2 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 3 | 4 | Authors: John Michael Hall 5 | 6 | Copyright: 2022 Mir Stat Authors. 7 | 8 | +/ 9 | 10 | module mir.math.internal.fp_powi; 11 | 12 | import mir.bignum.fp: Fp; 13 | 14 | /// 15 | package(mir) 16 | T fp_powi(T)(const T x, size_t i) 17 | if (is(T == Fp!size, size_t size)) 18 | { 19 | if (i == 0) { 20 | return T(1.0); 21 | } else if (i == 1) { 22 | return x; 23 | } else { 24 | T output = x; 25 | for (size_t j = 1; j < i; j++) { 26 | output *= x; 27 | } 28 | return output; 29 | } 30 | } 31 | 32 | version(mir_stat_test) 33 | @safe pure nothrow @nogc 34 | unittest 35 | { 36 | import mir.conv: to; 37 | auto x = Fp!128(3.0); 38 | assert(x.fp_powi(0).to!double == 1); 39 | assert(x.fp_powi(1).to!double == 3); 40 | assert(x.fp_powi(2).to!double == 9); 41 | } 42 | -------------------------------------------------------------------------------- /source/mir/math/internal/linearAlgebra.d: -------------------------------------------------------------------------------- 1 | /++ 2 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 3 | 4 | Authors: John Michael Hall 5 | 6 | Copyright: 2023 Mir Stat Authors. 7 | 8 | +/ 9 | 10 | module mir.math.internal.linearAlgebra; 11 | 12 | version(mir_stat_test_blas) 13 | @safe pure nothrow @nogc 14 | unittest 15 | { 16 | import mir.lapack; 17 | } 18 | 19 | -------------------------------------------------------------------------------- /source/mir/math/internal/log1p.d: -------------------------------------------------------------------------------- 1 | /++ 2 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 3 | 4 | Authors: John Michael Hall 5 | 6 | Copyright: 2023 Mir Stat Authors. 7 | 8 | +/ 9 | 10 | module mir.math.internal.log1p; 11 | 12 | 13 | // There are issues with the v2.102.2 point release for `log1p` producing the 14 | // wrong results. This takes a simpler approach for 2.102 as a whole 15 | static if (__VERSION__ != 2102) { 16 | public import std.math.exponential: log1p; 17 | } else { 18 | import std.math.exponential: log; 19 | package(mir) 20 | @safe pure nothrow @nogc 21 | float log1p(const float x) { 22 | return log(x + 1); 23 | } 24 | 25 | package(mir) 26 | @safe pure nothrow @nogc 27 | double log1p(const double x) { 28 | return log(x + 1); 29 | } 30 | 31 | package(mir) 32 | @safe pure nothrow @nogc 33 | real log1p(const real x) { 34 | return log(x + 1); 35 | } 36 | 37 | version(mir_stat_test) 38 | @safe pure nothrow @nogc 39 | unittest { 40 | import std.math.exponential: log; 41 | import mir.test: shouldApprox; 42 | float x1 = -0.0125; 43 | double x2 = x1; 44 | real x3 = x1; 45 | x1.log1p.shouldApprox == -0.0125787822; 46 | x2.log1p.shouldApprox == -0.0125787822; 47 | x3.log1p.shouldApprox == -0.0125787822; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /source/mir/math/internal/log_beta.d: -------------------------------------------------------------------------------- 1 | /++ 2 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 3 | 4 | Authors: John Michael Hall 5 | 6 | Copyright: 2022 Mir Stat Authors. 7 | 8 | +/ 9 | 10 | module mir.math.internal.log_beta; 11 | 12 | import mir.internal.utility: isFloatingPoint; 13 | 14 | /// 15 | package(mir) 16 | @safe pure nothrow @nogc 17 | T logBeta(T)(const T alpha, const T beta) 18 | if (isFloatingPoint!T) 19 | { 20 | import std.mathspecial: logGamma; 21 | 22 | return logGamma(alpha) + logGamma(beta) - logGamma(alpha + beta); 23 | } 24 | 25 | version(mir_stat_test) 26 | @safe pure nothrow 27 | unittest 28 | { 29 | import mir.math.common: approxEqual, log; 30 | import std.mathspecial: beta; 31 | 32 | assert(logBeta(1.0, 2).approxEqual(log(beta(1.0, 2)))); 33 | assert(logBeta(0.5, 4).approxEqual(log(beta(0.5, 4)))); 34 | } 35 | -------------------------------------------------------------------------------- /source/mir/math/internal/log_binomial.d: -------------------------------------------------------------------------------- 1 | /++ 2 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 3 | 4 | Authors: John Michael Hall 5 | 6 | Copyright: 2022 Mir Stat Authors. 7 | 8 | +/ 9 | 10 | module mir.math.internal.log_binomial; 11 | 12 | import mir.bignum.fp: Fp; 13 | import mir.internal.utility: isFloatingPoint; 14 | 15 | private enum size_t logFactorialAlternative = 1 << 11; 16 | 17 | /// 18 | T logFactorial(T = double)(ulong count, ulong start = 1) 19 | if (isFloatingPoint!T) 20 | in (start, "start must be larger than zero") 21 | { 22 | import mir.bignum.fp: fp_log; 23 | import mir.math.numeric: factorial; 24 | import std.mathspecial: logGamma; 25 | 26 | if (count + start < logFactorialAlternative) { 27 | return fp_log!T(factorial(count, start)); 28 | } else { 29 | T output = T(logGamma(count + start)); // normally logGamma(x + 1), but start is included in value so subtract out 30 | if (start < logFactorialAlternative) { 31 | return output - fp_log!T(factorial(start - 1)); 32 | } else { 33 | return output - T(logGamma(start)); 34 | } 35 | } 36 | } 37 | 38 | /// 39 | @safe pure nothrow @nogc 40 | version(mir_stat_test_logBinomial) 41 | unittest { 42 | import mir.math.common: approxEqual, log; 43 | assert(logFactorial(0) == 0); 44 | assert(logFactorial(1) == 0); 45 | assert(logFactorial(2).approxEqual(log(1.0 * 2))); 46 | assert(logFactorial(3).approxEqual(log(1.0 * 2 * 3))); 47 | assert(logFactorial(4).approxEqual(log(1.0 * 2 * 3 * 4))); 48 | assert(logFactorial(5).approxEqual(log(1.0 * 2 * 3 * 4 * 5))); 49 | } 50 | 51 | // test starting points 52 | @safe pure nothrow @nogc 53 | version(mir_stat_test_logBinomial) 54 | unittest { 55 | import mir.math.common: approxEqual, log; 56 | assert(logFactorial(2, 2).approxEqual(log(2.0 * 3))); 57 | assert(logFactorial(3, 2).approxEqual(log(2.0 * 3 * 4))); 58 | assert(logFactorial(4, 2).approxEqual(log(2.0 * 3 * 4 * 5))); 59 | assert(logFactorial(4, 3).approxEqual(log(3.0 * 4 * 5 * 6))); 60 | assert(logFactorial(4, 4).approxEqual(log(4.0 * 5 * 6 * 7))); 61 | assert(logFactorial(5, 2).approxEqual(log(2.0 * 3 * 4 * 5 * 6))); 62 | assert(logFactorial(5, 3).approxEqual(log(3.0 * 4 * 5 * 6 * 7))); 63 | assert(logFactorial(5, 4).approxEqual(log(4.0 * 5 * 6 * 7 * 8))); 64 | assert(logFactorial(5, 5).approxEqual(log(5.0 * 6 * 7 * 8 * 9))); 65 | } 66 | 67 | // test larger value 68 | @safe pure nothrow @nogc 69 | version(mir_stat_test_logBinomial) 70 | unittest { 71 | import mir.bignum.fp: fp_log; 72 | import mir.math.common: approxEqual, log; 73 | import mir.math.numeric: factorial; 74 | import std.mathspecial: logGamma; 75 | 76 | size_t x = logFactorialAlternative + 500; 77 | assert(logFactorial(x).approxEqual(logGamma(cast(double) x + 1))); 78 | assert(logFactorial(x, 2).approxEqual(logGamma(cast(double) x + 1 + 1) - log(1.0))); 79 | assert(logFactorial(x, 3).approxEqual(logGamma(cast(double) x + 2 + 1) - log(2.0))); 80 | assert(logFactorial(x, x / 2).approxEqual(logGamma(cast(double) x + x / 2) - fp_log!double(factorial(x / 2 - 1)))); 81 | } 82 | 83 | private enum size_t logBinomialCoefficientAlternative = 1 << 11; 84 | 85 | /// 86 | T logBinomialCoefficient(T = double)(ulong n, uint k) 87 | if (isFloatingPoint!T) 88 | in (k <= n, "k must be less than or equal to n") 89 | { 90 | import mir.math.common: log; 91 | import mir.bignum.fp: fp_log; 92 | import mir.math.numeric: binomialCoefficient; 93 | 94 | if (k > n - k) { 95 | k = cast(uint)(n - k); 96 | } 97 | if (k == 0) { 98 | return T(0.0); 99 | } else if (k == 1) { 100 | return log(cast(T) n); 101 | } else { 102 | if (n < logBinomialCoefficientAlternative) { 103 | return fp_log!T(binomialCoefficient(n, k)); 104 | } else { 105 | return logFactorial!T(k, n - k + 1) - logFactorial!T(k); 106 | } 107 | } 108 | } 109 | 110 | /// 111 | @safe pure nothrow @nogc 112 | version(mir_stat_test) 113 | unittest { 114 | import mir.bignum.fp: Fp, fp_log; 115 | import mir.math.numeric: binomialCoefficient; 116 | import mir.math.common: approxEqual, log; 117 | 118 | assert(logBinomialCoefficient(5, 1).approxEqual(log(5.0))); 119 | assert(logBinomialCoefficient(5, 2).approxEqual(fp_log!double(binomialCoefficient(5, 2)))); 120 | assert(logBinomialCoefficient(5, 3).approxEqual(fp_log!double(binomialCoefficient(5, 3)))); 121 | assert(logBinomialCoefficient(5, 4).approxEqual(fp_log!double(binomialCoefficient(5, 4)))); 122 | } 123 | 124 | // test n = 6 125 | @safe pure nothrow @nogc 126 | version(mir_stat_test) 127 | unittest { 128 | import mir.bignum.fp: fp_log; 129 | import mir.math.numeric: binomialCoefficient; 130 | import mir.math.common: approxEqual; 131 | 132 | assert(logBinomialCoefficient(6, 1).approxEqual(fp_log!double(binomialCoefficient(6, 1)))); 133 | assert(logBinomialCoefficient(6, 2).approxEqual(fp_log!double(binomialCoefficient(6, 2)))); 134 | assert(logBinomialCoefficient(6, 3).approxEqual(fp_log!double(binomialCoefficient(6, 3)))); 135 | assert(logBinomialCoefficient(6, 4).approxEqual(fp_log!double(binomialCoefficient(6, 4)))); 136 | assert(logBinomialCoefficient(6, 5).approxEqual(fp_log!double(binomialCoefficient(6, 5)))); 137 | } 138 | 139 | // test n = 7 140 | @safe pure nothrow @nogc 141 | version(mir_stat_test) 142 | unittest { 143 | import mir.bignum.fp: fp_log; 144 | import mir.math.numeric: binomialCoefficient; 145 | import mir.math.common: approxEqual; 146 | 147 | assert(logBinomialCoefficient(7, 1).approxEqual(fp_log!double(binomialCoefficient(7, 1)))); 148 | assert(logBinomialCoefficient(7, 2).approxEqual(fp_log!double(binomialCoefficient(7, 2)))); 149 | assert(logBinomialCoefficient(7, 3).approxEqual(fp_log!double(binomialCoefficient(7, 3)))); 150 | assert(logBinomialCoefficient(7, 4).approxEqual(fp_log!double(binomialCoefficient(7, 4)))); 151 | assert(logBinomialCoefficient(7, 5).approxEqual(fp_log!double(binomialCoefficient(7, 5)))); 152 | assert(logBinomialCoefficient(7, 6).approxEqual(fp_log!double(binomialCoefficient(7, 6)))); 153 | } 154 | 155 | // test n = 8 156 | @safe pure nothrow @nogc 157 | version(mir_stat_test) 158 | unittest { 159 | import mir.bignum.fp: fp_log; 160 | import mir.math.numeric: binomialCoefficient; 161 | import mir.math.common: approxEqual; 162 | 163 | assert(logBinomialCoefficient(8, 1).approxEqual(fp_log!double(binomialCoefficient(8, 1)))); 164 | assert(logBinomialCoefficient(8, 2).approxEqual(fp_log!double(binomialCoefficient(8, 2)))); 165 | assert(logBinomialCoefficient(8, 3).approxEqual(fp_log!double(binomialCoefficient(8, 3)))); 166 | assert(logBinomialCoefficient(8, 4).approxEqual(fp_log!double(binomialCoefficient(8, 4)))); 167 | assert(logBinomialCoefficient(8, 5).approxEqual(fp_log!double(binomialCoefficient(8, 5)))); 168 | assert(logBinomialCoefficient(8, 6).approxEqual(fp_log!double(binomialCoefficient(8, 6)))); 169 | assert(logBinomialCoefficient(8, 7).approxEqual(fp_log!double(binomialCoefficient(8, 7)))); 170 | } 171 | 172 | // test k = 0, n = k 173 | @safe pure nothrow @nogc 174 | version(mir_stat_test) 175 | unittest { 176 | assert(logBinomialCoefficient(5, 0) == 0); 177 | assert(logBinomialCoefficient(5, 5) == 0); 178 | assert(logBinomialCoefficient(1, 1) == 0); 179 | assert(logBinomialCoefficient(1, 0) == 0); 180 | } 181 | 182 | // Test large values 183 | @safe pure nothrow @nogc 184 | version(mir_stat_test) 185 | unittest { 186 | import mir.bignum.fp: fp_log; 187 | import mir.math.numeric: binomialCoefficient; 188 | import mir.math.common: approxEqual, log; 189 | 190 | size_t x = logBinomialCoefficientAlternative + 500; 191 | 192 | assert(logBinomialCoefficient(x, 1).approxEqual(log(cast(double) x))); 193 | assert(logBinomialCoefficient(x, 250).approxEqual(logFactorial(x) - logFactorial(250) - logFactorial(x - 250))); 194 | assert(logBinomialCoefficient(x, cast(uint) x / 2).approxEqual(logFactorial(x) - logFactorial(x / 2) - logFactorial(x / 2))); 195 | assert(logBinomialCoefficient(x, cast(uint) x - 250).approxEqual(logBinomialCoefficient(x, 250))); 196 | } 197 | -------------------------------------------------------------------------------- /source/mir/math/internal/powi.d: -------------------------------------------------------------------------------- 1 | /++ 2 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 3 | 4 | Authors: John Michael Hall 5 | 6 | Copyright: 2022 Mir Stat Authors. 7 | 8 | +/ 9 | 10 | module mir.math.internal.powi; 11 | 12 | import mir.internal.utility: isFloatingPoint; 13 | import std.traits: isSigned, isUnsigned, Unqual; 14 | 15 | /// 16 | package(mir) 17 | Unqual!T powi(T, U)(T x, U i) 18 | if (isFloatingPoint!(Unqual!(T)) && isSigned!(Unqual!(U))) 19 | { 20 | import mir.math.common: powi; 21 | return powi(cast(Unqual!(T)) x, cast(Unqual!(U)) i); 22 | } 23 | 24 | /// 25 | package(mir) 26 | double powi(T, U)(T x, U i) 27 | if (!isFloatingPoint!(Unqual!(T)) && is(Unqual!(T) : double) && isSigned!(Unqual!(U))) 28 | { 29 | import mir.math.common: powi; 30 | return powi(cast(double) x, cast(Unqual!(U)) i); 31 | } 32 | 33 | /// 34 | package(mir) 35 | Unqual!T powi(T, U)(T x, U i) 36 | if (isFloatingPoint!(Unqual!(T)) && isUnsigned!(Unqual!(U))) 37 | { 38 | assert(i < int.max, "powi: converting unsigned i to signed will result in overflow"); 39 | 40 | return powi(cast(Unqual!(T)) x, cast(int) i); 41 | } 42 | 43 | /// 44 | package(mir) 45 | double powi(T, U)(T x, U i) 46 | if (!isFloatingPoint!(Unqual!(T)) && is(Unqual!(T) : double) && isUnsigned!(Unqual!(U))) 47 | { 48 | assert(i < int.max, "powi: converting unsigned i to signed will result in overflow"); 49 | 50 | return powi(cast(double) x, cast(int) i); 51 | } 52 | 53 | /// 54 | package(mir) 55 | Unqual!T powi(T)(T x, size_t i) 56 | if (!isFloatingPoint!(Unqual!(T)) && !is(Unqual!(T) : double)) 57 | { 58 | if (i == 0) { 59 | return cast(Unqual!T) 1; 60 | } else if (i == 1) { 61 | return cast(Unqual!T) x; 62 | } else { 63 | auto output = cast(Unqual!T) x; 64 | for (size_t j = 1; j < i; j++) { 65 | output *= x; 66 | } 67 | return output; 68 | } 69 | } 70 | 71 | version(mir_stat_test) 72 | @safe pure nothrow 73 | unittest 74 | { 75 | import mir.complex; 76 | alias C = Complex!double; 77 | auto x = C(1, 2); 78 | assert(x.powi(0) == 1); 79 | assert(x.powi(1) == x); 80 | } 81 | 82 | version(mir_stat_test) 83 | @safe pure nothrow 84 | unittest 85 | { 86 | import std.complex: Complex; 87 | Complex!double x = Complex!double(1.0, 2.0); 88 | assert(x.powi(0) == 1); 89 | assert(x.powi(1) == x); 90 | } 91 | -------------------------------------------------------------------------------- /source/mir/math/internal/xlogy.d: -------------------------------------------------------------------------------- 1 | /++ 2 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 3 | 4 | Authors: John Michael Hall 5 | 6 | Copyright: 2022 Mir Stat Authors. 7 | 8 | +/ 9 | 10 | module mir.math.internal.xlogy; 11 | 12 | import mir.internal.utility: isFloatingPoint; 13 | 14 | /++ 15 | Returns x * log(x) 16 | 17 | Returns: 18 | x * log(x) 19 | +/ 20 | package(mir) 21 | @safe pure nothrow @nogc 22 | T xlog(T)(const T x) 23 | if (isFloatingPoint!T) 24 | { 25 | import mir.math.common: log; 26 | 27 | assert(x >= 0, "xlog: x must be greater than or equal to zero"); 28 | return x ? x * log(x) : 0; 29 | } 30 | 31 | /// 32 | package(mir) 33 | @safe pure nothrow @nogc 34 | T xlogy(T)(const T x, const T y) 35 | if (isFloatingPoint!T) 36 | { 37 | import mir.math.common: log; 38 | 39 | if (x == 0) 40 | return 0; 41 | else 42 | return x * log(y); 43 | } 44 | 45 | version(mir_stat_test) 46 | @safe pure nothrow 47 | unittest 48 | { 49 | import mir.math.common: approxEqual, log; 50 | 51 | assert(xlogy(0.0, 2) == 0); 52 | assert(xlogy(0.5, 4).approxEqual(0.5 * log(4.0))); 53 | } 54 | 55 | /// 56 | package(mir) 57 | @safe pure nothrow @nogc 58 | T xlog1py(T)(const T x, const T y) 59 | if (isFloatingPoint!T) 60 | { 61 | import mir.math.internal.log1p: log1p; 62 | 63 | if (x == 0) 64 | return 0; 65 | else 66 | return x * log1p(y); 67 | } 68 | 69 | version(mir_stat_test) 70 | @safe pure nothrow 71 | unittest 72 | { 73 | import mir.math.common: approxEqual, log; 74 | 75 | assert(xlog1py(0.0, 2) == 0); 76 | assert(xlog1py(0.5, 4).approxEqual(0.5 * log(1 + 4.0))); 77 | } 78 | -------------------------------------------------------------------------------- /source/mir/stat/constant.d: -------------------------------------------------------------------------------- 1 | /++ 2 | This module contains constants used in statistical algorithms. 3 | 4 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 | 6 | Authors: John Michael Hall 7 | 8 | Copyright: 2022-3 Mir Stat Authors. 9 | 10 | +/ 11 | 12 | module mir.stat.constant; 13 | 14 | import mir.math.common: log, sqrt; 15 | import mir.math.constant: PI; 16 | 17 | /// 18 | enum real LOGPI = 0x1.250d048e7a1bd0bd5f956c6a843f4p+0L; // log(pi) 19 | /// 20 | enum real LOGSQRT2PI = 0x0.eb3f8e4325f5a53494bc900144192p+0L; // log(sqrt(2pi)) 21 | /// 22 | enum real SQRTPI = 0x1.c5bf891b4ef6aa79c3b0520d5db93p+0L; // sqrt(PI); 23 | /// 24 | enum real M_SQRTPI = 0x0.906eba8214db688d71d48a7f6bfec3p+0L; // 1/sqrt(pi) 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /source/mir/stat/descriptive/aliases.d: -------------------------------------------------------------------------------- 1 | /++ 2 | This module contains aliases of common functions. 3 | 4 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 | 6 | Authors: John Michael Hall 7 | 8 | Copyright: 2023 Mir Stat Authors. 9 | 10 | Macros: 11 | SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, stat, $1)$(NBSP) 12 | SUB2REF = $(REF_ALTTEXT $(TT $2), $2, mir, stat, descriptive, $1)$(NBSP) 13 | MATHREF = $(GREF_ALTTEXT mir-algorithm, $(TT $2), $2, mir, math, $1)$(NBSP) 14 | NDSLICEREF = $(GREF_ALTTEXT mir-algorithm, $(TT $2), $2, mir, ndslice, $1)$(NBSP) 15 | T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) 16 | T3=$(TR $(TDNW $(LREF $1)) $(TD $2) $(TD $+)) 17 | T4=$(TR $(TDNW $(LREF $1)) $(TD $2) $(TD $3) $(TD $4)) 18 | 19 | +/ 20 | 21 | module mir.stat.descriptive.aliases; 22 | 23 | import mir.stat.descriptive.univariate: standardDeviation, variance, skewness, 24 | kurtosis, coefficientOfVariation, 25 | interquartileRange, medianAbsoluteDeviation; 26 | import mir.stat.descriptive.multivariate: covariance, correlation; 27 | 28 | // From univariate 29 | /// 30 | alias sd = standardDeviation; 31 | /// 32 | alias var = variance; 33 | /// 34 | alias skew = skewness; 35 | /// 36 | alias kurt = kurtosis; 37 | /// 38 | alias cv = coefficientOfVariation; 39 | /// 40 | alias iqr = interquartileRange; 41 | /// 42 | alias mad = medianAbsoluteDeviation; 43 | 44 | 45 | // From multivariate 46 | /// 47 | alias cov = covariance; 48 | /// 49 | alias cor = correlation; 50 | 51 | @safe pure @nogc nothrow 52 | unittest 53 | { 54 | alias a = sd; 55 | alias b = sd!double; 56 | alias c = sd!"naive"; 57 | } 58 | 59 | @safe pure @nogc nothrow 60 | unittest 61 | { 62 | alias a = var; 63 | alias b = var!double; 64 | alias c = var!"naive"; 65 | } 66 | 67 | @safe pure @nogc nothrow 68 | unittest 69 | { 70 | alias a = skew; 71 | alias b = skew!double; 72 | alias c = skew!"naive"; 73 | } 74 | 75 | @safe pure @nogc nothrow 76 | unittest 77 | { 78 | alias a = kurt; 79 | alias b = kurt!double; 80 | alias c = kurt!"naive"; 81 | } 82 | 83 | @safe pure @nogc nothrow 84 | unittest 85 | { 86 | alias a = cv; 87 | alias b = cv!double; 88 | alias c = cv!"naive"; 89 | } 90 | 91 | @safe pure @nogc nothrow 92 | unittest 93 | { 94 | alias a = iqr; 95 | alias b = iqr!double; 96 | alias c = iqr!"type7"; 97 | } 98 | 99 | @safe pure @nogc nothrow 100 | unittest 101 | { 102 | alias a = mad; 103 | } 104 | 105 | @safe pure @nogc nothrow 106 | unittest 107 | { 108 | alias a = cov; 109 | alias b = cov!double; 110 | alias c = cov!"naive"; 111 | } 112 | 113 | @safe pure @nogc nothrow 114 | unittest 115 | { 116 | alias a = cor; 117 | alias b = cor!double; 118 | alias c = cor!"naive"; 119 | } 120 | -------------------------------------------------------------------------------- /source/mir/stat/descriptive/package.d: -------------------------------------------------------------------------------- 1 | /++ 2 | This package publicly imports `mir.stat.descriptive.*` modules. 3 | 4 | $(BOOKTABLE , 5 | $(TR 6 | $(TH Modules) 7 | $(TH Description) 8 | ) 9 | $(TR $(TDNW $(MREF mir,stat,descriptive,aliases)) $(TD Aliases for common functions )) 10 | $(TR $(TDNW $(MREF mir,stat,descriptive,multivariate)) $(TD Multivariate Descriptive statistics )) 11 | $(TR $(TDNW $(MREF mir,stat,descriptive,univariate)) $(TD Univariate Descriptive statistics )) 12 | $(TR $(TDNW $(MREF mir,stat,descriptive,weighted)) $(TD Descriptive statistics with weights )) 13 | ) 14 | 15 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 16 | 17 | Authors: John Michael Hall, Ilya Yaroshenko 18 | 19 | Copyright: 2022-3 Mir Stat Authors. 20 | 21 | Macros: 22 | SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, stat, $1)$(NBSP) 23 | MATHREF = $(GREF_ALTTEXT mir-algorithm, $(TT $2), $2, mir, math, $1)$(NBSP) 24 | NDSLICEREF = $(GREF_ALTTEXT mir-algorithm, $(TT $2), $2, mir, ndslice, $1)$(NBSP) 25 | T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) 26 | T4=$(TR $(TDNW $(LREF $1)) $(TD $2) $(TD $3) $(TD $4)) 27 | 28 | +/ 29 | 30 | module mir.stat.descriptive; 31 | 32 | /// 33 | public import mir.stat.descriptive.aliases; 34 | /// 35 | public import mir.stat.descriptive.multivariate; 36 | /// 37 | public import mir.stat.descriptive.univariate; 38 | /// 39 | public import mir.stat.descriptive.weighted; 40 | -------------------------------------------------------------------------------- /source/mir/stat/distribution/bernoulli.d: -------------------------------------------------------------------------------- 1 | /++ 2 | This module contains algorithms for the $(LINK2 https://en.wikipedia.org/wiki/Bernoulli_distribution, Bernoulli Distribution). 3 | 4 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 | 6 | Authors: John Michael Hall 7 | 8 | Copyright: 2022-3 Mir Stat Authors. 9 | 10 | +/ 11 | 12 | module mir.stat.distribution.bernoulli; 13 | 14 | import mir.internal.utility: isFloatingPoint; 15 | 16 | /++ 17 | Computes the bernoulli probability mass function (PMF). 18 | 19 | Params: 20 | x = value to evaluate PMF 21 | p = `true` probability 22 | 23 | See_also: 24 | $(LINK2 https://en.wikipedia.org/wiki/Bernoulli_distribution, Bernoulli Distribution) 25 | +/ 26 | @safe pure nothrow @nogc 27 | T bernoulliPMF(T)(const bool x, const T p) 28 | if (isFloatingPoint!T) 29 | in (p >= 0, "p must be greater than or equal to 0") 30 | in (p <= 1, "p must be less than or equal to 1") 31 | { 32 | return p * x + (1 - p) * (1 - x); 33 | } 34 | 35 | /// 36 | version(mir_stat_test) 37 | @safe pure nothrow @nogc 38 | unittest { 39 | import mir.math.common: approxEqual; 40 | 41 | assert(true.bernoulliPMF(0.5) == 0.5); 42 | assert(false.bernoulliPMF(0.5) == 0.5); 43 | 44 | assert(true.bernoulliPMF(0.7).approxEqual(0.7)); 45 | assert(false.bernoulliPMF(0.7).approxEqual(0.3)); 46 | } 47 | 48 | /++ 49 | Computes the bernoulli cumulatve distribution function (CDF). 50 | 51 | Params: 52 | x = value to evaluate CDF 53 | p = `true` probability 54 | 55 | See_also: 56 | $(LINK2 https://en.wikipedia.org/wiki/Bernoulli_distribution, Bernoulli Distribution) 57 | +/ 58 | @safe pure nothrow @nogc 59 | T bernoulliCDF(T)(const bool x, const T p) 60 | if (isFloatingPoint!T) 61 | in (p >= 0, "p must be greater than or equal to 0") 62 | in (p <= 1, "p must be less than or equal to 1") 63 | { 64 | return x + (1 - x) * (1 - p); 65 | } 66 | 67 | /// 68 | version(mir_stat_test) 69 | @safe pure nothrow @nogc 70 | unittest { 71 | import mir.math.common: approxEqual; 72 | 73 | assert(true.bernoulliCDF(0.5) == 1); 74 | assert(false.bernoulliCDF(0.5) == 0.5); 75 | 76 | assert(true.bernoulliCDF(0.7) == 1); 77 | assert(false.bernoulliCDF(0.7).approxEqual(0.3)); 78 | } 79 | 80 | /++ 81 | Computes the bernoulli complementary cumulative distribution function (CCDF). 82 | 83 | Params: 84 | x = value to evaluate CCDF 85 | p = `true` probability 86 | 87 | See_also: 88 | $(LINK2 https://en.wikipedia.org/wiki/Bernoulli_distribution, Bernoulli Distribution) 89 | +/ 90 | @safe pure nothrow @nogc 91 | T bernoulliCCDF(T)(const bool x, const T p) 92 | if (isFloatingPoint!T) 93 | in (p >= 0, "p must be greater than or equal to 0") 94 | in (p <= 1, "p must be less than or equal to 1") 95 | { 96 | return (1 - x) * p; 97 | } 98 | 99 | /// 100 | version(mir_stat_test) 101 | @safe pure nothrow @nogc 102 | unittest { 103 | import mir.math.common: approxEqual; 104 | 105 | assert(true.bernoulliCCDF(0.5) == 0); 106 | assert(false.bernoulliCCDF(0.5) == 0.5); 107 | 108 | assert(true.bernoulliCCDF(0.7) == 0); 109 | assert(false.bernoulliCCDF(0.7).approxEqual(0.7)); 110 | } 111 | 112 | /++ 113 | Computes the bernoulli inverse cumulative distribution function (InvCDF). 114 | 115 | Params: 116 | q = value to evaluate InvCDF 117 | p = `true` probability 118 | 119 | See_also: 120 | $(LINK2 https://en.wikipedia.org/wiki/Bernoulli_distribution, Bernoulli Distribution) 121 | +/ 122 | @safe pure nothrow @nogc 123 | bool bernoulliInvCDF(T)(const T q, const T p) 124 | if (isFloatingPoint!T) 125 | in (q >= 0, "q must be greater than or equal to 0") 126 | in (q <= 1, "q must be less than or equal to 1") 127 | in (p >= 0, "p must be greater than or equal to 0") 128 | in (p <= 1, "p must be less than or equal to 1") 129 | { 130 | return q > p; // this ensures bernoulliInvCDF(a, a) == false, which is consistent with bernoulliCDF(false, a) == a 131 | } 132 | 133 | /// 134 | version(mir_stat_test) 135 | @safe pure nothrow @nogc 136 | unittest { 137 | assert(0.25.bernoulliInvCDF(0.5) == false); 138 | assert(0.5.bernoulliInvCDF(0.5) == false); 139 | assert(0.75.bernoulliInvCDF(0.5) == true); 140 | 141 | assert(0.3.bernoulliInvCDF(0.7) == false); 142 | assert(0.7.bernoulliInvCDF(0.7) == false); 143 | assert(0.9.bernoulliInvCDF(0.7) == true); 144 | } 145 | 146 | version(mir_stat_test) 147 | @safe pure nothrow @nogc 148 | unittest { 149 | assert(0.0.bernoulliInvCDF(0.5) == false); 150 | assert(1.0.bernoulliInvCDF(0.5) == true); 151 | assert(0.0.bernoulliInvCDF(0.7) == false); 152 | assert(1.0.bernoulliInvCDF(0.7) == true); 153 | } 154 | 155 | /++ 156 | Computes the bernoulli log probability mass function (LPMF). 157 | 158 | Params: 159 | x = value to evaluate LPDF 160 | p = `true` probability 161 | 162 | See_also: 163 | $(LINK2 https://en.wikipedia.org/wiki/Bernoulli_distribution, Bernoulli Distribution) 164 | +/ 165 | @safe pure nothrow @nogc 166 | T bernoulliLPMF(T)(const bool x, const T p) 167 | if (isFloatingPoint!T) 168 | in (p >= 0, "p must be greater than or equal to 0") 169 | in (p <= 1, "p must be less than or equal to 1") 170 | { 171 | import mir.math.internal.xlogy: xlogy, xlog1py; 172 | 173 | return xlogy(x, p) + xlog1py(1 - x, -p); 174 | } 175 | 176 | /// 177 | version(mir_stat_test) 178 | @safe pure nothrow @nogc 179 | unittest { 180 | import mir.math.common: approxEqual, log; 181 | 182 | assert(true.bernoulliLPMF(0.5).approxEqual(log(bernoulliPMF(true, 0.5)))); 183 | assert(false.bernoulliLPMF(0.5).approxEqual(log(bernoulliPMF(false, 0.5)))); 184 | 185 | assert(true.bernoulliLPMF(0.7).approxEqual(log(bernoulliPMF(true, 0.7)))); 186 | assert(false.bernoulliLPMF(0.7).approxEqual(log(bernoulliPMF(false, 0.7)))); 187 | } 188 | -------------------------------------------------------------------------------- /source/mir/stat/distribution/beta.d: -------------------------------------------------------------------------------- 1 | /++ 2 | This module contains algorithms for the $(LINK2 https://en.wikipedia.org/wiki/Beta_distribution, Beta Distribution). 3 | 4 | An alternate parameterization of this distribution is provided in $(MREF mir,stat,distribution,beta_proportion). 5 | 6 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 7 | 8 | Authors: John Michael Hall 9 | 10 | Copyright: 2022-3 Mir Stat Authors. 11 | 12 | +/ 13 | 14 | module mir.stat.distribution.beta; 15 | 16 | import mir.internal.utility: isFloatingPoint; 17 | 18 | /++ 19 | Computes the beta probability density function (PDF). 20 | 21 | Params: 22 | x = value to evaluate PDF 23 | alpha = shape parameter #1 24 | beta = shape parameter #2 25 | 26 | See_also: 27 | $(LINK2 https://en.wikipedia.org/wiki/Beta_distribution, Beta Distribution) 28 | +/ 29 | @safe pure nothrow @nogc 30 | T betaPDF(T)(const T x, const T alpha, const T beta) 31 | if (isFloatingPoint!T) 32 | in (x >= 0, "x must be greater than or equal to 0") 33 | in (x <= 1, "x must be less than or equal to 1") 34 | in (alpha > 0, "alpha must be greater than zero") 35 | in (beta > 0, "beta must be greater than zero") 36 | { 37 | import mir.math.common: pow; 38 | import std.mathspecial: betaFunc = beta; 39 | 40 | return pow(x, (alpha - 1)) * pow((1 - x), (beta - 1)) / betaFunc(alpha, beta); 41 | } 42 | 43 | /// 44 | version(mir_stat_test) 45 | @safe pure nothrow @nogc 46 | unittest { 47 | import mir.math.common: approxEqual; 48 | 49 | assert(0.5.betaPDF(1, 1) == 1); 50 | assert(0.75.betaPDF(1, 2).approxEqual(0.5)); 51 | assert(0.25.betaPDF(0.5, 4).approxEqual(0.9228516)); 52 | } 53 | 54 | /++ 55 | Computes the beta cumulatve distribution function (CDF). 56 | 57 | Params: 58 | x = value to evaluate CDF 59 | alpha = shape parameter #1 60 | beta = shape parameter #2 61 | 62 | See_also: 63 | $(LINK2 https://en.wikipedia.org/wiki/Beta_distribution, Beta Distribution) 64 | +/ 65 | @safe pure nothrow @nogc 66 | T betaCDF(T)(const T x, const T alpha, const T beta) 67 | if (isFloatingPoint!T) 68 | in (x >= 0, "x must be greater than or equal to 0") 69 | in (x <= 1, "x must be less than or equal to 1") 70 | in (alpha > 0, "alpha must be greater than zero") 71 | in (beta > 0, "beta must be greater than zero") 72 | { 73 | import std.mathspecial: betaIncomplete; 74 | 75 | return betaIncomplete(alpha, beta, x); 76 | } 77 | 78 | /// 79 | version(mir_stat_test) 80 | @safe pure nothrow @nogc 81 | unittest { 82 | import mir.math.common: approxEqual; 83 | 84 | assert(0.5.betaCDF(1, 1).approxEqual(0.5)); 85 | assert(0.75.betaCDF(1, 2).approxEqual(0.9375)); 86 | assert(0.25.betaCDF(0.5, 4).approxEqual(0.8588867)); 87 | } 88 | 89 | /++ 90 | Computes the beta complementary cumulative distribution function (CCDF). 91 | 92 | Params: 93 | x = value to evaluate CCDF 94 | alpha = shape parameter #1 95 | beta = shape parameter #2 96 | 97 | See_also: 98 | $(LINK2 https://en.wikipedia.org/wiki/Beta_distribution, Beta Distribution) 99 | +/ 100 | @safe pure nothrow @nogc 101 | T betaCCDF(T)(const T x, const T alpha, const T beta) 102 | if (isFloatingPoint!T) 103 | in (x >= 0, "x must be greater than or equal to 0") 104 | in (x <= 1, "x must be less than or equal to 1") 105 | in (alpha > 0, "alpha must be greater than zero") 106 | in (beta > 0, "beta must be greater than zero") 107 | { 108 | import std.mathspecial: betaIncomplete; 109 | 110 | return betaIncomplete(beta, alpha, 1 - x); 111 | } 112 | 113 | /// 114 | version(mir_stat_test) 115 | @safe pure nothrow @nogc 116 | unittest { 117 | import mir.math.common: approxEqual; 118 | 119 | assert(0.5.betaCCDF(1, 1).approxEqual(0.5)); 120 | assert(0.75.betaCCDF(1, 2).approxEqual(0.0625)); 121 | assert(0.25.betaCCDF(0.5, 4).approxEqual(0.1411133)); 122 | } 123 | 124 | /++ 125 | Computes the beta inverse cumulative distribution function (InvCDF). 126 | 127 | Params: 128 | p = value to evaluate InvCDF 129 | alpha = shape parameter #1 130 | beta = shape parameter #2 131 | 132 | See_also: 133 | $(LINK2 https://en.wikipedia.org/wiki/Beta_distribution, Beta Distribution) 134 | +/ 135 | @safe pure nothrow @nogc 136 | T betaInvCDF(T)(const T p, const T alpha, const T beta) 137 | if (isFloatingPoint!T) 138 | in (p >= 0, "p must be greater than or equal to 0") 139 | in (p <= 1, "p must be less than or equal to 1") 140 | in (alpha > 0, "alpha must be greater than zero") 141 | in (beta > 0, "beta must be greater than zero") 142 | { 143 | import std.mathspecial: betaIncompleteInverse; 144 | 145 | return betaIncompleteInverse(alpha, beta, p); 146 | } 147 | 148 | /// 149 | version(mir_stat_test) 150 | @safe pure nothrow @nogc 151 | unittest { 152 | import mir.math.common: approxEqual; 153 | 154 | assert(0.5.betaInvCDF(1, 1).approxEqual(0.5)); 155 | assert(0.9375.betaInvCDF(1, 2).approxEqual(0.75)); 156 | assert(0.8588867.betaInvCDF(0.5, 4).approxEqual(0.25)); 157 | } 158 | 159 | /++ 160 | Computes the beta log probability density function (LPDF). 161 | 162 | Params: 163 | x = value to evaluate LPDF 164 | alpha = shape parameter #1 165 | beta = shape parameter #2 166 | 167 | See_also: 168 | $(LINK2 https://en.wikipedia.org/wiki/Beta_distribution, Beta Distribution) 169 | +/ 170 | @safe pure nothrow @nogc 171 | T betaLPDF(T)(const T x, const T alpha, const T beta) 172 | if (isFloatingPoint!T) 173 | in (x >= 0, "x must be greater than or equal to 0") 174 | in (x <= 1, "x must be less than or equal to 1") 175 | in (alpha > 0, "alpha must be greater than zero") 176 | in (beta > 0, "beta must be greater than zero") 177 | { 178 | import mir.math.internal.log_beta: logBeta; 179 | import mir.math.internal.xlogy: xlogy, xlog1py; 180 | 181 | return xlogy(alpha - 1, x) + xlog1py(beta - 1, -x) - logBeta(alpha, beta); 182 | } 183 | 184 | /// 185 | version(mir_stat_test) 186 | @safe pure nothrow @nogc 187 | unittest { 188 | import mir.math.common: approxEqual, log; 189 | 190 | assert(0.5.betaLPDF(1, 1).approxEqual(log(betaPDF(0.5, 1, 1)))); 191 | assert(0.75.betaLPDF(1, 2).approxEqual(log(betaPDF(0.75, 1, 2)))); 192 | assert(0.25.betaLPDF(0.5, 4).approxEqual(log(betaPDF(0.25, 0.5, 4)))); 193 | } 194 | -------------------------------------------------------------------------------- /source/mir/stat/distribution/beta_proportion.d: -------------------------------------------------------------------------------- 1 | /++ 2 | This module contains algorithms for the $(LINK2 https://en.wikipedia.org/wiki/Beta_distribution, Beta Proportion Distribution). 3 | 4 | An alternate parameterization of the $(MREF mir,stat,distribution,beta) distribuion in terms of 5 | the mean of the distribution and the sum of its shape parameters (also known as the sample 6 | size of the Beta distribution). 7 | 8 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 9 | 10 | Authors: John Michael Hall 11 | 12 | Copyright: 2022-3 Mir Stat Authors. 13 | 14 | Macros: 15 | DISTREF = $(REF_ALTTEXT $(TT $2), $2, mir, stat, distribution, $1)$(NBSP) 16 | 17 | +/ 18 | 19 | module mir.stat.distribution.beta_proportion; 20 | 21 | import mir.internal.utility: isFloatingPoint; 22 | 23 | /++ 24 | Computes the beta proportion probability density function (PDF). 25 | 26 | Params: 27 | x = value to evaluate PDF 28 | mu = shape parameter #1 29 | kappa = shape parameter #2 30 | 31 | See_also: 32 | $(LINK2 https://en.wikipedia.org/wiki/Beta_distribution, Beta Proportion Distribution), 33 | $(DISTREF beta, betaPDF) 34 | +/ 35 | @safe pure nothrow @nogc 36 | T betaProportionPDF(T)(const T x, const T mu, const T kappa) 37 | if (isFloatingPoint!T) 38 | in (x >= 0, "x must be greater than or equal to 0") 39 | in (x <= 1, "x must be less than or equal to 1") 40 | in (mu > 0, "mu must be greater than zero") 41 | in (mu < 1, "mu must be less than one") 42 | in (kappa > 0, "kappa must be greater than zero") 43 | { 44 | import mir.stat.distribution.beta: betaPDF; 45 | 46 | immutable T alpha = mu * kappa; 47 | immutable T beta = kappa - alpha; 48 | return betaPDF(x, alpha, beta); 49 | } 50 | 51 | /// 52 | version(mir_stat_test) 53 | @safe pure nothrow @nogc 54 | unittest { 55 | import mir.math.common: approxEqual; 56 | 57 | assert(0.5.betaProportionPDF(0.5, 2) == 1); 58 | assert(0.75.betaProportionPDF((1.0 / 3), 3).approxEqual(0.5)); 59 | assert(0.25.betaProportionPDF((1.0 / 9), 4.5).approxEqual(0.9228516)); 60 | } 61 | 62 | /++ 63 | Computes the beta proportion cumulatve distribution function (CDF). 64 | 65 | Params: 66 | x = value to evaluate CDF 67 | mu = shape parameter #1 68 | kappa = shape parameter #2 69 | 70 | See_also: 71 | $(LINK2 https://en.wikipedia.org/wiki/Beta_distribution, Beta Proportion Distribution), 72 | $(DISTREF beta, betaCDF) 73 | +/ 74 | @safe pure nothrow @nogc 75 | T betaProportionCDF(T)(const T x, const T mu, const T kappa) 76 | if (isFloatingPoint!T) 77 | in (x >= 0, "x must be greater than or equal to 0") 78 | in (x <= 1, "x must be less than or equal to 1") 79 | in (mu > 0, "mu must be greater than zero") 80 | in (mu < 1, "mu must be less than one") 81 | in (kappa > 0, "kappa must be greater than zero") 82 | { 83 | import mir.stat.distribution.beta: betaCDF; 84 | 85 | immutable T alpha = mu * kappa; 86 | immutable T beta = kappa - alpha; 87 | return betaCDF(x, alpha, beta); 88 | } 89 | 90 | /// 91 | version(mir_stat_test) 92 | @safe pure nothrow @nogc 93 | unittest { 94 | import mir.math.common: approxEqual; 95 | 96 | assert(0.5.betaProportionCDF(0.5, 2).approxEqual(0.5)); 97 | assert(0.75.betaProportionCDF((1.0 / 3), 3).approxEqual(0.9375)); 98 | assert(0.25.betaProportionCDF((1.0 / 9), 4.5).approxEqual(0.8588867)); 99 | } 100 | 101 | /++ 102 | Computes the beta proportion complementary cumulative distribution function (CCDF). 103 | 104 | Params: 105 | x = value to evaluate CCDF 106 | mu = shape parameter #1 107 | kappa = shape parameter #2 108 | 109 | See_also: 110 | $(LINK2 https://en.wikipedia.org/wiki/Beta_distribution, Beta Proportion Distribution), 111 | $(DISTREF beta, betaCDF) 112 | +/ 113 | @safe pure nothrow @nogc 114 | T betaProportionCCDF(T)(const T x, const T mu, const T kappa) 115 | if (isFloatingPoint!T) 116 | in (x >= 0, "x must be greater than or equal to 0") 117 | in (x <= 1, "x must be less than or equal to 1") 118 | in (mu > 0, "mu must be greater than zero") 119 | in (mu < 1, "mu must be less than one") 120 | in (kappa > 0, "kappa must be greater than zero") 121 | { 122 | import mir.stat.distribution.beta: betaCCDF; 123 | 124 | immutable T alpha = mu * kappa; 125 | immutable T beta = kappa - alpha; 126 | return betaCCDF(x, alpha, beta); 127 | } 128 | 129 | /// 130 | version(mir_stat_test) 131 | @safe pure nothrow @nogc 132 | unittest { 133 | import mir.math.common: approxEqual; 134 | 135 | assert(0.5.betaProportionCCDF(0.5, 2).approxEqual(0.5)); 136 | assert(0.75.betaProportionCCDF((1.0 / 3), 3).approxEqual(0.0625)); 137 | assert(0.25.betaProportionCCDF((1.0 / 9), 4.5).approxEqual(0.1411133)); 138 | } 139 | 140 | /++ 141 | Computes the beta proportion inverse cumulative distribution function (InvCDF). 142 | 143 | Params: 144 | p = value to evaluate InvCDF 145 | mu = shape parameter #1 146 | kappa = shape parameter #2 147 | 148 | See_also: 149 | $(LINK2 https://en.wikipedia.org/wiki/Beta_distribution, Beta Proportion Distribution), 150 | $(DISTREF beta, betaInvCDF) 151 | +/ 152 | @safe pure nothrow @nogc 153 | T betaProportionInvCDF(T)(const T p, const T mu, const T kappa) 154 | if (isFloatingPoint!T) 155 | in (p >= 0, "p must be greater than or equal to 0") 156 | in (p <= 1, "p must be less than or equal to 1") 157 | in (mu > 0, "mu must be greater than zero") 158 | in (mu < 1, "mu must be less than one") 159 | in (kappa > 0, "kappa must be greater than zero") 160 | { 161 | import mir.stat.distribution.beta: betaInvCDF; 162 | 163 | immutable T alpha = mu * kappa; 164 | immutable T beta = kappa - alpha; 165 | return betaInvCDF(p, alpha, beta); 166 | } 167 | 168 | /// 169 | version(mir_stat_test) 170 | @safe pure nothrow @nogc 171 | unittest { 172 | import mir.math.common: approxEqual; 173 | 174 | assert(0.5.betaProportionInvCDF(0.5, 2).approxEqual(0.5)); 175 | assert(0.9375.betaProportionInvCDF((1.0 / 3), 3).approxEqual(0.75)); 176 | assert(0.8588867.betaProportionInvCDF((1.0 / 9), 4.5).approxEqual(0.25)); 177 | } 178 | 179 | /++ 180 | Computes the beta proportion log probability density function (LPDF). 181 | 182 | Params: 183 | x = value to evaluate LPDF 184 | mu = shape parameter #1 185 | kappa = shape parameter #2 186 | 187 | See_also: 188 | $(LINK2 https://en.wikipedia.org/wiki/Beta_distribution, Beta Proportion Distribution), 189 | $(DISTREF beta, betaLPDF) 190 | +/ 191 | @safe pure nothrow @nogc 192 | T betaProportionLPDF(T)(const T x, const T mu, const T kappa) 193 | if (isFloatingPoint!T) 194 | in (x >= 0, "x must be greater than or equal to 0") 195 | in (x <= 1, "x must be less than or equal to 1") 196 | in (mu > 0, "mu must be greater than zero") 197 | in (mu < 1, "mu must be less than one") 198 | in (kappa > 0, "kappa must be greater than zero") 199 | { 200 | import mir.stat.distribution.beta: betaLPDF; 201 | 202 | immutable T alpha = mu * kappa; 203 | immutable T beta = kappa - alpha; 204 | return betaLPDF(x, alpha, beta); 205 | } 206 | 207 | /// 208 | version(mir_stat_test) 209 | @safe pure nothrow @nogc 210 | unittest { 211 | import mir.math.common: approxEqual, log; 212 | 213 | assert(0.5.betaProportionLPDF(0.5, 2).approxEqual(log(betaProportionPDF(0.5, 0.5, 2)))); 214 | assert(0.75.betaProportionLPDF((1.0 / 3), 3).approxEqual(log(betaProportionPDF(0.75, (1.0 / 3), 3)))); 215 | assert(0.25.betaProportionLPDF((1.0 / 9), 4.5).approxEqual(log(betaProportionPDF(0.25, (1.0 / 9), 4.5)))); 216 | } 217 | -------------------------------------------------------------------------------- /source/mir/stat/distribution/categorical.d: -------------------------------------------------------------------------------- 1 | /++ 2 | This module contains algorithms for the $(LINK2 https://en.wikipedia.org/wiki/Categorical_distribution, Categorical Distribution). 3 | 4 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 | 6 | Authors: John Michael Hall 7 | 8 | Copyright: 2023 Mir Stat Authors. 9 | 10 | +/ 11 | 12 | module mir.stat.distribution.categorical; 13 | 14 | import mir.algorithm.iteration: all; 15 | import mir.internal.utility: isFloatingPoint; 16 | import mir.math.common: approxEqual; 17 | import mir.math.sum: elementType, sumType, sum; 18 | import mir.ndslice.slice: Slice, SliceKind; 19 | import std.traits: CommonType; 20 | 21 | /++ 22 | Computes the Categorical probability mass function (PMF). 23 | 24 | Params: 25 | x = value to evaluate PMF 26 | p = slice containing the probability associated with the Categorical Distribution 27 | 28 | See_also: 29 | $(LINK2 https://en.wikipedia.org/wiki/Categorical_distribution, Categorical Distribution) 30 | +/ 31 | @safe pure nothrow @nogc 32 | elementType!(Slice!(Iterator, 1, kind)) categoricalPMF(Iterator, SliceKind kind)(const size_t x, scope const Slice!(Iterator, 1, kind) p) 33 | if (isFloatingPoint!(elementType!(Slice!(Iterator, 1, kind)))) 34 | in (x < p.length, "x must be less than the length of p") 35 | in (p.sum.approxEqual(1.0), "p must sum to 1") 36 | in (p.all!("a >= 0"), "p must be greater than or equal to 0") 37 | in (p.all!("a <= 1"), "p must be less than or equal to 1") 38 | { 39 | return p[x]; 40 | } 41 | 42 | /// ditto 43 | @safe pure nothrow @nogc 44 | T categoricalPMF(T)(const size_t x, scope const T[] p...) 45 | if (isFloatingPoint!T) 46 | in (x < p.length, "x must be less than the length of p") 47 | in (p.sum.approxEqual(1.0), "p must sum to 1") 48 | in (p.all!("a >= 0"), "p must be greater than or equal to 0") 49 | in (p.all!("a <= 1"), "p must be less than or equal to 1") 50 | { 51 | return p[x]; 52 | } 53 | 54 | /// 55 | version(mir_stat_test) 56 | @safe pure nothrow @nogc 57 | unittest 58 | { 59 | import mir.ndslice.slice: sliced; 60 | import mir.test: shouldApprox; 61 | 62 | static immutable x = [0.1, 0.5, 0.4]; 63 | auto p = x.sliced; 64 | 65 | 0.categoricalPMF(p).shouldApprox == 0.1; 66 | 1.categoricalPMF(p).shouldApprox == 0.5; 67 | 2.categoricalPMF(p).shouldApprox == 0.4; 68 | } 69 | 70 | /// Can also use dynamic array 71 | version(mir_stat_test) 72 | @safe pure nothrow 73 | unittest 74 | { 75 | import mir.test: shouldApprox; 76 | 77 | double[] p = [0.1, 0.5, 0.4]; 78 | 79 | 0.categoricalPMF(p).shouldApprox == 0.1; 80 | 1.categoricalPMF(p).shouldApprox == 0.5; 81 | 2.categoricalPMF(p).shouldApprox == 0.4; 82 | } 83 | 84 | /// 85 | version(mir_stat_test) 86 | @safe pure nothrow @nogc 87 | unittest 88 | { 89 | import mir.math.sum: sum; 90 | import mir.ndslice.slice: sliced; 91 | import mir.test: shouldApprox; 92 | 93 | static immutable x = [1.0, 5, 4]; 94 | auto p = x.sliced; 95 | auto q = p / sum(p); 96 | 97 | 0.categoricalPMF(q).shouldApprox == 0.1; 98 | 1.categoricalPMF(q).shouldApprox == 0.5; 99 | 2.categoricalPMF(q).shouldApprox == 0.4; 100 | } 101 | 102 | /++ 103 | Computes the Categorical cumulative distribution function (CDF). 104 | 105 | Params: 106 | x = value to evaluate CDF 107 | p = slice containing the probability associated with the Categorical Distribution 108 | 109 | See_also: 110 | $(LINK2 https://en.wikipedia.org/wiki/Categorical_distribution, Categorical Distribution) 111 | +/ 112 | @safe pure nothrow @nogc 113 | sumType!(Slice!(Iterator, 1, kind)) categoricalCDF(Iterator, SliceKind kind)(const size_t x, scope const Slice!(Iterator, 1, kind) p) 114 | if (isFloatingPoint!(elementType!(Slice!(Iterator, 1, kind)))) 115 | in (x < p.length, "x must be less than the length of p") 116 | in (p.sum.approxEqual(1.0), "p must sum to 1") 117 | in (p.all!("a >= 0"), "p must be greater than or equal to 0") 118 | in (p.all!("a <= 1"), "p must be less than or equal to 1") 119 | { 120 | return p[0 .. (x + 1)].sum; 121 | } 122 | 123 | /// ditto 124 | @safe pure nothrow @nogc 125 | T categoricalCDF(T)(const size_t x, scope const T[] p...) 126 | if (isFloatingPoint!T) 127 | in (x < p.length, "x must be less than the length of p") 128 | in (p.sum.approxEqual(1.0), "p must sum to 1") 129 | in (p.all!("a >= 0"), "p must be greater than or equal to 0") 130 | in (p.all!("a <= 1"), "p must be less than or equal to 1") 131 | { 132 | return p[0 .. (x + 1)].sum; 133 | } 134 | 135 | /// 136 | version(mir_stat_test) 137 | @safe pure nothrow @nogc 138 | unittest 139 | { 140 | import mir.ndslice.slice: sliced; 141 | import mir.test: shouldApprox; 142 | 143 | static immutable x = [0.1, 0.5, 0.4]; 144 | auto p = x.sliced; 145 | 146 | 0.categoricalCDF(p).shouldApprox == 0.1; 147 | 1.categoricalCDF(p).shouldApprox == 0.6; 148 | 2.categoricalCDF(p).shouldApprox == 1.0; 149 | } 150 | 151 | /// Can also use dynamic array 152 | version(mir_stat_test) 153 | @safe pure nothrow 154 | unittest 155 | { 156 | import mir.test: shouldApprox; 157 | 158 | double[] p = [0.1, 0.5, 0.4]; 159 | 160 | 0.categoricalCDF(p).shouldApprox == 0.1; 161 | 1.categoricalCDF(p).shouldApprox == 0.6; 162 | 2.categoricalCDF(p).shouldApprox == 1.0; 163 | } 164 | 165 | /++ 166 | Computes the Categorical complementary cumulative distribution function (CCDF). 167 | 168 | Params: 169 | x = value to evaluate CCDF 170 | p = slice containing the probability associated with the Categorical Distribution 171 | 172 | See_also: 173 | $(LINK2 https://en.wikipedia.org/wiki/Categorical_distribution, Categorical Distribution) 174 | +/ 175 | @safe pure nothrow @nogc 176 | sumType!(Slice!(Iterator, 1, kind)) categoricalCCDF(Iterator, SliceKind kind)(const size_t x, scope const Slice!(Iterator, 1, kind) p) 177 | if (isFloatingPoint!(elementType!(Slice!(Iterator, 1, kind)))) 178 | in (x < p.length, "x must be less than the length of p") 179 | in (p.sum.approxEqual(1.0), "p must sum to 1") 180 | in (p.all!("a >= 0"), "p must be greater than or equal to 0") 181 | in (p.all!("a <= 1"), "p must be less than or equal to 1") 182 | { 183 | return p[x .. $].sum; 184 | } 185 | 186 | /// ditto 187 | @safe pure nothrow @nogc 188 | T categoricalCCDF(T)(const size_t x, scope const T[] p...) 189 | if (isFloatingPoint!T) 190 | in (x < p.length, "x must be less than the length of p") 191 | in (p.sum.approxEqual(1.0), "p must sum to 1") 192 | in (p.all!("a >= 0"), "p must be greater than or equal to 0") 193 | in (p.all!("a <= 1"), "p must be less than or equal to 1") 194 | { 195 | return p[x .. $].sum; 196 | } 197 | 198 | /// 199 | version(mir_stat_test) 200 | @safe pure nothrow @nogc 201 | unittest 202 | { 203 | import mir.ndslice.slice: sliced; 204 | import mir.test: shouldApprox; 205 | 206 | static immutable x = [0.1, 0.5, 0.4]; 207 | auto p = x.sliced; 208 | 209 | 0.categoricalCCDF(p).shouldApprox == 1.0; 210 | 1.categoricalCCDF(p).shouldApprox == 0.9; 211 | 2.categoricalCCDF(p).shouldApprox == 0.4; 212 | } 213 | 214 | /// Can also use dynamic array 215 | version(mir_stat_test) 216 | @safe pure nothrow 217 | unittest 218 | { 219 | import mir.test: shouldApprox; 220 | 221 | double[] p = [0.1, 0.5, 0.4]; 222 | 223 | 0.categoricalCCDF(p).shouldApprox == 1.0; 224 | 1.categoricalCCDF(p).shouldApprox == 0.9; 225 | 2.categoricalCCDF(p).shouldApprox == 0.4; 226 | } 227 | 228 | /++ 229 | Computes the Categorical inverse cumulative distribution function (InvCDF). 230 | 231 | Params: 232 | q = value to evaluate InvCDF 233 | p = slice containing the probability associated with the Categorical Distribution 234 | 235 | See_also: 236 | $(LINK2 https://en.wikipedia.org/wiki/Categorical_distribution, Categorical Distribution) 237 | +/ 238 | @safe pure nothrow @nogc 239 | size_t categoricalInvCDF(T, Iterator, SliceKind kind)(const T q, scope const Slice!(Iterator, 1, kind) p) 240 | if (isFloatingPoint!(CommonType!(T, elementType!(Slice!(Iterator, 1, kind))))) 241 | in (q >= 0, "q must be greater than or equal to 0") 242 | in (q <= 1, "q must be less than or equal to 1") 243 | in (p.sum.approxEqual(1.0), "p must sum to 1") 244 | in (p.all!("a >= 0"), "p must be greater than or equal to 0") 245 | in (p.all!("a <= 1"), "p must be less than or equal to 1") 246 | { 247 | CommonType!(T, elementType!(typeof(p))) s = 0.0; 248 | size_t i; 249 | s += p[i]; 250 | while (q > s) { 251 | i++; 252 | s += p[i]; 253 | } 254 | return i;// this ensures categoricalInvCDF(a, p) == b, which is consistent with categoricalCDF(b, p) == a (similar to bernoulliInvCDF) 255 | } 256 | 257 | /// ditto 258 | @safe pure nothrow @nogc 259 | size_t categoricalInvCDF(T)(const T q, scope const T[] p...) 260 | if (isFloatingPoint!T) 261 | in (q >= 0, "q must be greater than or equal to 0") 262 | in (q <= 1, "q must be less than or equal to 1") 263 | in (p.sum.approxEqual(1.0), "p must sum to 1") 264 | in (p.all!("a >= 0"), "p must be greater than or equal to 0") 265 | in (p.all!("a <= 1"), "p must be less than or equal to 1") 266 | { 267 | T s = 0.0; 268 | size_t i; 269 | s += p[i]; 270 | while (q > s) { 271 | i++; 272 | s += p[i]; 273 | } 274 | return i;// this ensures categoricalInvCDF(a, p) == b, which is consistent with categoricalCDF(b, p) == a (similar to bernoulliInvCDF) 275 | } 276 | 277 | /// 278 | version(mir_stat_test) 279 | @safe pure nothrow @nogc 280 | unittest 281 | { 282 | import mir.ndslice.slice: sliced; 283 | import mir.test: should; 284 | 285 | static immutable x = [0.1, 0.5, 0.4]; 286 | auto p = x.sliced; 287 | 288 | categoricalInvCDF(0.0, p).should == 0; 289 | categoricalInvCDF(0.1, p).should == 0; 290 | categoricalInvCDF(0.2, p).should == 1; 291 | categoricalInvCDF(0.3, p).should == 1; 292 | categoricalInvCDF(0.4, p).should == 1; 293 | categoricalInvCDF(0.5, p).should == 1; 294 | categoricalInvCDF(0.6, p).should == 1; 295 | categoricalInvCDF(0.7, p).should == 2; 296 | categoricalInvCDF(0.8, p).should == 2; 297 | categoricalInvCDF(0.9, p).should == 2; 298 | categoricalInvCDF(1.0, p).should == 2; 299 | } 300 | 301 | /// Can also use dynamic array 302 | version(mir_stat_test) 303 | @safe pure nothrow 304 | unittest 305 | { 306 | import mir.test: should; 307 | 308 | double[] p = [0.1, 0.5, 0.4]; 309 | 310 | categoricalInvCDF(0.5, p).should == 1; 311 | } 312 | 313 | /++ 314 | Computes the Categorical log probability mass function (LPMF). 315 | 316 | Params: 317 | x = value to evaluate LPMF 318 | p = slice containing the probability associated with the Categorical Distribution 319 | 320 | See_also: 321 | $(LINK2 https://en.wikipedia.org/wiki/Categorical_distribution, Categorical Distribution) 322 | +/ 323 | @safe pure nothrow @nogc 324 | elementType!(Slice!(Iterator, 1, kind)) categoricalLPMF(Iterator, SliceKind kind)(const size_t x, scope const Slice!(Iterator, 1, kind) p) 325 | if (isFloatingPoint!(elementType!(Slice!(Iterator, 1, kind)))) 326 | in (x < p.length, "x must be less than the length of p") 327 | in (p.sum.approxEqual(1.0), "p must sum to 1") 328 | in (p.all!("a >= 0"), "p must be greater than or equal to 0") 329 | in (p.all!("a <= 1"), "p must be less than or equal to 1") 330 | { 331 | import mir.math.common: log; 332 | return x.categoricalPMF(p).log; 333 | } 334 | 335 | /// ditto 336 | @safe pure nothrow @nogc 337 | T categoricalLPMF(T)(const size_t x, scope const T[] p...) 338 | if (isFloatingPoint!T) 339 | in (x < p.length, "x must be less than the length of p") 340 | in (p.sum.approxEqual(1.0), "p must sum to 1") 341 | in (p.all!("a >= 0"), "p must be greater than or equal to 0") 342 | in (p.all!("a <= 1"), "p must be less than or equal to 1") 343 | { 344 | import mir.math.common: log; 345 | return x.categoricalPMF(p).log; 346 | } 347 | 348 | /// 349 | version(mir_stat_test) 350 | @safe pure nothrow @nogc 351 | unittest 352 | { 353 | import mir.math.common: log; 354 | import mir.ndslice.slice: sliced; 355 | import mir.test: shouldApprox; 356 | 357 | static immutable x = [0.1, 0.5, 0.4]; 358 | auto p = x.sliced; 359 | 360 | 0.categoricalLPMF(p).shouldApprox == log(0.1); 361 | 1.categoricalLPMF(p).shouldApprox == log(0.5); 362 | 2.categoricalLPMF(p).shouldApprox == log(0.4); 363 | } 364 | 365 | /// Can also use dynamic array 366 | version(mir_stat_test) 367 | @safe pure nothrow 368 | unittest 369 | { 370 | import mir.math.common: log; 371 | import mir.test: shouldApprox; 372 | 373 | double[] p = [0.1, 0.5, 0.4]; 374 | 375 | 0.categoricalLPMF(p).shouldApprox == log(0.1); 376 | 1.categoricalLPMF(p).shouldApprox == log(0.5); 377 | 2.categoricalLPMF(p).shouldApprox == log(0.4); 378 | } 379 | -------------------------------------------------------------------------------- /source/mir/stat/distribution/cauchy.d: -------------------------------------------------------------------------------- 1 | /++ 2 | This module contains algorithms for the $(LINK2 https://en.wikipedia.org/wiki/Cauchy_distribution, Cauchy Distribution). 3 | 4 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 | 6 | Authors: John Michael Hall 7 | 8 | Copyright: 2023 Mir Stat Authors. 9 | 10 | +/ 11 | 12 | module mir.stat.distribution.cauchy; 13 | 14 | import mir.internal.utility: isFloatingPoint; 15 | 16 | /++ 17 | Computes the Cauchy probability density function (PDF). 18 | 19 | Params: 20 | x = value to evaluate PDF 21 | 22 | See_also: 23 | $(LINK2 https://en.wikipedia.org/wiki/Cauchy_distribution, Cauchy Distribution) 24 | +/ 25 | @safe pure nothrow @nogc 26 | T cauchyPDF(T)(const T x) 27 | if (isFloatingPoint!T) 28 | { 29 | import mir.math.constant: M_1_PI; 30 | 31 | return T(M_1_PI) / (1 + x * x); 32 | } 33 | 34 | /++ 35 | Ditto, with location and scale parameters (by standardizing `x`). 36 | 37 | Params: 38 | x = value to evaluate PDF 39 | location = location parameter 40 | scale = scale parameter 41 | +/ 42 | @safe pure nothrow @nogc 43 | T cauchyPDF(T)(const T x, const T location, const T scale) 44 | if (isFloatingPoint!T) 45 | in (scale > 0, "scale must be greater than zero") 46 | { 47 | return cauchyPDF((x - location) / scale) / scale; 48 | } 49 | 50 | /// 51 | version(mir_stat_test) 52 | @safe pure nothrow @nogc 53 | unittest { 54 | import mir.test: shouldApprox; 55 | 56 | cauchyPDF(-3.0).shouldApprox == 0.03183099; 57 | cauchyPDF(-2.0).shouldApprox == 0.06366198; 58 | cauchyPDF(-1.0).shouldApprox == 0.1591549; 59 | cauchyPDF(0.0).shouldApprox == 0.3183099; 60 | cauchyPDF(1.0).shouldApprox == 0.1591549; 61 | cauchyPDF(2.0).shouldApprox == 0.06366198; 62 | cauchyPDF(3.0).shouldApprox == 0.03183099; 63 | 64 | // Can include location/scale 65 | cauchyPDF(-3.0, 1, 2).shouldApprox == 0.03183099; 66 | cauchyPDF(-2.0, 1, 2).shouldApprox == 0.04897075; 67 | cauchyPDF(-1.0, 1, 2).shouldApprox == 0.07957747; 68 | cauchyPDF(0.0, 1, 2).shouldApprox == 0.127324; 69 | cauchyPDF(1.0, 1, 2).shouldApprox == 0.1591549; 70 | cauchyPDF(2.0, 1, 2).shouldApprox == 0.127324; 71 | cauchyPDF(3.0, 1, 2).shouldApprox == 0.07957747; 72 | } 73 | 74 | /++ 75 | Computes the Cauchy cumulative distribution function (CDF). 76 | 77 | Params: 78 | x = value to evaluate CDF 79 | 80 | See_also: 81 | $(LINK2 https://en.wikipedia.org/wiki/Cauchy_distribution, Cauchy Distribution) 82 | +/ 83 | @safe pure nothrow @nogc 84 | T cauchyCDF(T)(const T x) 85 | if (isFloatingPoint!T) 86 | { 87 | import mir.math.constant: M_1_PI; 88 | import std.math.trigonometry: atan; 89 | 90 | return 0.5 + T(M_1_PI) * atan(x); 91 | } 92 | 93 | /++ 94 | Ditto, with location and scale parameters (by standardizing `x`). 95 | 96 | Params: 97 | x = value to evaluate CDF 98 | location = location parameter 99 | scale = scale parameter 100 | +/ 101 | @safe pure nothrow @nogc 102 | T cauchyCDF(T)(const T x, const T location, const T scale) 103 | if (isFloatingPoint!T) 104 | in (scale > 0, "scale must be greater than zero") 105 | { 106 | return cauchyCDF((x - location) / scale); 107 | } 108 | 109 | /// 110 | version(mir_stat_test) 111 | @safe pure nothrow @nogc 112 | unittest { 113 | import mir.test: shouldApprox; 114 | 115 | cauchyCDF(-3.0).shouldApprox == 0.1024164; 116 | cauchyCDF(-2.0).shouldApprox == 0.1475836; 117 | cauchyCDF(-1.0).shouldApprox == 0.25; 118 | cauchyCDF(0.0).shouldApprox == 0.5; 119 | cauchyCDF(1.0).shouldApprox == 0.75; 120 | cauchyCDF(2.0).shouldApprox == 0.8524164; 121 | cauchyCDF(3.0).shouldApprox == 0.8975836; 122 | 123 | // Can include location/scale 124 | cauchyCDF(-3.0, 1, 2).shouldApprox == 0.1475836; 125 | cauchyCDF(-2.0, 1, 2).shouldApprox == 0.187167; 126 | cauchyCDF(-1.0, 1, 2).shouldApprox == 0.25; 127 | cauchyCDF(0.0, 1, 2).shouldApprox == 0.3524164; 128 | cauchyCDF(1.0, 1, 2).shouldApprox == 0.5; 129 | cauchyCDF(2.0, 1, 2).shouldApprox == 0.6475836; 130 | cauchyCDF(3.0, 1, 2).shouldApprox == 0.75; 131 | } 132 | 133 | /++ 134 | Computes the Cauchy complementary cumulative distribution function (CCDF). 135 | 136 | Params: 137 | x = value to evaluate CCDF 138 | 139 | See_also: 140 | $(LINK2 https://en.wikipedia.org/wiki/Cauchy_distribution, Cauchy Distribution) 141 | +/ 142 | @safe pure nothrow @nogc 143 | T cauchyCCDF(T)(const T x) 144 | if (isFloatingPoint!T) 145 | { 146 | return cauchyCDF(-x); 147 | } 148 | 149 | /++ 150 | Ditto, with location and scale parameters (by standardizing `x`). 151 | 152 | Params: 153 | x = value to evaluate CCDF 154 | location = location parameter 155 | scale = scale parameter 156 | +/ 157 | @safe pure nothrow @nogc 158 | T cauchyCCDF(T)(const T x, const T location, const T scale) 159 | if (isFloatingPoint!T) 160 | in (scale > 0, "scale must be greater than zero") 161 | { 162 | return cauchyCDF((location - x) / scale); 163 | } 164 | 165 | /// 166 | version(mir_stat_test) 167 | @safe pure nothrow @nogc 168 | unittest { 169 | import mir.test: shouldApprox; 170 | 171 | cauchyCCDF(-3.0).shouldApprox == 0.8975836; 172 | cauchyCCDF(-2.0).shouldApprox == 0.8524164; 173 | cauchyCCDF(-1.0).shouldApprox == 0.75; 174 | cauchyCCDF(0.0).shouldApprox == 0.5; 175 | cauchyCCDF(1.0).shouldApprox == 0.25; 176 | cauchyCCDF(2.0).shouldApprox == 0.1475836; 177 | cauchyCCDF(3.0).shouldApprox == 0.1024164; 178 | 179 | // Can include location/scale 180 | cauchyCCDF(-3.0, 1, 2).shouldApprox == 0.8524164; 181 | cauchyCCDF(-2.0, 1, 2).shouldApprox == 0.812833; 182 | cauchyCCDF(-1.0, 1, 2).shouldApprox == 0.75; 183 | cauchyCCDF(0.0, 1, 2).shouldApprox == 0.6475836; 184 | cauchyCCDF(1.0, 1, 2).shouldApprox == 0.5; 185 | cauchyCCDF(2.0, 1, 2).shouldApprox == 0.3524164; 186 | cauchyCCDF(3.0, 1, 2).shouldApprox == 0.25; 187 | } 188 | 189 | /++ 190 | Computes the Cauchy inverse cumulative distribution function (InvCDF). 191 | 192 | Params: 193 | p = value to evaluate InvCDF 194 | 195 | See_also: 196 | $(LINK2 https://en.wikipedia.org/wiki/Cauchy_distribution, Cauchy Distribution) 197 | +/ 198 | @safe pure nothrow @nogc 199 | T cauchyInvCDF(T)(const T p) 200 | if (isFloatingPoint!T) 201 | in (p >= 0, "p must be greater than or equal to 0") 202 | in (p <= 1, "p must be less than or equal to 1") 203 | { 204 | import mir.math.constant: PI; 205 | import std.math.trigonometry: tan; 206 | 207 | if (p > 0 && p < 1) { 208 | return tan(T(PI) * (p - 0.5)); 209 | } else if (p == 0) { 210 | return -T.infinity; 211 | } else if (p == 1) { 212 | return T.infinity; 213 | } 214 | assert(0, "Should not be here"); 215 | } 216 | 217 | /++ 218 | Ditto, with location and scale parameters. 219 | 220 | Params: 221 | p = value to evaluate InvCDF 222 | location = location parameter 223 | scale = scale parameter 224 | +/ 225 | @safe pure nothrow @nogc 226 | T cauchyInvCDF(T)(const T p, const T location, const T scale) 227 | if (isFloatingPoint!T) 228 | in (p >= 0, "p must be greater than or equal to 0") 229 | in (p <= 1, "p must be less than or equal to 1") 230 | in (scale > 0, "scale must be greater than zero") 231 | { 232 | return location + scale * cauchyInvCDF(p); 233 | } 234 | 235 | /// 236 | version(mir_stat_test) 237 | @safe pure nothrow @nogc 238 | unittest { 239 | import mir.test: shouldApprox; 240 | 241 | cauchyInvCDF(0.0).shouldApprox == -double.infinity; 242 | cauchyInvCDF(0.1).shouldApprox == -3.077684; 243 | cauchyInvCDF(0.2).shouldApprox == -1.376382; 244 | cauchyInvCDF(0.3).shouldApprox == -0.7265425; 245 | cauchyInvCDF(0.4).shouldApprox == -0.3249197; 246 | cauchyInvCDF(0.5).shouldApprox == 0.0; 247 | cauchyInvCDF(0.6).shouldApprox == 0.3249197; 248 | cauchyInvCDF(0.7).shouldApprox == 0.7265425; 249 | cauchyInvCDF(0.8).shouldApprox == 1.376382; 250 | cauchyInvCDF(0.9).shouldApprox == 3.077684; 251 | cauchyInvCDF(1.0).shouldApprox == double.infinity; 252 | 253 | // Can include location/scale 254 | cauchyInvCDF(0.2, 1, 2).shouldApprox == -1.752764; 255 | cauchyInvCDF(0.4, 1, 2).shouldApprox == 0.3501606; 256 | cauchyInvCDF(0.6, 1, 2).shouldApprox == 1.649839; 257 | cauchyInvCDF(0.8, 1, 2).shouldApprox == 3.752764; 258 | } 259 | 260 | /++ 261 | Computes the Cauchy log probability density function (LPDF). 262 | 263 | Params: 264 | x = value to evaluate LPDF 265 | 266 | See_also: 267 | $(LINK2 https://en.wikipedia.org/wiki/Cauchy_distribution, Cauchy Distribution) 268 | +/ 269 | @safe pure nothrow @nogc 270 | T cauchyLPDF(T)(const T x) 271 | if (isFloatingPoint!T) 272 | { 273 | import mir.math.common: log; 274 | import mir.stat.constant: LOGPI; 275 | 276 | return -T(LOGPI) - log(1 + x * x); 277 | } 278 | 279 | /++ 280 | Ditto, with location and scale parameters (by standardizing `x`). 281 | 282 | Params: 283 | x = value to evaluate LPDF 284 | location = location parameter 285 | scale = scale parameter 286 | +/ 287 | @safe pure nothrow @nogc 288 | T cauchyLPDF(T)(const T x, const T location, const T scale) 289 | if (isFloatingPoint!T) 290 | in (scale > 0, "scale must be greater than zero") 291 | { 292 | import mir.math.common: log; 293 | 294 | return cauchyLPDF((x - location) / scale) - log(scale); 295 | } 296 | 297 | /// 298 | version(mir_stat_test) 299 | @safe pure nothrow @nogc 300 | unittest { 301 | import mir.math.common: log; 302 | import mir.test: shouldApprox; 303 | 304 | cauchyLPDF(-3.0).shouldApprox == log(0.03183099); 305 | cauchyLPDF(-2.0).shouldApprox == log(0.06366198); 306 | cauchyLPDF(-1.0).shouldApprox == log(0.1591549); 307 | cauchyLPDF(0.0).shouldApprox == log(0.3183099); 308 | cauchyLPDF(1.0).shouldApprox == log(0.1591549); 309 | cauchyLPDF(2.0).shouldApprox == log(0.06366198); 310 | cauchyLPDF(3.0).shouldApprox == log(0.03183099); 311 | 312 | // Can include location/scale 313 | cauchyLPDF(-3.0, 1, 2).shouldApprox == log(0.03183099); 314 | cauchyLPDF(-2.0, 1, 2).shouldApprox == log(0.04897075); 315 | cauchyLPDF(-1.0, 1, 2).shouldApprox == log(0.07957747); 316 | cauchyLPDF(0.0, 1, 2).shouldApprox == log(0.127324); 317 | cauchyLPDF(1.0, 1, 2).shouldApprox == log(0.1591549); 318 | cauchyLPDF(2.0, 1, 2).shouldApprox == log(0.127324); 319 | cauchyLPDF(3.0, 1, 2).shouldApprox == log(0.07957747); 320 | } 321 | -------------------------------------------------------------------------------- /source/mir/stat/distribution/cdf.d: -------------------------------------------------------------------------------- 1 | /++ 2 | This package publicly imports `mir.stat.distribution.*CDF` modules. 3 | 4 | $(BOOKTABLE , 5 |     $(TR 6 |         $(TH Functions) 7 |         $(TH Description) 8 |     ) 9 |     $(LEADINGROW Univariate Discrete Distributions) 10 |     $(TR $(TDNW $(SUBREF bernoulli, bernoulliCDF)) $(TD Bernoulli CDF )) 11 |     $(TR $(TDNW $(SUBREF binomial, binomialCDF)) $(TD Binomial CDF )) 12 |     $(TR $(TDNW $(SUBREF geometric, geometricCDF)) $(TD Geometric CDF )) 13 |     $(TR $(TDNW $(SUBREF hypergeometric, hypergeometricCDF)) $(TD Hypergeometric CDF )) 14 |     $(TR $(TDNW $(SUBREF negative_binomial, negativeBinomialCDF)) $(TD Negative Binomial CDF )) 15 |     $(TR $(TDNW $(SUBREF poisson, poissonCDF)) $(TD Poisson CDF )) 16 |     $(TR $(TDNW $(SUBREF uniform_discrete, uniformDiscreteCDF)) $(TD Discrete Uniform CDF )) 17 |     $(LEADINGROW Univariate Continuous Distributions) 18 |     $(TR $(TDNW $(SUBREF beta, betaCDF)) $(TD Beta CDF )) 19 |     $(TR $(TDNW $(SUBREF beta_proportion, betaProportionCDF)) $(TD Beta Proportion CDF )) 20 |     $(TR $(TDNW $(SUBREF cauchy, cauchyCDF)) $(TD Cauchy CDF )) 21 |     $(TR $(TDNW $(SUBREF chi2, chi2CDF)) $(TD Chi-squared CDF )) 22 |     $(TR $(TDNW $(SUBREF exponential, exponentialCDF)) $(TD Exponential CDF )) 23 |     $(TR $(TDNW $(SUBREF f, fCDF)) $(TD F CDF )) 24 |     $(TR $(TDNW $(SUBREF gamma, gammaCDF)) $(TD Gamma CDF )) 25 |     $(TR $(TDNW $(SUBREF generalized_pareto, generalizedParetoCDF)) $(TD Generalized Pareto CDF )) 26 |     $(TR $(TDNW $(SUBREF gev, gevCDF)) $(TD Generalized Extreme Value (GEV) CDF )) 27 |     $(TR $(TDNW $(SUBREF laplace, laplaceCDF)) $(TD Laplace CDF )) 28 |     $(TR $(TDNW $(SUBREF log_normal, logNormalCDF)) $(TD Log-normal CDF )) 29 |     $(TR $(TDNW $(SUBREF logistic, logisticCDF)) $(TD Logistic CDF )) 30 |     $(TR $(TDNW $(SUBREF normal, normalCDF)) $(TD Normal CDF )) 31 |     $(TR $(TDNW $(SUBREF pareto, paretoCDF)) $(TD Pareto CDF )) 32 |     $(TR $(TDNW $(SUBREF rayleigh, rayleighCDF)) $(TD Rayleigh CDF )) 33 |     $(TR $(TDNW $(SUBREF students_t, studentsTCDF)) $(TD Student's t CDF )) 34 |     $(TR $(TDNW $(SUBREF uniform, uniformCDF)) $(TD Continuous Uniform CDF )) 35 |     $(TR $(TDNW $(SUBREF weibull, weibullCDF)) $(TD Weibull CDF )) 36 |     $(LEADINGROW Multivariate Distributions) 37 |     $(TR $(TDNW $(SUBREF categorical, categoricalCDF)) $(TD Categorical CDF )) 38 | ) 39 | 40 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 41 | 42 | Authors: John Michael Hall, Ilya Yaroshenko 43 | 44 | Copyright: 2022-3 Mir Stat Authors. 45 | 46 | Macros: 47 | SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, stat, distribution, $1)$(NBSP) 48 | 49 | +/ 50 | 51 | module mir.stat.distribution.cdf; 52 | 53 | /// 54 | public import mir.stat.distribution.bernoulli: bernoulliCDF; 55 | /// 56 | public import mir.stat.distribution.beta: betaCDF; 57 | /// 58 | public import mir.stat.distribution.beta_proportion: betaProportionCDF; 59 | /// 60 | public import mir.stat.distribution.binomial: binomialCDF; 61 | /// 62 | public import mir.stat.distribution.categorical: categoricalCDF; 63 | /// 64 | public import mir.stat.distribution.cauchy: cauchyCDF; 65 | /// 66 | public import mir.stat.distribution.chi2: chi2CDF; 67 | /// 68 | public import mir.stat.distribution.exponential: exponentialCDF; 69 | /// 70 | public import mir.stat.distribution.f: fCDF; 71 | /// 72 | public import mir.stat.distribution.gamma: gammaCDF; 73 | /// 74 | public import mir.stat.distribution.generalized_pareto: generalizedParetoCDF; 75 | /// 76 | public import mir.stat.distribution.geometric: geometricCDF; 77 | /// 78 | public import mir.stat.distribution.gev: gevCDF; 79 | /// 80 | public import mir.stat.distribution.hypergeometric: hypergeometricCDF; 81 | /// 82 | public import mir.stat.distribution.laplace: laplaceCDF; 83 | /// 84 | public import mir.stat.distribution.log_normal: logNormalCDF; 85 | /// 86 | public import mir.stat.distribution.logistic: logisticCDF; 87 | /// 88 | public import mir.stat.distribution.negative_binomial: negativeBinomialCDF; 89 | /// 90 | public import mir.stat.distribution.normal: normalCDF; 91 | /// 92 | public import mir.stat.distribution.pareto: paretoCDF; 93 | /// 94 | public import mir.stat.distribution.poisson: poissonCDF; 95 | /// 96 | public import mir.stat.distribution.rayleigh: rayleighCDF; 97 | /// 98 | public import mir.stat.distribution.students_t: studentsTCDF; 99 | /// 100 | public import mir.stat.distribution.uniform: uniformCDF; 101 | /// 102 | public import mir.stat.distribution.uniform_discrete: uniformDiscreteCDF; 103 | /// 104 | public import mir.stat.distribution.weibull: weibullCDF; 105 | -------------------------------------------------------------------------------- /source/mir/stat/distribution/chi2.d: -------------------------------------------------------------------------------- 1 | /++ 2 | This module contains algorithms for the $(LINK2 https://en.wikipedia.org/wiki/Chi-squared_distribution, Chi-squared Distribution). 3 | 4 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 | 6 | Authors: Ilia Ki, John Michael Hall 7 | 8 | Copyright: 2022-3 Mir Stat Authors. 9 | 10 | +/ 11 | module mir.stat.distribution.chi2; 12 | 13 | import mir.internal.utility: isFloatingPoint; 14 | 15 | /++ 16 | Computes the Chi-squared probability density function (PDF). 17 | 18 | Params: 19 | x = value to evaluate PDF 20 | k = degrees of freedom 21 | 22 | See_also: 23 | $(LINK2 https://en.wikipedia.org/wiki/Chi-squared_distribution, Chi-squared Distribution) 24 | +/ 25 | @safe pure nothrow @nogc 26 | T chi2PDF(T)(const T x, const uint k) 27 | if (isFloatingPoint!T) 28 | in (x >= 0, "x must be greater than or equal to 0") 29 | in (k >= 1, "k must be greater than or equal to 1") 30 | { 31 | import mir.stat.distribution.gamma: gammaPDF; 32 | 33 | return gammaPDF(x, T(k) * 0.5f, 2); 34 | } 35 | 36 | /// 37 | version(mir_stat_test) 38 | @safe pure nothrow @nogc 39 | unittest 40 | { 41 | import mir.test: shouldApprox; 42 | 0.2.chi2PDF(2).shouldApprox == 0.4524187; 43 | } 44 | 45 | /++ 46 | Computes the Chi-squared cumulative distribution function (CDF). 47 | 48 | Params: 49 | x = value to evaluate CDF 50 | k = degrees of freedom 51 | 52 | See_also: 53 | $(LINK2 https://en.wikipedia.org/wiki/Chi-squared_distribution, Chi-squared Distribution) 54 | +/ 55 | @safe pure nothrow @nogc 56 | T chi2CDF(T)(const T x, const uint k) 57 | if (isFloatingPoint!T) 58 | in (x >= 0, "x must be greater than or equal to 0") 59 | in (k >= 1, "k must be greater than or equal to 1") 60 | { 61 | import mir.stat.distribution.gamma: gammaCDF; 62 | 63 | return gammaCDF(x, T(k) * 0.5f, 2); 64 | } 65 | 66 | /// 67 | version(mir_stat_test) 68 | @safe pure nothrow @nogc 69 | unittest 70 | { 71 | import mir.test: shouldApprox; 72 | 0.2.chi2CDF(2).shouldApprox == 0.09516258; 73 | } 74 | 75 | /++ 76 | Computes the Chi-squared complementary cumulative distribution function (CCDF). 77 | 78 | Params: 79 | x = value to evaluate CCDF 80 | k = degrees of freedom 81 | 82 | See_also: 83 | $(LINK2 https://en.wikipedia.org/wiki/Chi-squared_distribution, Chi-squared Distribution) 84 | +/ 85 | @safe pure nothrow @nogc 86 | T chi2CCDF(T)(const T x, const uint k) 87 | if (isFloatingPoint!T) 88 | in (x >= 0, "x must be greater than or equal to 0") 89 | in (k >= 1, "k must be greater than or equal to 1") 90 | { 91 | import mir.stat.distribution.gamma: gammaCCDF; 92 | 93 | return gammaCCDF(x, T(k) * 0.5f , 2); 94 | } 95 | 96 | /// 97 | version(mir_stat_test) 98 | @safe pure nothrow @nogc 99 | unittest 100 | { 101 | import mir.test: shouldApprox; 102 | 0.2.chi2CCDF(2).shouldApprox == 0.9048374; 103 | } 104 | 105 | /++ 106 | Computes the Chi-squared inverse cumulative distribution function (InvCDF). 107 | 108 | Params: 109 | p = value to evaluate InvCDF 110 | k = degrees of freedom 111 | 112 | See_also: 113 | $(LINK2 https://en.wikipedia.org/wiki/Chi-squared_distribution, Chi-squared Distribution) 114 | +/ 115 | @safe pure nothrow @nogc 116 | T chi2InvCDF(T)(const T p, const uint k) 117 | if (isFloatingPoint!T) 118 | in (p >= 0, "p must be greater than or equal to 0") 119 | in (p <= 1, "p must be less than or equal to 1") 120 | in (k >= 1, "k must be greater than or equal to 1") 121 | { 122 | import mir.stat.distribution.gamma: gammaInvCDF; 123 | 124 | return gammaInvCDF(p, T(k) * 0.5f, 2); 125 | } 126 | 127 | /// 128 | version(mir_stat_test) 129 | @safe pure nothrow @nogc 130 | unittest 131 | { 132 | import mir.test: shouldApprox; 133 | 0.09516258.chi2InvCDF(2).shouldApprox == 0.2; 134 | } 135 | 136 | /++ 137 | Computes the Chi-squared probability density function (LPDF). 138 | 139 | Params: 140 | x = value to evaluate LPDF 141 | k = degrees of freedom 142 | 143 | See_also: 144 | $(LINK2 https://en.wikipedia.org/wiki/Chi-squared_distribution, Chi-squared Distribution) 145 | +/ 146 | @safe pure nothrow @nogc 147 | T chi2LPDF(T)(const T x, const uint k) 148 | if (isFloatingPoint!T) 149 | in (x >= 0, "x must be greater than or equal to 0") 150 | in (k >= 1, "k must be greater than or equal to 1") 151 | { 152 | import mir.math.common: log; 153 | import mir.math.constant: LN2; 154 | import std.mathspecial: logGamma; 155 | 156 | if (x == 0) { 157 | if (k > 2) { 158 | return -T.infinity; 159 | } else if (k < 2) { 160 | return T.infinity; 161 | } else { 162 | return -T(LN2); 163 | } 164 | } 165 | 166 | return (T(k) * 0.5f - 1) * (log(x) - T(LN2)) - x *0.5f - cast(T) logGamma(T(k) * 0.5f) - T(LN2); 167 | } 168 | 169 | /// 170 | version(mir_stat_test) 171 | @safe pure nothrow @nogc 172 | unittest 173 | { 174 | import mir.test: shouldApprox; 175 | 0.2.chi2LPDF(2).shouldApprox == -0.7931472; 176 | } 177 | 178 | // 179 | version(mir_stat_test) 180 | @safe pure nothrow @nogc 181 | unittest 182 | { 183 | import mir.test: shouldApprox; 184 | 0.0.chi2LPDF(2).shouldApprox == -0.6931472; 185 | 0.0.chi2LPDF(1).shouldApprox == double.infinity; 186 | 0.0.chi2LPDF(3).shouldApprox == -double.infinity; 187 | } 188 | -------------------------------------------------------------------------------- /source/mir/stat/distribution/cornish_fisher.d: -------------------------------------------------------------------------------- 1 | /++ 2 | This module contains algorithms for the $(LINK2 https://en.wikipedia.org/wiki/Cornish%E2%80%93Fisher_expansion, Cornish-Fisher Expansion). 3 | 4 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 | 6 | Authors: John Michael Hall 7 | 8 | Copyright: 2022-3 Mir Stat Authors. 9 | 10 | +/ 11 | 12 | module mir.stat.distribution.cornish_fisher; 13 | 14 | import mir.internal.utility: isFloatingPoint; 15 | 16 | /++ 17 | Approximates the inverse CDF of a continuous distribution using the Cornish-Fisher expansion. 18 | 19 | It is generally recommended to only use the Cornish-Fisher expansion with 20 | distributions that are similar to the normal distribution. Extreme values of 21 | `skewness` or `excessKurtosis` can result in poorer approximations. 22 | 23 | Params: 24 | p = quantile to calculate inverse CDF 25 | mu = mean 26 | std = standard deviation 27 | skewness = skewness 28 | excessKurtosis = excess kurtosis (kurtosis - 3) 29 | 30 | See_also: 31 | $(LINK2 https://en.wikipedia.org/wiki/Cornish%E2%80%93Fisher_expansion, Cornish-Fisher Expansion) 32 | +/ 33 | T cornishFisherInvCDF(T)(const T p, const T mu, const T std, const T skewness, const T excessKurtosis) 34 | if (isFloatingPoint!T) 35 | { 36 | return mu + std * cornishFisherInvCDF(p, skewness, excessKurtosis); 37 | } 38 | 39 | /// 40 | version(mir_stat_test) 41 | @safe pure @nogc nothrow 42 | unittest { 43 | import mir.test: shouldApprox; 44 | 45 | 0.99.cornishFisherInvCDF(0, 1, 0.1, 1).shouldApprox == 2.629904; 46 | 0.99.cornishFisherInvCDF(0.1, 0.2, 0.1, 1).shouldApprox == 0.6259808; 47 | } 48 | 49 | /++ 50 | Ditto, but assumes mu = 0 and std = 1 51 | 52 | Params: 53 | p = quantile to calculate inverse CDF 54 | skewness = skewness (default = 0) 55 | excessKurtosis = excess kurtosis (kurtosis - 3) (default = 0) 56 | +/ 57 | T cornishFisherInvCDF(T)(const T p, const T skewness = 0, const T excessKurtosis = 0) 58 | if (isFloatingPoint!T) 59 | { 60 | import mir.stat.distribution.normal: normalInvCDF; 61 | 62 | T x = normalInvCDF(p); 63 | return x.cornishFisherInvCDFImpl(skewness, excessKurtosis); 64 | } 65 | 66 | /// 67 | version(mir_stat_test) 68 | @safe pure @nogc nothrow 69 | unittest { 70 | import mir.test: shouldApprox; 71 | 72 | 0.5.cornishFisherInvCDF.shouldApprox == 0; 73 | 0.5.cornishFisherInvCDF(1).shouldApprox == -0.1666667; 74 | 0.5.cornishFisherInvCDF(-1, 0).shouldApprox == 0.1666667; 75 | 76 | 0.9.cornishFisherInvCDF(0.1, 0).shouldApprox == 1.292868; 77 | 0.9.cornishFisherInvCDF(0.1, 1).shouldApprox == 1.220374; 78 | 0.9.cornishFisherInvCDF(0.1, -1).shouldApprox == 1.365363; 79 | 80 | 0.99.cornishFisherInvCDF(0.1, 0).shouldApprox == 2.396116; 81 | 0.99.cornishFisherInvCDF(0.1, 1).shouldApprox == 2.629904; 82 | 0.99.cornishFisherInvCDF(0.1, -1).shouldApprox == 2.162328; 83 | 84 | 0.01.cornishFisherInvCDF(0.1, 0).shouldApprox == -2.249053 ; 85 | 0.01.cornishFisherInvCDF(0.1, 1).shouldApprox == -2.482841; 86 | 0.01.cornishFisherInvCDF(0.1, -1).shouldApprox == -2.015265; 87 | } 88 | 89 | package(mir.stat) 90 | T cornishFisherInvCDFImpl(T)(const T x, const T skewness, const T excessKurtosis) 91 | if (isFloatingPoint!T) 92 | { 93 | import mir.stat.distribution.normal: normalInvCDF; 94 | 95 | T x2 = x * x; 96 | T x3 = x2 * x; 97 | return x + (x2 - 1) * skewness / 6 + (x3 - 3 * x) * excessKurtosis / 24 - (2 * x3 - 5 * x) * skewness * skewness / 36; 98 | } 99 | 100 | // 101 | version(mir_stat_test) 102 | @safe pure @nogc nothrow 103 | unittest { 104 | import mir.test: shouldApprox; 105 | 106 | 0.0.cornishFisherInvCDFImpl(0, 0).shouldApprox == 0; 107 | 0.0.cornishFisherInvCDFImpl(1, 0).shouldApprox == -0.1666667; 108 | 0.0.cornishFisherInvCDFImpl(-1, 0).shouldApprox == 0.1666667; 109 | 110 | 1.281552.cornishFisherInvCDFImpl(0.1, 0).shouldApprox == 1.292868; 111 | 1.281552.cornishFisherInvCDFImpl(0.1, 1).shouldApprox == 1.220374; 112 | 1.281552.cornishFisherInvCDFImpl(0.1, -1).shouldApprox == 1.365363; 113 | 114 | 2.326348.cornishFisherInvCDFImpl(0.1, 0).shouldApprox == 2.396116; 115 | 2.326348.cornishFisherInvCDFImpl(0.1, 1).shouldApprox == 2.629904; 116 | 2.326348.cornishFisherInvCDFImpl(0.1, -1).shouldApprox == 2.162328; 117 | 118 | (-2.326348).cornishFisherInvCDFImpl(0.1, 0).shouldApprox == -2.249053 ; 119 | (-2.326348).cornishFisherInvCDFImpl(0.1, 1).shouldApprox == -2.482841; 120 | (-2.326348).cornishFisherInvCDFImpl(0.1, -1).shouldApprox == -2.015265; 121 | } 122 | -------------------------------------------------------------------------------- /source/mir/stat/distribution/exponential.d: -------------------------------------------------------------------------------- 1 | /++ 2 | This module contains algorithms for the $(LINK2 https://en.wikipedia.org/wiki/Exponential_distribution, Exponential Distribution). 3 | 4 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 | 6 | Authors: John Michael Hall 7 | 8 | Copyright: 2022-3 Mir Stat Authors. 9 | 10 | +/ 11 | 12 | module mir.stat.distribution.exponential; 13 | 14 | import mir.internal.utility: isFloatingPoint; 15 | 16 | /++ 17 | Computes the exponential probability density function (PDF). 18 | 19 | Params: 20 | x = value to evaluate PDF 21 | lambda = number of events in an interval 22 | 23 | See_also: 24 | $(LINK2 https://en.wikipedia.org/wiki/Exponential_distribution, Exponential Distribution) 25 | +/ 26 | @safe pure nothrow @nogc 27 | T exponentialPDF(T)(const T x, const T lambda) 28 | if (isFloatingPoint!T) 29 | in (x >= 0, "x must be greater than or equal to 0") 30 | in (lambda > 0, "lambda must be greater than zero") 31 | { 32 | import mir.math.common: exp; 33 | 34 | return lambda * exp(-lambda * x); 35 | } 36 | 37 | /// 38 | version(mir_stat_test) 39 | @safe pure nothrow @nogc 40 | unittest { 41 | import mir.math.common: approxEqual; 42 | 43 | assert(0.5.exponentialPDF(2.0).approxEqual(0.7357589)); 44 | assert(0.75.exponentialPDF(2.0).approxEqual(0.4462603)); 45 | assert(0.25.exponentialPDF(0.5).approxEqual(0.4412485)); 46 | } 47 | 48 | /++ 49 | Computes the exponential cumulative distribution function (CDF). 50 | 51 | Params: 52 | x = value to evaluate CDF 53 | lambda = number of events in an interval 54 | 55 | See_also: 56 | $(LINK2 https://en.wikipedia.org/wiki/Exponential_distribution, Exponential Distribution) 57 | +/ 58 | @safe pure nothrow @nogc 59 | T exponentialCDF(T)(const T x, const T lambda) 60 | if (isFloatingPoint!T) 61 | in (x >= 0, "x must be greater than or equal to 0") 62 | in (lambda > 0, "lambda must be greater than zero") 63 | { 64 | import mir.math.common: exp; 65 | 66 | return 1 - exp(-lambda * x); 67 | } 68 | 69 | /// 70 | version(mir_stat_test) 71 | @safe pure nothrow @nogc 72 | unittest { 73 | import mir.math.common: approxEqual; 74 | 75 | assert(0.5.exponentialCDF(2.0).approxEqual(0.6321206)); 76 | assert(0.75.exponentialCDF(2.0).approxEqual(0.7768698)); 77 | assert(0.25.exponentialCDF(0.5).approxEqual(0.1175031)); 78 | } 79 | 80 | /++ 81 | Computes the exponential complementary cumulative distribution function (CCDF). 82 | 83 | Params: 84 | x = value to evaluate CCDF 85 | lambda = number of events in an interval 86 | 87 | See_also: 88 | $(LINK2 https://en.wikipedia.org/wiki/Exponential_distribution, Exponential Distribution) 89 | +/ 90 | @safe pure nothrow @nogc 91 | T exponentialCCDF(T)(const T x, const T lambda) 92 | if (isFloatingPoint!T) 93 | in (x >= 0, "x must be greater than or equal to 0") 94 | in (lambda > 0, "lambda must be greater than zero") 95 | { 96 | import mir.math.common: exp; 97 | 98 | return exp(-lambda * x); 99 | } 100 | 101 | /// 102 | version(mir_stat_test) 103 | @safe pure nothrow @nogc 104 | unittest { 105 | import mir.math.common: approxEqual; 106 | 107 | assert(0.5.exponentialCCDF(2.0).approxEqual(1 - exponentialCDF(0.5, 2.0))); 108 | assert(0.75.exponentialCCDF(2.0).approxEqual(1 - exponentialCDF(0.75, 2.0))); 109 | assert(0.25.exponentialCCDF(0.5).approxEqual(1 - exponentialCDF(0.25, 0.5))); 110 | } 111 | 112 | /++ 113 | Computes the exponential inverse cumulative distribution function (InvCDF). 114 | 115 | Params: 116 | p = value to evaluate InvCDF 117 | lambda = number of events in an interval 118 | 119 | See_also: 120 | $(LINK2 https://en.wikipedia.org/wiki/Exponential_distribution, Exponential Distribution) 121 | +/ 122 | 123 | @safe pure nothrow @nogc 124 | T exponentialInvCDF(T)(const T p, const T lambda) 125 | if (isFloatingPoint!T) 126 | in (p >= 0, "p must be greater than or equal to 0") 127 | in (p < 1, "p must be less than or equal to 1") 128 | in (lambda > 0, "lambda must be greater than zero") 129 | { 130 | import mir.math.internal.log1p: log1p; 131 | 132 | return -log1p(-p) / lambda; 133 | } 134 | 135 | /// 136 | version(mir_stat_test) 137 | @safe pure nothrow @nogc 138 | unittest { 139 | import mir.math.common: approxEqual; 140 | 141 | assert(0.6321206.exponentialInvCDF(2.0).approxEqual(0.5)); 142 | assert(0.7768698.exponentialInvCDF(2.0).approxEqual(0.75)); 143 | assert(0.1175031.exponentialInvCDF(0.5).approxEqual(0.25)); 144 | } 145 | 146 | /++ 147 | Computes the exponential log probability density function (LPDF). 148 | 149 | Params: 150 | x = value to evaluate LPDF 151 | lambda = number of events in an interval 152 | 153 | See_also: 154 | $(LINK2 https://en.wikipedia.org/wiki/Exponential_distribution, Exponential Distribution) 155 | +/ 156 | @safe pure nothrow @nogc 157 | T exponentialLPDF(T)(const T x, const T lambda) 158 | if (isFloatingPoint!T) 159 | in (x >= 0, "x must be greater than or equal to 0") 160 | in (lambda > 0, "lambda must be greater than zero") 161 | { 162 | import mir.math.common: log; 163 | 164 | return log(lambda) - x * lambda; 165 | } 166 | 167 | /// 168 | version(mir_stat_test) 169 | @safe pure nothrow @nogc 170 | unittest { 171 | import mir.math.common: approxEqual, log; 172 | 173 | assert(0.5.exponentialLPDF(2.0).approxEqual(log(exponentialPDF(0.5, 2.0)))); 174 | assert(0.75.exponentialLPDF(2.0).approxEqual(log(exponentialPDF(0.75, 2.0)))); 175 | assert(0.25.exponentialLPDF(0.5).approxEqual(log(exponentialPDF(0.25, 0.5)))); 176 | } 177 | -------------------------------------------------------------------------------- /source/mir/stat/distribution/f.d: -------------------------------------------------------------------------------- 1 | /++ 2 | This module contains algorithms for the $(LINK2 https://en.wikipedia.org/wiki/F-distribution, F Distribution). 3 | 4 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 | 6 | Authors: John Michael Hall 7 | 8 | Copyright: 2022-3 Mir Stat Authors. 9 | 10 | +/ 11 | 12 | module mir.stat.distribution.f; 13 | 14 | import mir.internal.utility: isFloatingPoint; 15 | 16 | /++ 17 | Computes the F probability density function (PDF). 18 | 19 | Params: 20 | x = value to evaluate PDF 21 | df1 = degrees of freedom parameter #1 22 | df2 = degrees of freedom parameter #2 23 | 24 | See_also: 25 | $(LINK2 https://en.wikipedia.org/wiki/F-distribution, F Distribution) 26 | +/ 27 | @safe pure nothrow @nogc 28 | T fPDF(T)(const T x, const T df1, const T df2) 29 | if (isFloatingPoint!T) 30 | in (x >= 0, "x must be greater than or equal to 0") 31 | in (df1 > 0, "df1 must be greater than zero") 32 | in (df2 > 0, "df2 must be greater than zero") 33 | { 34 | import mir.math.common: pow, sqrt; 35 | import std.mathspecial: beta; 36 | 37 | if (df1 == 1 && x == 0) 38 | return T.infinity; 39 | return sqrt(pow(df1 * x, df1) * pow(df2, df2) / pow(df1 * x + df2, df1 + df2)) / (x * beta(df1 * 0.5, df2 * 0.5)); 40 | } 41 | 42 | /// 43 | version(mir_stat_test) 44 | @safe pure nothrow @nogc 45 | unittest { 46 | import mir.test: shouldApprox; 47 | 48 | 0.50.fPDF(1, 1).shouldApprox == 0.3001054; 49 | 0.75.fPDF(1, 2).shouldApprox == 0.2532039; 50 | 0.25.fPDF(0.5, 4).shouldApprox == 0.4904035; 51 | 0.10.fPDF(2, 1).shouldApprox == 0.7607258; 52 | 0.00.fPDF(1, 3).shouldApprox == double.infinity; 53 | } 54 | 55 | /++ 56 | Computes the F cumulative distribution function (CDF). 57 | 58 | Params: 59 | x = value to evaluate CDF 60 | df1 = degrees of freedom parameter #1 61 | df2 = degrees of freedom parameter #2 62 | 63 | See_also: 64 | $(LINK2 https://en.wikipedia.org/wiki/F-distribution, F Distribution) 65 | +/ 66 | @safe pure nothrow @nogc 67 | T fCDF(T)(const T x, const T df1, const T df2) 68 | if (isFloatingPoint!T) 69 | in (x >= 0, "x must be greater than or equal to 0") 70 | in (df1 > 0, "df1 must be greater than zero") 71 | in (df2 > 0, "df2 must be greater than zero") 72 | { 73 | import std.mathspecial: betaIncomplete; 74 | 75 | return betaIncomplete(df1 * 0.5, df2 * 0.5, (df1 * x) / (df1 * x + df2)); 76 | } 77 | 78 | /// 79 | version(mir_stat_test) 80 | @safe pure nothrow @nogc 81 | unittest { 82 | import mir.test: shouldApprox; 83 | 84 | 0.50.fCDF(1, 1).shouldApprox == 0.3918266; 85 | 0.75.fCDF(1, 2).shouldApprox == 0.522233; 86 | 0.25.fCDF(0.5, 4).shouldApprox == 0.5183719; 87 | 0.10.fCDF(2, 1).shouldApprox == 0.08712907; 88 | } 89 | 90 | /++ 91 | Computes the F complementary cumulative distribution function (CCDF). 92 | 93 | Params: 94 | x = value to evaluate CCDF 95 | df1 = degrees of freedom parameter #1 96 | df2 = degrees of freedom parameter #2 97 | 98 | See_also: 99 | $(LINK2 https://en.wikipedia.org/wiki/F-distribution, F Distribution) 100 | +/ 101 | @safe pure nothrow @nogc 102 | T fCCDF(T)(const T x, const T df1, const T df2) 103 | if (isFloatingPoint!T) 104 | in (x >= 0, "x must be greater than or equal to 0") 105 | in (df1 > 0, "df1 must be greater than zero") 106 | in (df2 > 0, "df2 must be greater than zero") 107 | { 108 | import std.mathspecial: betaIncomplete; 109 | 110 | return betaIncomplete(df2 * 0.5, df1 * 0.5, df2 / (df1 * x + df2)); 111 | } 112 | 113 | /// 114 | version(mir_stat_test) 115 | @safe pure nothrow @nogc 116 | unittest { 117 | import mir.test: shouldApprox; 118 | 119 | 0.50.fCCDF(1, 1).shouldApprox == 0.6081734; 120 | 0.75.fCCDF(1, 2).shouldApprox == 0.477767; 121 | 0.25.fCCDF(0.5, 4).shouldApprox == 0.4816281; 122 | 0.10.fCCDF(2, 1).shouldApprox == 0.9128709; 123 | } 124 | 125 | /++ 126 | Computes the F inverse cumulative distribution function (InvCDF). 127 | 128 | Params: 129 | p = value to evaluate InvCDF 130 | df1 = degrees of freedom parameter #1 131 | df2 = degrees of freedom parameter #2 132 | 133 | See_also: 134 | $(LINK2 https://en.wikipedia.org/wiki/F-distribution, F Distribution) 135 | +/ 136 | @safe pure nothrow @nogc 137 | T fInvCDF(T)(const T p, const T df1, const T df2) 138 | if (isFloatingPoint!T) 139 | in (p >= 0, "p must be greater than or equal to 0") 140 | in (p <= 1, "p must be less than or equal to 1") 141 | in (df1 > 0, "df1 must be greater than zero") 142 | in (df2 > 0, "df2 must be greater than zero") 143 | { 144 | import std.mathspecial: betaIncompleteInverse; 145 | 146 | if (p == 0) 147 | return 0; 148 | if (p == 1) 149 | return T.infinity; 150 | const T invBeta = betaIncompleteInverse(df1 * 0.5, df2 * 0.5, p); 151 | return invBeta * df2 / ((1 - invBeta) * df1); 152 | } 153 | 154 | /// 155 | version(mir_stat_test) 156 | @safe pure nothrow @nogc 157 | unittest { 158 | import mir.test: shouldApprox; 159 | 160 | 0.3918266.fInvCDF(1, 1).shouldApprox == 0.50; 161 | 0.522233.fInvCDF(1, 2).shouldApprox == 0.75; 162 | 0.5183719.fInvCDF(0.5, 4).shouldApprox == 0.25; 163 | 0.08712907.fInvCDF(2, 1).shouldApprox == 0.10; 164 | 0.0.fInvCDF(1, 1).shouldApprox == 0; 165 | 1.0.fInvCDF(1, 1).shouldApprox == double.infinity; 166 | } 167 | 168 | /++ 169 | Computes the F log probability density function (LPDF). 170 | 171 | Params: 172 | x = value to evaluate LPDF 173 | df1 = degrees of freedom parameter #1 174 | df2 = degrees of freedom parameter #2 175 | 176 | See_also: 177 | $(LINK2 https://en.wikipedia.org/wiki/F-distribution, F Distribution) 178 | +/ 179 | @safe pure nothrow @nogc 180 | T fLPDF(T)(const T x, const T df1, const T df2) 181 | if (isFloatingPoint!T) 182 | in (x >= 0, "x must be greater than or equal to 0") 183 | in (df1 > 0, "df1 must be greater than zero") 184 | in (df2 > 0, "df2 must be greater than zero") 185 | { 186 | import mir.math.common: log; 187 | import mir.math.internal.log_beta: logBeta; 188 | 189 | if (df1 == 1 && x == 0) 190 | return T.infinity; 191 | return 0.5 * (df1 * log(df1 * x) + df2 * log(df2) - (df1 + df2) * log(df1 * x + df2)) - log(x) - logBeta(df1 * 0.5, df2 * 0.5); 192 | } 193 | 194 | /// 195 | version(mir_stat_test) 196 | @safe pure nothrow @nogc 197 | unittest { 198 | import mir.math.common: log; 199 | import mir.test: shouldApprox; 200 | 201 | 0.50.fLPDF(1, 1).shouldApprox == log(0.3001054); 202 | 0.75.fLPDF(1, 2).shouldApprox == log(0.2532039); 203 | 0.25.fLPDF(0.5, 4).shouldApprox == log(0.4904035); 204 | 0.10.fLPDF(2, 1).shouldApprox == log(0.7607258); 205 | 0.00.fLPDF(1, 3).shouldApprox == double.infinity; 206 | } 207 | -------------------------------------------------------------------------------- /source/mir/stat/distribution/generalized_pareto.d: -------------------------------------------------------------------------------- 1 | /++ 2 | This module contains algorithms for the $(LINK2 https://en.wikipedia.org/wiki/Generalized_Pareto_distribution, Generalized Pareto Distribution). 3 | 4 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 | 6 | Authors: John Michael Hall 7 | 8 | Copyright: 2022-3 Mir Stat Authors. 9 | 10 | +/ 11 | 12 | module mir.stat.distribution.generalized_pareto; 13 | 14 | import mir.internal.utility: isFloatingPoint; 15 | 16 | /++ 17 | Computes the generalized pareto probability density function (PDF). 18 | 19 | Params: 20 | x = value to evaluate PDF 21 | mu = location parameter 22 | sigma = scale parameter 23 | xi = shape parameter 24 | 25 | See_also: 26 | $(LINK2 https://en.wikipedia.org/wiki/Generalized_Pareto_distribution, Generalized Pareto Distribution) 27 | +/ 28 | @safe pure nothrow @nogc 29 | T generalizedParetoPDF(T)(const T x, const T mu, const T sigma, const T xi) 30 | if (isFloatingPoint!T) 31 | in (sigma > 0, "sigma must be greater than zero") 32 | in (x >= mu, "x must be greater than or equal to mu") 33 | in (xi >= 0 || (xi < 0 && x <= (mu - sigma / xi)), "if xi is less than zero, x must be less than mu - sigma / xi") 34 | { 35 | import mir.math.common: exp, pow; 36 | 37 | const T z = (x - mu) / sigma; 38 | if (xi != 0) { 39 | return (cast(T) 1 / sigma) * pow(1 + xi * z, -(cast(T) 1 / xi + 1)); 40 | } else { 41 | return exp(-z); 42 | } 43 | } 44 | 45 | /// 46 | version(mir_stat_test) 47 | @safe pure nothrow @nogc 48 | unittest { 49 | import mir.test: shouldApprox; 50 | 51 | 1.0.generalizedParetoPDF(1, 1, 0.5).shouldApprox == 1; 52 | 2.0.generalizedParetoPDF(1, 1, 0.5).shouldApprox == 0.2962963; 53 | 3.0.generalizedParetoPDF(2, 3, 0.25).shouldApprox == 0.2233923; 54 | 5.0.generalizedParetoPDF(2, 3, 0).shouldApprox == 0.3678794; 55 | } 56 | 57 | /++ 58 | Computes the generalized pareto cumulative distribution function (CDF). 59 | 60 | Params: 61 | x = value to evaluate CDF 62 | mu = location parameter 63 | sigma = scale parameter 64 | xi = shape parameter 65 | 66 | See_also: 67 | $(LINK2 https://en.wikipedia.org/wiki/Generalized_Pareto_distribution, Generalized Pareto Distribution) 68 | +/ 69 | @safe pure nothrow @nogc 70 | T generalizedParetoCDF(T)(const T x, const T mu, const T sigma, const T xi) 71 | if (isFloatingPoint!T) 72 | in (sigma > 0, "sigma must be greater than zero") 73 | in (x >= mu, "x must be greater than or equal to mu") 74 | in (xi >= 0 || (xi < 0 && x <= (mu - sigma / xi)), "if xi is less than zero, x must be less than mu - sigma / xi") 75 | { 76 | import mir.math.common: exp, pow; 77 | 78 | const T z = (x - mu) / sigma; 79 | if (xi != 0) { 80 | return 1 - pow(1 + xi * z, -(cast(T) 1) / xi); 81 | } else { 82 | return 1 - exp(-z); 83 | } 84 | } 85 | 86 | /// 87 | version(mir_stat_test) 88 | @safe pure nothrow @nogc 89 | unittest { 90 | import mir.test: shouldApprox; 91 | 92 | 1.0.generalizedParetoCDF(1, 1, 0.5).shouldApprox == 0; 93 | 2.0.generalizedParetoCDF(1, 1, 0.5).shouldApprox == 0.5555556; 94 | 3.0.generalizedParetoCDF(2, 3, 0.25).shouldApprox == 0.273975; 95 | 5.0.generalizedParetoCDF(2, 3, 0).shouldApprox == 0.6321206; 96 | } 97 | 98 | /++ 99 | Computes the generalized pareto complementary cumulative distribution function (CCDF). 100 | 101 | Params: 102 | x = value to evaluate CCDF 103 | mu = location parameter 104 | sigma = scale parameter 105 | xi = shape parameter 106 | 107 | See_also: 108 | $(LINK2 https://en.wikipedia.org/wiki/Generalized_Pareto_distribution, Generalized Pareto Distribution) 109 | +/ 110 | @safe pure nothrow @nogc 111 | T generalizedParetoCCDF(T)(const T x, const T mu, const T sigma, const T xi) 112 | if (isFloatingPoint!T) 113 | in (sigma > 0, "sigma must be greater than zero") 114 | in (x >= mu, "x must be greater than or equal to mu") 115 | in (xi >= 0 || (xi < 0 && x <= (mu - sigma / xi)), "if xi is less than zero, x must be less than mu - sigma / xi") 116 | { 117 | import mir.math.common: exp, pow; 118 | 119 | const T z = (x - mu) / sigma; 120 | if (xi != 0) { 121 | return pow(1 + xi * z, -(cast(T) 1) / xi); 122 | } else { 123 | return exp(-z); 124 | } 125 | } 126 | 127 | /// 128 | version(mir_stat_test) 129 | @safe pure nothrow @nogc 130 | unittest { 131 | import mir.test: shouldApprox; 132 | 133 | 1.0.generalizedParetoCCDF(1, 1, 0.5).shouldApprox == 1; 134 | 2.0.generalizedParetoCCDF(1, 1, 0.5).shouldApprox == 0.4444444; 135 | 3.0.generalizedParetoCCDF(2, 3, 0.25).shouldApprox == 0.726025; 136 | 5.0.generalizedParetoCCDF(2, 3, 0).shouldApprox == 0.3678794; 137 | } 138 | 139 | /++ 140 | Computes the generalized pareto inverse cumulative distribution function (InvCDF). 141 | 142 | Params: 143 | p = value to evaluate InvCDF 144 | mu = location parameter 145 | sigma = scale parameter 146 | xi = shape parameter 147 | 148 | See_also: 149 | $(LINK2 https://en.wikipedia.org/wiki/Generalized_Pareto_distribution, Generalized Pareto Distribution) 150 | +/ 151 | @safe pure nothrow @nogc 152 | T generalizedParetoInvCDF(T)(const T p, const T mu, const T sigma, const T xi) 153 | if (isFloatingPoint!T) 154 | in (p >= 0, "p must be greater than or equal to 0") 155 | in (p <= 1, "p must be less than or equal to 1") 156 | in (sigma > 0, "sigma must be greater than zero") 157 | { 158 | import mir.math.common: pow; 159 | import mir.math.internal.log1p: log1p; 160 | 161 | T output; 162 | if (xi != 0) { 163 | output = (cast(T) 1 / xi) * (pow(1 - p, -xi) - 1); 164 | } else { 165 | output = -log1p(-p); 166 | } 167 | return mu + sigma * output; 168 | } 169 | 170 | /// 171 | version(mir_stat_test) 172 | @safe pure nothrow @nogc 173 | unittest { 174 | import mir.test: shouldApprox; 175 | 176 | 0.0.generalizedParetoInvCDF(1, 1, 0.5).shouldApprox == 1; 177 | 0.5555556.generalizedParetoInvCDF(1, 1, 0.5).shouldApprox == 2; 178 | 0.273975.generalizedParetoInvCDF(2, 3, 0.25).shouldApprox == 3; 179 | 0.6321206.generalizedParetoInvCDF(2, 3, 0).shouldApprox == 5; 180 | } 181 | 182 | /++ 183 | Computes the generalized pareto log probability density function (LPDF). 184 | 185 | Params: 186 | x = value to evaluate LPDF 187 | mu = location parameter 188 | sigma = scale parameter 189 | xi = shape parameter 190 | 191 | See_also: 192 | $(LINK2 https://en.wikipedia.org/wiki/Generalized_Pareto_distribution, Generalized Pareto Distribution) 193 | +/ 194 | @safe pure nothrow @nogc 195 | T generalizedParetoLPDF(T)(const T x, const T mu, const T sigma, const T xi) 196 | if (isFloatingPoint!T) 197 | in (sigma > 0, "sigma must be greater than zero") 198 | in (x >= mu, "x must be greater than or equal to mu") 199 | in (xi >= 0 || (xi < 0 && x <= (mu - sigma / xi)), "if xi is less than zero, x must be less than mu - sigma / xi") 200 | { 201 | import mir.math.common: log; 202 | import mir.math.internal.xlogy: xlogy; 203 | 204 | const T z = (x - mu) / sigma; 205 | if (xi != 0) { 206 | return -log(sigma) + xlogy(-(cast(T) 1 / xi + 1), 1 + xi * z); 207 | } else { 208 | return -z; 209 | } 210 | } 211 | 212 | /// 213 | version(mir_stat_test) 214 | @safe pure nothrow @nogc 215 | unittest { 216 | import mir.math.common: log; 217 | import mir.test: shouldApprox; 218 | 219 | 1.0.generalizedParetoLPDF(1, 1, 0.5).shouldApprox == log(generalizedParetoPDF(1.0, 1, 1, 0.5)); 220 | 2.0.generalizedParetoLPDF(1, 1, 0.5).shouldApprox == log(generalizedParetoPDF(2.0, 1, 1, 0.5)); 221 | 3.0.generalizedParetoLPDF(2, 3, 0.25).shouldApprox == log(generalizedParetoPDF(3.0, 2, 3, 0.25)); 222 | 5.0.generalizedParetoLPDF(2, 3, 0).shouldApprox == log(generalizedParetoPDF(5.0, 2, 3, 0)); 223 | } 224 | -------------------------------------------------------------------------------- /source/mir/stat/distribution/geometric.d: -------------------------------------------------------------------------------- 1 | /++ 2 | This module contains algorithms for the $(LINK2 https://en.wikipedia.org/wiki/Geometric_distribution, Geometric Distribution). 3 | 4 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 | 6 | Authors: John Michael Hall 7 | 8 | Copyright: 2022-3 Mir Stat Authors. 9 | 10 | +/ 11 | 12 | module mir.stat.distribution.geometric; 13 | 14 | import mir.internal.utility: isFloatingPoint; 15 | 16 | /++ 17 | Computes the geometric probability density function (PMF). 18 | 19 | Params: 20 | k = value to evaluate PMF 21 | p = `true` probability 22 | 23 | See_also: $(LINK2 https://en.wikipedia.org/wiki/Geometric_distribution, Geometric Distribution) 24 | +/ 25 | @safe pure @nogc nothrow 26 | T geometricPMF(T)(const size_t k, const T p) 27 | if (isFloatingPoint!T) 28 | in (p >= 0, "p must be greater than or equal to 0") 29 | in (p <= 1, "p must be less than or equal to 1") 30 | { 31 | import mir.math.common: pow; 32 | return pow(1 - p, k) * p; 33 | } 34 | 35 | /// 36 | @safe pure @nogc nothrow 37 | version(mir_stat_test) 38 | unittest 39 | { 40 | import mir.test: shouldApprox; 41 | 42 | 0.geometricPMF(0.5).shouldApprox == 0.5; 43 | 1.geometricPMF(0.5).shouldApprox == 0.25; 44 | 2.geometricPMF(0.25).shouldApprox == 0.140625; 45 | } 46 | 47 | /++ 48 | Computes the geometric cumulative density function (CDF). 49 | 50 | Params: 51 | k = value to evaluate CDF 52 | p = `true` probability 53 | 54 | See_also: $(LINK2 https://en.wikipedia.org/wiki/Geometric_distribution, Geometric Distribution) 55 | +/ 56 | @safe pure @nogc nothrow 57 | T geometricCDF(T)(const size_t k, const T p) 58 | if (isFloatingPoint!T) 59 | in (p >= 0, "p must be greater than or equal to 0") 60 | in (p <= 1, "p must be less than or equal to 1") 61 | { 62 | import mir.math.common: pow; 63 | return 1 - pow(1 - p, k + 1); 64 | } 65 | 66 | /// ditto 67 | @safe pure @nogc nothrow 68 | T geometricCDF(T)(const T x, const T p) 69 | if (isFloatingPoint!T) 70 | in (x >= -1, "x must be larger than or equal to -1") 71 | in (p >= 0, "p must be greater than or equal to 0") 72 | in (p <= 1, "p must be less than or equal to 1") 73 | { 74 | if (x < 0) 75 | return 0; 76 | return geometricCDF!T(cast(const size_t) x, p); 77 | } 78 | 79 | /// 80 | @safe pure @nogc nothrow 81 | version(mir_stat_test) 82 | unittest 83 | { 84 | import mir.test: shouldApprox; 85 | 86 | geometricCDF(-1.0, 0.5).shouldApprox == 0; // UFCS chaining deduces this as size_t instead of a floating point type 87 | 0.geometricCDF(0.5).shouldApprox == 0.5; 88 | 1.geometricCDF(0.5).shouldApprox == 0.75; 89 | 2.geometricCDF(0.5).shouldApprox == 0.875; 90 | 91 | geometricCDF(-1.0, 0.25).shouldApprox == 0; // UFCS chaining deduces this as size_t instead of a floating point type 92 | 0.geometricCDF(0.25).shouldApprox == 0.25; 93 | 1.geometricCDF(0.25).shouldApprox == 0.4375; 94 | 2.geometricCDF(0.25).shouldApprox == 0.578125; 95 | 2.5.geometricCDF(0.25).shouldApprox == 0.578125; 96 | } 97 | 98 | /++ 99 | Computes the geometric complementary cumulative density function (CCDF). 100 | 101 | Params: 102 | k = value to evaluate CCDF 103 | p = `true` probability 104 | 105 | See_also: $(LINK2 https://en.wikipedia.org/wiki/Geometric_distribution, Geometric Distribution) 106 | +/ 107 | @safe pure @nogc nothrow 108 | T geometricCCDF(T)(const size_t k, const T p) 109 | if (isFloatingPoint!T) 110 | in (p >= 0, "p must be greater than or equal to 0") 111 | in (p <= 1, "p must be less than or equal to 1") 112 | { 113 | import mir.math.common: pow; 114 | return pow(1 - p, k + 1); 115 | } 116 | 117 | /// ditto 118 | @safe pure @nogc nothrow 119 | T geometricCCDF(T)(const T x, const T p) 120 | if (isFloatingPoint!T) 121 | in (x >= -1, "x must be larger than or equal to -1") 122 | in (p >= 0, "p must be greater than or equal to 0") 123 | in (p <= 1, "p must be less than or equal to 1") 124 | { 125 | if (x < 0) 126 | return 1; 127 | return geometricCCDF!T(cast(const size_t) x, p); 128 | } 129 | 130 | /// 131 | @safe pure @nogc nothrow 132 | version(mir_stat_test) 133 | unittest 134 | { 135 | import mir.test: shouldApprox; 136 | 137 | geometricCCDF(-1.0, 0.5).shouldApprox == 1.0; // UFCS chaining deduces this as size_t instead of a floating point type 138 | 0.geometricCCDF(0.5).shouldApprox == 0.5; 139 | 1.geometricCCDF(0.5).shouldApprox == 0.25; 140 | 2.geometricCCDF(0.5).shouldApprox == 0.125; 141 | 142 | geometricCCDF(-1.0, 0.25).shouldApprox == 1.0; // UFCS chaining deduces this as size_t instead of a floating point type 143 | 0.geometricCCDF(0.25).shouldApprox == 0.75; 144 | 1.geometricCCDF(0.25).shouldApprox == 0.5625; 145 | 2.geometricCCDF(0.25).shouldApprox == 0.421875; 146 | 2.5.geometricCCDF(0.25).shouldApprox == 0.421875; 147 | } 148 | 149 | /++ 150 | Computes the geometric inverse cumulative distribution function (InvCDF). 151 | 152 | Params: 153 | q = value to evaluate InvCDF 154 | p = `true` probability 155 | 156 | See_also: $(LINK2 https://en.wikipedia.org/wiki/Geometric_distribution, Geometric Distribution) 157 | +/ 158 | @safe pure @nogc nothrow 159 | T geometricInvCDF(T)(const T q, const T p) 160 | if (isFloatingPoint!T) 161 | in (q >= 0, "q must be greater than or equal to 0") 162 | in (q <= 1, "q must be less than or equal to 1") 163 | in (p >= 0, "p must be greater than or equal to 0") 164 | in (p <= 1, "p must be less than or equal to 1") 165 | { 166 | import mir.math.common: approxEqual, ceil, nearbyint, log; 167 | if (q == 1) { 168 | return T.infinity; 169 | } 170 | if (p == 1) { 171 | return 0; 172 | } 173 | T guess = log(1 - q) / log(1 - p); 174 | T guessNearby = nearbyint(guess); 175 | if (approxEqual(guess, guessNearby, T.epsilon * 2)) { 176 | return guessNearby - 1; 177 | } else { 178 | return ceil(guess) - 1; 179 | } 180 | } 181 | 182 | /// 183 | @safe pure @nogc nothrow 184 | version(mir_stat_test) 185 | unittest 186 | { 187 | import mir.test: should; 188 | 189 | 0.geometricInvCDF(0.5).should == -1; 190 | 0.5.geometricInvCDF(0.5).should == 0; 191 | 0.75.geometricInvCDF(0.5).should == 1; 192 | 0.875.geometricInvCDF(0.5).should == 2; 193 | 0.95.geometricInvCDF(0.5).should == 4; 194 | 195 | 0.geometricInvCDF(0.25).should == -1; 196 | 0.25.geometricInvCDF(0.25).should == 0; 197 | 0.4375.geometricInvCDF(0.25).should == 1; 198 | 0.578125.geometricInvCDF(0.25).should == 2; 199 | 0.95.geometricInvCDF(0.25).should == 10; 200 | 201 | 0.5.geometricInvCDF(1).should == 0; 202 | 1.geometricInvCDF(0.5).should == double.infinity; 203 | } 204 | 205 | /++ 206 | Computes the geometric log probability density function (LPMF). 207 | 208 | Params: 209 | k = value to evaluate LPMF 210 | p = `true` probability 211 | 212 | See_also: $(LINK2 https://en.wikipedia.org/wiki/Geometric_distribution, Geometric Distribution) 213 | +/ 214 | @safe pure @nogc nothrow 215 | T geometricLPMF(T)(const size_t k, const T p) 216 | if (isFloatingPoint!T) 217 | in (p >= 0, "p must be greater than or equal to 0") 218 | in (p <= 1, "p must be less than or equal to 1") 219 | { 220 | import mir.math.common: log; 221 | import mir.math.internal.xlogy: xlog1py; 222 | return xlog1py(k, -p) + log(p); 223 | } 224 | 225 | /// 226 | @safe pure @nogc nothrow 227 | version(mir_stat_test) 228 | unittest 229 | { 230 | import mir.math.common: exp; 231 | import mir.test: shouldApprox; 232 | 233 | 0.geometricLPMF(0.5).exp.shouldApprox == 0.geometricPMF(0.5); 234 | 1.geometricLPMF(0.5).exp.shouldApprox == 1.geometricPMF(0.5); 235 | 2.geometricLPMF(0.25).exp.shouldApprox == 2.geometricPMF(0.25); 236 | } 237 | -------------------------------------------------------------------------------- /source/mir/stat/distribution/gev.d: -------------------------------------------------------------------------------- 1 | /++ 2 | This module contains algorithms for the $(LINK2 https://en.wikipedia.org/wiki/Generalized_extreme_value_distribution, Generalized Extreme Value (GEV) Distribution). 3 | 4 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 | 6 | Authors: Ilia Ki, John Michael Hall 7 | 8 | Copyright: 2022-3 Mir Stat Authors. 9 | 10 | +/ 11 | 12 | module mir.stat.distribution.gev; 13 | 14 | import mir.internal.utility: isFloatingPoint; 15 | 16 | import mir.math.common: fabs, exp, pow, log; 17 | 18 | /++ 19 | Computes the generalized extreme value (GEV) probability density function (PDF). 20 | 21 | Params: 22 | x = value to evaluate 23 | mu = location 24 | sigma = scale 25 | xi = shape 26 | 27 | See_also: 28 | $(LINK2 https://en.wikipedia.org/wiki/Generalized_extreme_value_distribution, Generalized Extreme Value (GEV) Distribution) 29 | +/ 30 | @safe pure nothrow @nogc 31 | T gevPDF(T)(const T x, const T mu, const T sigma, const T xi) 32 | if (isFloatingPoint!T) 33 | in (xi >= 0 || x <= mu - sigma / xi, "if xi is less than zero, x must be less than or equal to mu - sigma / xi") 34 | in (xi <= 0 || x >= mu - sigma / xi, "if xi is greater than zero, xi must be greater than or equal to mu - sigma / xi") 35 | { 36 | auto s = (x - mu) / sigma; 37 | if (xi.fabs <= T.min_normal) 38 | { 39 | auto t = exp(-s); 40 | return t * exp(-t); 41 | } 42 | auto v = 1 + xi * s; 43 | if (v <= 0) 44 | return 0; 45 | auto a = pow(v, -1 / xi); 46 | return a * exp(-a) / (v * sigma); 47 | } 48 | 49 | /// 50 | version(mir_stat_test) 51 | @safe pure nothrow @nogc 52 | unittest 53 | { 54 | import mir.test: shouldApprox; 55 | 56 | gevPDF(-3, 2, 3, -0.5).shouldApprox == 0.02120353011709564; 57 | gevPDF(-1, 2, 3, +0.5).shouldApprox == 0.04884170370329114; 58 | gevPDF(-1, 2, 3, 0.0).shouldApprox == 0.1793740787340172; 59 | } 60 | 61 | // Checking v <= 0 branch 62 | version(mir_stat_test) 63 | @safe pure nothrow @nogc 64 | unittest 65 | { 66 | import mir.test: should; 67 | gevPDF(-1.0, 0, 1, 1).should == 0; 68 | } 69 | 70 | /++ 71 | Computes the generalized extreme value (GEV) cumulatve distribution function (CDF). 72 | 73 | Params: 74 | x = value to evaluate 75 | mu = location 76 | sigma = scale 77 | xi = shape 78 | 79 | See_also: 80 | $(LINK2 https://en.wikipedia.org/wiki/Generalized_extreme_value_distribution, Generalized Extreme Value (GEV) Distribution) 81 | +/ 82 | @safe pure nothrow @nogc 83 | T gevCDF(T)(const T x, const T mu, const T sigma, const T xi) 84 | if (isFloatingPoint!T) 85 | in (xi >= 0 || x <= mu - sigma / xi, "if xi is less than zero, x must be less than or equal to mu - sigma / xi") 86 | in (xi <= 0 || x >= mu - sigma / xi, "if xi is greater than zero, xi must be greater than or equal to mu - sigma / xi") 87 | { 88 | auto s = (x - mu) / sigma; 89 | if (xi.fabs <= T.min_normal) 90 | return exp(-exp(-s)); 91 | auto v = 1 + xi * s; 92 | if (v <= 0) 93 | return xi > 0 ? 0 : 1; 94 | auto a = pow(v, -1 / xi); 95 | return exp(-a); 96 | } 97 | 98 | /// 99 | version(mir_stat_test) 100 | @safe pure nothrow @nogc 101 | unittest 102 | { 103 | import mir.test: shouldApprox; 104 | 105 | gevCDF(-3, 2, 3, -0.5).shouldApprox == 0.034696685646156494; 106 | gevCDF(-1, 2, 3, +0.5).shouldApprox == 0.01831563888873418; 107 | gevCDF(-1, 2, 3, 0.0).shouldApprox == 0.06598803584531254; 108 | } 109 | 110 | // Checking v <= 0 branch 111 | version(mir_stat_test) 112 | @safe pure nothrow @nogc 113 | unittest 114 | { 115 | import mir.test: should; 116 | gevCDF(-1.0, 0, 1, 1).should == 0; 117 | gevCDF(1.0, 0, 1, -1).should == 1; 118 | } 119 | 120 | /++ 121 | Computes the generalized extreme value (GEV) complementary cumulatve distribution function (CCDF). 122 | 123 | Params: 124 | x = value to evaluate 125 | mu = location 126 | sigma = scale 127 | xi = shape 128 | 129 | See_also: 130 | $(LINK2 https://en.wikipedia.org/wiki/Generalized_extreme_value_distribution, Generalized Extreme Value (GEV) Distribution) 131 | +/ 132 | @safe pure nothrow @nogc 133 | T gevCCDF(T)(const T x, const T mu, const T sigma, const T xi) 134 | if (isFloatingPoint!T) 135 | in (xi >= 0 || x <= mu - sigma / xi, "if xi is less than zero, x must be less than or equal to mu - sigma / xi") 136 | in (xi <= 0 || x >= mu - sigma / xi, "if xi is greater than zero, xi must be greater than or equal to mu - sigma / xi") 137 | { 138 | return 1 - gevCDF(x, mu, sigma, xi); 139 | } 140 | 141 | /// 142 | version(mir_stat_test) 143 | @safe pure nothrow @nogc 144 | unittest 145 | { 146 | import mir.test: shouldApprox; 147 | 148 | gevCCDF(-3, 2, 3, -0.5).shouldApprox == 0.965303314353844; 149 | gevCCDF(-1, 2, 3, +0.5).shouldApprox == 0.981684361111266; 150 | gevCCDF(-1, 2, 3, 0.0).shouldApprox == 0.934011964154687; 151 | } 152 | 153 | // Checking v <= 0 branch 154 | version(mir_stat_test) 155 | @safe pure nothrow @nogc 156 | unittest 157 | { 158 | import mir.test: should; 159 | gevCCDF(-1.0, 0, 1, 1).should == 1; 160 | gevCCDF(1.0, 0, 1, -1).should == 0; 161 | } 162 | 163 | /++ 164 | Computes the generalized extreme value (GEV) inverse cumulative distribution function (InvCDF). 165 | 166 | Params: 167 | p = value to evaluate 168 | mu = location 169 | sigma = scale 170 | xi = shape 171 | 172 | See_also: 173 | $(LINK2 https://en.wikipedia.org/wiki/Generalized_extreme_value_distribution, Generalized Extreme Value (GEV) Distribution) 174 | +/ 175 | @safe pure nothrow @nogc 176 | T gevInvCDF(T)(const T p, const T mu, const T sigma, const T xi) 177 | if (isFloatingPoint!T) 178 | in (p >= 0, "p must be greater than or equal to 0") 179 | in (p <= 1, "p must be less than or equal to 1") 180 | { 181 | auto logp = log(p); 182 | if (xi.fabs <= T.min_normal) 183 | return mu - sigma * log(-logp); 184 | return mu + (pow(-logp, -xi) - 1) * sigma / xi; 185 | } 186 | 187 | /// 188 | version(mir_stat_test) 189 | @safe pure nothrow @nogc 190 | unittest 191 | { 192 | import mir.test: shouldApprox; 193 | 194 | gevInvCDF(0.034696685646156494, 2, 3, -0.5).shouldApprox == -3; 195 | gevInvCDF(0.01831563888873418, 2, 3, +0.5).shouldApprox == -1; 196 | gevInvCDF(0.06598803584531254, 2, 3, 0.0).shouldApprox == -1; 197 | } 198 | 199 | /++ 200 | Computes the generalized extreme value (GEV) log probability density function (LPDF). 201 | 202 | Params: 203 | x = value to evaluate 204 | mu = location 205 | sigma = scale 206 | xi = shape 207 | 208 | See_also: 209 | $(LINK2 https://en.wikipedia.org/wiki/Generalized_extreme_value_distribution, Generalized Extreme Value (GEV) Distribution) 210 | +/ 211 | @safe pure nothrow @nogc 212 | T gevLPDF(T)(const T x, const T mu, const T sigma, const T xi) 213 | if (isFloatingPoint!T) 214 | in (xi >= 0 || x <= mu - sigma / xi, "if xi is less than zero, x must be less than or equal to mu - sigma / xi") 215 | in (xi <= 0 || x >= mu - sigma / xi, "if xi is greater than zero, xi must be greater than or equal to mu - sigma / xi") 216 | { 217 | import mir.math.common: log; 218 | 219 | auto s = (x - mu) / sigma; 220 | if (xi.fabs <= T.min_normal) 221 | { 222 | auto t = exp(-s); 223 | return log(t) - t; 224 | } 225 | auto v = 1 + xi * s; 226 | if (v <= 0) 227 | return -double.infinity; 228 | auto a = pow(v, -1 / xi); 229 | return log(a) - a - log(v * sigma); 230 | } 231 | 232 | /// 233 | version(mir_stat_test) 234 | @safe pure nothrow @nogc 235 | unittest 236 | { 237 | import mir.test: shouldApprox; 238 | 239 | gevLPDF(-3, 2, 3, -0.5).shouldApprox == -3.85358759620891; 240 | gevLPDF(-1, 2, 3, +0.5).shouldApprox == -3.01917074698827; 241 | gevLPDF(-1, 2, 3, 0.0).shouldApprox == -1.71828182845905; 242 | } 243 | 244 | // Checking v <= 0 branch 245 | version(mir_stat_test) 246 | @safe pure nothrow @nogc 247 | unittest 248 | { 249 | import mir.test: shouldApprox; 250 | gevLPDF(-1.0, 0, 1, 1).shouldApprox == -double.infinity; 251 | } 252 | -------------------------------------------------------------------------------- /source/mir/stat/distribution/invcdf.d: -------------------------------------------------------------------------------- 1 | /++ 2 | This package publicly imports `mir.stat.distribution.*InvCDF` modules. 3 | 4 | $(BOOKTABLE , 5 |     $(TR 6 |         $(TH Functions) 7 |         $(TH Description) 8 |     ) 9 |     $(LEADINGROW Univariate Discrete Distributions) 10 |     $(TR $(TDNW $(SUBREF bernoulli, bernoulliInvCDF)) $(TD Bernoulli Inverse CDF )) 11 |     $(TR $(TDNW $(SUBREF binomial, binomialInvCDF)) $(TD Binomial Inverse CDF )) 12 |     $(TR $(TDNW $(SUBREF geometric, geometricInvCDF)) $(TD Geometric Inverse CDF )) 13 |     $(TR $(TDNW $(SUBREF hypergeometric, hypergeometricInvCDF)) $(TD Hypergeometric Inverse CDF )) 14 |     $(TR $(TDNW $(SUBREF negative_binomial, negativeBinomialInvCDF)) $(TD Negative Binomial Inverse CDF )) 15 |     $(TR $(TDNW $(SUBREF poisson, poissonInvCDF)) $(TD Poisson Inverse CDF )) 16 |     $(TR $(TDNW $(SUBREF uniform_discrete, uniformDiscreteInvCDF)) $(TD Discrete Uniform Inverse CDF )) 17 |     $(LEADINGROW Univariate Continuous Distributions) 18 |     $(TR $(TDNW $(SUBREF beta, betaInvCDF)) $(TD Beta Inverse CDF )) 19 |     $(TR $(TDNW $(SUBREF beta_proportion, betaProportionInvCDF)) $(TD Beta Proportion Inverse CDF )) 20 |     $(TR $(TDNW $(SUBREF cauchy, cauchyInvCDF)) $(TD Cauchy Inverse CDF )) 21 |     $(TR $(TDNW $(SUBREF chi2, chi2InvCDF)) $(TD Chi-squared Inverse CDF )) 22 |     $(TR $(TDNW $(SUBREF exponential, exponentialInvCDF)) $(TD Exponential Inverse CDF )) 23 |     $(TR $(TDNW $(SUBREF f, fInvCDF)) $(TD F Inverse CDF )) 24 |     $(TR $(TDNW $(SUBREF gamma, gammaInvCDF)) $(TD Gamma Inverse CDF )) 25 |     $(TR $(TDNW $(SUBREF generalized_pareto, generalizedParetoInvCDF)) $(TD Generalized Pareto Inverse CDF )) 26 |     $(TR $(TDNW $(SUBREF gev, gevInvCDF)) $(TD Generalized Extreme Value (GEV) Inverse CDF )) 27 |     $(TR $(TDNW $(SUBREF laplace, laplaceInvCDF)) $(TD Laplace Inverse CDF )) 28 |     $(TR $(TDNW $(SUBREF log_normal, logNormalInvCDF)) $(TD Log-normal Inverse CDF )) 29 |     $(TR $(TDNW $(SUBREF logistic, logisticInvCDF)) $(TD Logistic Inverse CDF )) 30 |     $(TR $(TDNW $(SUBREF normal, normalInvCDF)) $(TD Normal Inverse CDF )) 31 |     $(TR $(TDNW $(SUBREF pareto, paretoInvCDF)) $(TD Pareto Inverse CDF )) 32 |     $(TR $(TDNW $(SUBREF rayleigh, rayleighInvCDF)) $(TD Rayleigh Inverse CDF )) 33 |     $(TR $(TDNW $(SUBREF students_t, studentsTInvCDF)) $(TD Student's t Inverse CDF )) 34 |     $(TR $(TDNW $(SUBREF uniform, uniformInvCDF)) $(TD Continuous Uniform Inverse CDF )) 35 |     $(TR $(TDNW $(SUBREF weibull, weibullInvCDF)) $(TD Weibull Inverse CDF )) 36 |     $(LEADINGROW Multivariate Distributions) 37 |     $(TR $(TDNW $(SUBREF categorical, categoricalInvCDF)) $(TD Categorical Inverse CDF )) 38 | ) 39 | 40 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 41 | 42 | Authors: John Michael Hall, Ilya Yaroshenko 43 | 44 | Copyright: 2022-3 Mir Stat Authors. 45 | 46 | Macros: 47 | SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, stat, distribution, $1)$(NBSP) 48 | 49 | +/ 50 | 51 | module mir.stat.distribution.invcdf; 52 | 53 | /// 54 | public import mir.stat.distribution.bernoulli: bernoulliInvCDF; 55 | /// 56 | public import mir.stat.distribution.beta: betaInvCDF; 57 | /// 58 | public import mir.stat.distribution.beta_proportion: betaProportionInvCDF; 59 | /// 60 | public import mir.stat.distribution.binomial: binomialInvCDF; 61 | /// 62 | public import mir.stat.distribution.categorical: categoricalInvCDF; 63 | /// 64 | public import mir.stat.distribution.cauchy: cauchyInvCDF; 65 | /// 66 | public import mir.stat.distribution.chi2: chi2InvCDF; 67 | /// 68 | public import mir.stat.distribution.cornish_fisher: cornishFisherInvCDF; 69 | /// 70 | public import mir.stat.distribution.exponential: exponentialInvCDF; 71 | /// 72 | public import mir.stat.distribution.f: fInvCDF; 73 | /// 74 | public import mir.stat.distribution.gamma: gammaInvCDF; 75 | /// 76 | public import mir.stat.distribution.generalized_pareto: generalizedParetoInvCDF; 77 | /// 78 | public import mir.stat.distribution.geometric: geometricInvCDF; 79 | /// 80 | public import mir.stat.distribution.gev: gevInvCDF; 81 | /// 82 | public import mir.stat.distribution.hypergeometric: hypergeometricInvCDF; 83 | /// 84 | public import mir.stat.distribution.laplace: laplaceInvCDF; 85 | /// 86 | public import mir.stat.distribution.log_normal: logNormalInvCDF; 87 | /// 88 | public import mir.stat.distribution.logistic: logisticInvCDF; 89 | /// 90 | public import mir.stat.distribution.negative_binomial: negativeBinomialInvCDF; 91 | /// 92 | public import mir.stat.distribution.normal: normalInvCDF; 93 | /// 94 | public import mir.stat.distribution.pareto: paretoInvCDF; 95 | /// 96 | public import mir.stat.distribution.poisson: poissonInvCDF; 97 | /// 98 | public import mir.stat.distribution.rayleigh: rayleighInvCDF; 99 | /// 100 | public import mir.stat.distribution.students_t: studentsTInvCDF; 101 | /// 102 | public import mir.stat.distribution.uniform: uniformInvCDF; 103 | /// 104 | public import mir.stat.distribution.uniform_discrete: uniformDiscreteInvCDF; 105 | /// 106 | public import mir.stat.distribution.weibull: weibullInvCDF; 107 | -------------------------------------------------------------------------------- /source/mir/stat/distribution/laplace.d: -------------------------------------------------------------------------------- 1 | /++ 2 | This module contains algorithms for the $(LINK2 https://en.wikipedia.org/wiki/Laplace_distribution, Laplace Distribution). 3 | 4 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 | 6 | Authors: John Michael Hall 7 | 8 | Copyright: 2023 Mir Stat Authors. 9 | 10 | +/ 11 | 12 | module mir.stat.distribution.laplace; 13 | 14 | import mir.internal.utility: isFloatingPoint; 15 | 16 | /++ 17 | Computes the Laplace probability density function (PDF). 18 | 19 | Params: 20 | x = value to evaluate PDF 21 | 22 | See_also: 23 | $(LINK2 https://en.wikipedia.org/wiki/Laplace_distribution, Laplace Distribution) 24 | +/ 25 | @safe pure nothrow @nogc 26 | T laplacePDF(T)(const T x) 27 | if (isFloatingPoint!T) 28 | { 29 | import mir.math.common: exp, fabs; 30 | 31 | return 0.5 * exp(-fabs(x)); 32 | } 33 | 34 | /++ 35 | Ditto, with location and scale parameters (by standardizing `x`). 36 | 37 | Params: 38 | x = value to evaluate PDF 39 | location = location parameter 40 | scale = scale parameter 41 | +/ 42 | @safe pure nothrow @nogc 43 | T laplacePDF(T)(const T x, const T location, const T scale) 44 | if (isFloatingPoint!T) 45 | in (scale > 0, "scale must be greater than zero") 46 | { 47 | return laplacePDF((x - location) / scale) / scale; 48 | } 49 | 50 | /// 51 | version(mir_stat_test) 52 | @safe pure nothrow @nogc 53 | unittest 54 | { 55 | import mir.test: shouldApprox; 56 | 57 | laplacePDF(-2.0).shouldApprox == 0.06766764; 58 | laplacePDF(-1.0).shouldApprox == 0.1839397; 59 | laplacePDF(-0.5).shouldApprox == 0.3032653; 60 | laplacePDF(0.0).shouldApprox == 0.5; 61 | laplacePDF(0.5).shouldApprox == 0.3032653; 62 | laplacePDF(1.0).shouldApprox == 0.1839397; 63 | laplacePDF(2.0).shouldApprox == 0.06766764; 64 | 65 | // Can also provide location/scale parameters 66 | laplacePDF(-1.0, 2.0, 3.0).shouldApprox == 0.06131324; 67 | laplacePDF(1.0, 2.0, 3.0).shouldApprox == 0.1194219; 68 | laplacePDF(4.0, 2.0, 3.0).shouldApprox == 0.08556952; 69 | } 70 | 71 | /++ 72 | Computes the Laplace cumulative distribution function (CDF). 73 | 74 | Params: 75 | x = value to evaluate CDF 76 | 77 | See_also: 78 | $(LINK2 https://en.wikipedia.org/wiki/Laplace_distribution, Laplace Distribution) 79 | +/ 80 | @safe pure nothrow @nogc 81 | T laplaceCDF(T)(const T x) 82 | if (isFloatingPoint!T) 83 | { 84 | import mir.math.common: exp; 85 | 86 | if (x <= 0) { 87 | return 0.5 * exp(x); 88 | } else { 89 | return 1 - 0.5 * exp(-x); 90 | } 91 | } 92 | 93 | /++ 94 | Ditto, with location and scale parameters (by standardizing `x`). 95 | 96 | Params: 97 | x = value to evaluate CDF 98 | location = location parameter 99 | scale = scale parameter 100 | +/ 101 | @safe pure nothrow @nogc 102 | T laplaceCDF(T)(const T x, const T location, const T scale) 103 | if (isFloatingPoint!T) 104 | in (scale > 0, "scale must be greater than zero") 105 | { 106 | return laplaceCDF((x - location) / scale); 107 | } 108 | 109 | /// 110 | version(mir_stat_test) 111 | @safe pure nothrow @nogc 112 | unittest 113 | { 114 | import mir.test: shouldApprox; 115 | 116 | laplaceCDF(-2.0).shouldApprox == 0.06766764; 117 | laplaceCDF(-1.0).shouldApprox == 0.1839397; 118 | laplaceCDF(-0.5).shouldApprox == 0.3032653; 119 | laplaceCDF(0.0).shouldApprox == 0.5; 120 | laplaceCDF(0.5).shouldApprox == 0.6967347; 121 | laplaceCDF(1.0).shouldApprox == 0.8160603; 122 | laplaceCDF(2.0).shouldApprox == 0.9323324; 123 | 124 | // Can also provide location/scale parameters 125 | laplaceCDF(-1.0, 2.0, 3.0).shouldApprox == 0.1839397; 126 | laplaceCDF(1.0, 2.0, 3.0).shouldApprox == 0.3582657; 127 | laplaceCDF(4.0, 2.0, 3.0).shouldApprox == 0.7432914; 128 | } 129 | 130 | /++ 131 | Computes the Laplace complementary cumulative distribution function (CCDF). 132 | 133 | Params: 134 | x = value to evaluate CCDF 135 | 136 | See_also: 137 | $(LINK2 https://en.wikipedia.org/wiki/Laplace_distribution, Laplace Distribution) 138 | +/ 139 | @safe pure nothrow @nogc 140 | T laplaceCCDF(T)(const T x) 141 | if (isFloatingPoint!T) 142 | { 143 | import mir.math.common: exp; 144 | 145 | if (x <= 0) { 146 | return 1 - 0.5 * exp(x); 147 | } else { 148 | return 0.5 * exp(-x); 149 | } 150 | } 151 | 152 | /++ 153 | Ditto, with location and scale parameters (by standardizing `x`). 154 | 155 | Params: 156 | x = value to evaluate CCDF 157 | location = location parameter 158 | scale = scale parameter 159 | +/ 160 | T laplaceCCDF(T)(const T x, const T location, const T scale) 161 | if (isFloatingPoint!T) 162 | in (scale > 0, "scale must be greater than zero") 163 | { 164 | return laplaceCCDF((x - location) / scale); 165 | } 166 | 167 | /// 168 | version(mir_stat_test) 169 | @safe pure nothrow @nogc 170 | unittest 171 | { 172 | import mir.test: shouldApprox; 173 | 174 | laplaceCCDF(-2.0).shouldApprox == 0.9323324; 175 | laplaceCCDF(-1.0).shouldApprox == 0.8160603; 176 | laplaceCCDF(-0.5).shouldApprox == 0.6967347; 177 | laplaceCCDF(0.0).shouldApprox == 0.5; 178 | laplaceCCDF(0.5).shouldApprox == 0.3032653; 179 | laplaceCCDF(1.0).shouldApprox == 0.1839397; 180 | laplaceCCDF(2.0).shouldApprox == 0.06766764; 181 | 182 | // Can also provide location/scale parameters 183 | laplaceCCDF(-1.0, 2.0, 3.0).shouldApprox == 0.8160603; 184 | laplaceCCDF(1.0, 2.0, 3.0).shouldApprox == 0.6417343; 185 | laplaceCCDF(4.0, 2.0, 3.0).shouldApprox == 0.2567086; 186 | } 187 | 188 | /++ 189 | Computes the Laplace inverse cumulative distribution function (InvCDF). 190 | 191 | Params: 192 | p = value to evaluate InvCDF 193 | 194 | See_also: 195 | $(LINK2 https://en.wikipedia.org/wiki/Laplace_distribution, Laplace Distribution) 196 | +/ 197 | @safe pure nothrow @nogc 198 | T laplaceInvCDF(T)(const T p) 199 | if (isFloatingPoint!T) 200 | in (p >= 0, "p must be greater than or equal to 0") 201 | in (p <= 1, "p must be less than or equal to 1") 202 | { 203 | import mir.math.common: log; 204 | 205 | if (p <= 0.5) { 206 | return log(2 * p); 207 | } else { 208 | return -log(2 - 2 * p); 209 | } 210 | } 211 | 212 | /++ 213 | Ditto, with location and scale parameters (by standardizing `x`). 214 | 215 | Params: 216 | p = value to evaluate InvCDF 217 | location = location parameter 218 | scale = scale parameter 219 | 220 | See_also: 221 | $(LINK2 https://en.wikipedia.org/wiki/Logistic_distribution, Logistic probability distribution) 222 | +/ 223 | T laplaceInvCDF(T)(const T p, const T location, const T scale) 224 | if (isFloatingPoint!T) 225 | in (p >= 0, "p must be greater than or equal to 0") 226 | in (p <= 1, "p must be less than or equal to 1") 227 | in (scale > 0, "scale must be greater than zero") 228 | { 229 | return location + scale * laplaceInvCDF(p); 230 | } 231 | 232 | /// 233 | version(mir_stat_test) 234 | @safe pure nothrow @nogc 235 | unittest { 236 | import mir.test: shouldApprox; 237 | 238 | laplaceInvCDF(0.0).shouldApprox == -double.infinity; 239 | laplaceInvCDF(0.25).shouldApprox == -0.6931472; 240 | laplaceInvCDF(0.5).shouldApprox == 0.0; 241 | laplaceInvCDF(0.75).shouldApprox == 0.6931472; 242 | laplaceInvCDF(1.0).shouldApprox == double.infinity; 243 | 244 | // Can also provide location/scale parameters 245 | laplaceInvCDF(0.2, 2, 3).shouldApprox == -0.7488722; 246 | laplaceInvCDF(0.4, 2, 3).shouldApprox == 1.330569; 247 | laplaceInvCDF(0.6, 2, 3).shouldApprox == 2.669431; 248 | laplaceInvCDF(0.8, 2, 3).shouldApprox == 4.748872; 249 | } 250 | 251 | /++ 252 | Computes the Laplace log probability density function (LPDF). 253 | 254 | Params: 255 | x = value to evaluate LPDF 256 | 257 | See_also: 258 | $(LINK2 https://en.wikipedia.org/wiki/Laplace_distribution, Laplace Distribution) 259 | +/ 260 | @safe pure nothrow @nogc 261 | T laplaceLPDF(T)(const T x) 262 | if (isFloatingPoint!T) 263 | { 264 | import mir.math.common: fabs; 265 | import mir.math.constant: LN2; 266 | 267 | return -T(LN2) - fabs(x); 268 | } 269 | 270 | /++ 271 | Ditto, with location and scale parameters (by standardizing `x`). 272 | 273 | Params: 274 | x = value to evaluate LPDF 275 | location = location parameter 276 | scale = scale parameter 277 | 278 | See_also: 279 | $(LINK2 https://en.wikipedia.org/wiki/Laplace_distribution, Laplace Distribution) 280 | +/ 281 | T laplaceLPDF(T)(const T x, const T location, const T scale) 282 | if (isFloatingPoint!T) 283 | in (scale > 0, "shape must be greater than zero") 284 | { 285 | import mir.math.common: log; 286 | 287 | return laplaceLPDF((x - location) / scale) - log(scale); 288 | } 289 | 290 | /// 291 | version(mir_stat_test) 292 | @safe pure nothrow @nogc 293 | unittest 294 | { 295 | import mir.math.common: log; 296 | import mir.test: shouldApprox; 297 | 298 | laplaceLPDF(-2.0).shouldApprox == log(0.06766764); 299 | laplaceLPDF(-1.0).shouldApprox == log(0.1839397); 300 | laplaceLPDF(-0.5).shouldApprox == log(0.3032653); 301 | laplaceLPDF(0.0).shouldApprox == log(0.5); 302 | laplaceLPDF(0.5).shouldApprox == log(0.3032653); 303 | laplaceLPDF(1.0).shouldApprox == log(0.1839397); 304 | laplaceLPDF(2.0).shouldApprox == log(0.06766764); 305 | 306 | // Can also provide location/scale parameters 307 | laplaceLPDF(-1.0, 2.0, 3.0).shouldApprox == log(0.06131324); 308 | laplaceLPDF(1.0, 2.0, 3.0).shouldApprox == log(0.1194219); 309 | laplaceLPDF(4.0, 2.0, 3.0).shouldApprox == log(0.08556952); 310 | } 311 | -------------------------------------------------------------------------------- /source/mir/stat/distribution/log_normal.d: -------------------------------------------------------------------------------- 1 | /++ 2 | This module contains algorithms for the $(LINK2 https://en.wikipedia.org/wiki/Log-normal_distribution, Log-normal Distribution). 3 | 4 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 | 6 | Authors: John Michael Hall 7 | 8 | Copyright: 2022-3 Mir Stat Authors. 9 | 10 | +/ 11 | 12 | module mir.stat.distribution.log_normal; 13 | 14 | import mir.internal.utility: isFloatingPoint; 15 | 16 | /++ 17 | Computes the Log-normal probability density function (PDF). 18 | 19 | Params: 20 | x = value to evaluate PDF 21 | 22 | See_also: 23 | $(LINK2 https://en.wikipedia.org/wiki/Log-normal_distribution, Log-normal Distribution) 24 | +/ 25 | @safe pure nothrow @nogc 26 | T logNormalPDF(T)(const T x) 27 | if (isFloatingPoint!T) 28 | in (x >= 0, "x must be greater than or equal to zero") 29 | { 30 | import mir.math.common: log; 31 | import mir.stat.distribution.normal: normalPDF; 32 | if (x == 0) return 0; 33 | return x.log.normalPDF / x; 34 | } 35 | 36 | /++ 37 | Ditto, with location and scale parameters (by standardizing `x`). 38 | 39 | Params: 40 | x = value to evaluate PDF 41 | mean = location parameter 42 | stdDev = scale parameter 43 | +/ 44 | @safe pure nothrow @nogc 45 | T logNormalPDF(T)(const T x, const T mean, const T stdDev) 46 | if (isFloatingPoint!T) 47 | in (x >= 0, "x must be greater than or equal to zero") 48 | in (stdDev > 0, "stdDev must be greater than zero") 49 | { 50 | import mir.math.common: log; 51 | import mir.stat.distribution.normal: normalPDF; 52 | if (x == 0) return 0; 53 | return x.log.normalPDF(mean, stdDev) / x; 54 | } 55 | 56 | /// 57 | version(mir_stat_test) 58 | @safe pure nothrow @nogc 59 | unittest { 60 | import mir.test: shouldApprox; 61 | 62 | logNormalPDF(1.0).shouldApprox == 0.3989423; 63 | logNormalPDF(2.0).shouldApprox == 0.156874; 64 | logNormalPDF(3.0).shouldApprox == 0.07272826; 65 | 66 | // Can include location/scale 67 | logNormalPDF(1.0, 1, 2).shouldApprox == 0.1760327; 68 | logNormalPDF(2.0, 1, 2).shouldApprox == 0.09856858; 69 | logNormalPDF(3.0, 1, 2).shouldApprox == 0.06640961; 70 | } 71 | 72 | // check zero or near zero 73 | version(mir_stat_test) 74 | @safe pure nothrow @nogc 75 | unittest { 76 | import mir.test: shouldApprox; 77 | 78 | logNormalPDF(0.0).shouldApprox == 0.0; 79 | logNormalPDF(double.min_normal * double.epsilon).shouldApprox == 0.0; 80 | logNormalPDF(double.min_normal).shouldApprox == 0.0; 81 | 82 | logNormalPDF(0.0, 1, 2).shouldApprox == 0.0; 83 | logNormalPDF(double.min_normal * double.epsilon, 1, 2).shouldApprox == 0.0; 84 | logNormalPDF(double.min_normal, 1, 2).shouldApprox == 0.0; 85 | } 86 | 87 | /++ 88 | Computes the Log-normal cumulative distribution function (CDF). 89 | 90 | Params: 91 | x = value to evaluate CDF 92 | 93 | See_also: 94 | $(LINK2 https://en.wikipedia.org/wiki/Log-normal_distribution, Log-normal Distribution) 95 | +/ 96 | @safe pure nothrow @nogc 97 | T logNormalCDF(T)(const T x) 98 | if (isFloatingPoint!T) 99 | in (x >= 0, "x must be greater than or equal to zero") 100 | { 101 | import mir.math.common: log; 102 | import mir.stat.distribution.normal: normalCDF; 103 | return x.log.normalCDF; 104 | } 105 | 106 | /++ 107 | Ditto, with location and scale parameters (by standardizing `x`). 108 | 109 | Params: 110 | x = value to evaluate CDF 111 | mean = location parameter 112 | stdDev = scale parameter 113 | +/ 114 | @safe pure nothrow @nogc 115 | T logNormalCDF(T)(const T x, const T mean, const T stdDev) 116 | if (isFloatingPoint!T) 117 | in (x >= 0, "x must be greater than or equal to zero") 118 | in (stdDev > 0, "stdDev must be greater than zero") 119 | { 120 | import mir.math.common: log; 121 | import mir.stat.distribution.normal: normalCDF; 122 | return x.log.normalCDF(mean, stdDev); 123 | } 124 | 125 | /// 126 | version(mir_stat_test) 127 | @safe pure nothrow @nogc 128 | unittest { 129 | import mir.test: shouldApprox; 130 | 131 | logNormalCDF(0.0).shouldApprox == 0; 132 | logNormalCDF(1.0).shouldApprox == 0.5; 133 | logNormalCDF(2.0).shouldApprox == 0.7558914; 134 | logNormalCDF(3.0).shouldApprox == 0.8640314; 135 | 136 | // Can include location/scale 137 | logNormalCDF(0.0, 1, 2).shouldApprox == 0; 138 | logNormalCDF(1.0, 1, 2).shouldApprox == 0.3085375; 139 | logNormalCDF(2.0, 1, 2).shouldApprox == 0.439031; 140 | logNormalCDF(3.0, 1, 2).shouldApprox == 0.5196623; 141 | } 142 | 143 | /++ 144 | Computes the Student's t complementary cumulative distribution function (CCDF). 145 | 146 | Params: 147 | x = value to evaluate CCDF 148 | 149 | See_also: 150 | $(LINK2 https://en.wikipedia.org/wiki/Log-normal_distribution, Log-normal Distribution) 151 | +/ 152 | @safe pure nothrow @nogc 153 | T logNormalCCDF(T)(const T x) 154 | if (isFloatingPoint!T) 155 | in (x >= 0, "x must be greater than or equal to zero") 156 | { 157 | import mir.math.common: log; 158 | import mir.stat.distribution.normal: normalCCDF; 159 | return x.log.normalCCDF; 160 | } 161 | 162 | /++ 163 | Ditto, with location and scale parameters (by standardizing `x`). 164 | 165 | Params: 166 | x = value to evaluate CCDF 167 | mean = location parameter 168 | stdDev = scale parameter 169 | +/ 170 | @safe pure nothrow @nogc 171 | T logNormalCCDF(T)(const T x, const T mean, const T stdDev) 172 | if (isFloatingPoint!T) 173 | in (x >= 0, "x must be greater than or equal to zero") 174 | in (stdDev > 0, "stdDev must be greater than zero") 175 | { 176 | import mir.math.common: log; 177 | import mir.stat.distribution.normal: normalCCDF; 178 | return x.log.normalCCDF(mean, stdDev); 179 | } 180 | 181 | /// 182 | version(mir_stat_test) 183 | @safe pure nothrow @nogc 184 | unittest { 185 | import mir.test: shouldApprox; 186 | 187 | logNormalCCDF(0.0).shouldApprox == 1; 188 | logNormalCCDF(1.0).shouldApprox == 0.5; 189 | logNormalCCDF(2.0).shouldApprox == 0.2441086; 190 | logNormalCCDF(3.0).shouldApprox == 0.1359686; 191 | 192 | // Can include location/scale 193 | logNormalCCDF(0.0, 1, 2).shouldApprox == 1; 194 | logNormalCCDF(1.0, 1, 2).shouldApprox == 0.6914625; 195 | logNormalCCDF(2.0, 1, 2).shouldApprox == 0.560969; 196 | logNormalCCDF(3.0, 1, 2).shouldApprox == 0.4803377; 197 | } 198 | 199 | /++ 200 | Computes the Log-normal inverse cumulative distribution function (InvCDF). 201 | 202 | Params: 203 | p = value to evaluate InvCDF 204 | 205 | See_also: 206 | $(LINK2 https://en.wikipedia.org/wiki/Log-normal_distribution, Log-normal Distribution) 207 | +/ 208 | @safe pure nothrow @nogc 209 | T logNormalInvCDF(T)(const T p) 210 | if (isFloatingPoint!T) 211 | in (p >= 0, "p must be greater than or equal to 0") 212 | in (p <= 1, "p must be less than or equal to 1") 213 | out (r; r >= 0, "return must be greater than or equal to zero") 214 | { 215 | import mir.math.common: exp; 216 | import mir.stat.distribution.normal: normalInvCDF; 217 | return p.normalInvCDF.exp; 218 | } 219 | 220 | /++ 221 | Ditto, with location and scale parameters (by standardizing `x`). 222 | 223 | Params: 224 | p = value to evaluate InvCDF 225 | mean = location parameter 226 | stdDev = scale parameter 227 | +/ 228 | @safe pure nothrow @nogc 229 | T logNormalInvCDF(T)(const T p, const T mean, const T stdDev) 230 | if (isFloatingPoint!T) 231 | in (p >= 0, "p must be greater than or equal to 0") 232 | in (p <= 1, "p must be less than or equal to 1") 233 | in (stdDev > 0, "stdDev must be greater than zero") 234 | out (r; r >= 0, "return must be greater than or equal to zero") 235 | { 236 | import mir.math.common: exp; 237 | import mir.stat.distribution.normal: normalInvCDF; 238 | return p.normalInvCDF(mean, stdDev).exp; 239 | } 240 | 241 | /// 242 | version(mir_stat_test) 243 | @safe pure nothrow @nogc 244 | unittest { 245 | import mir.test: shouldApprox; 246 | 247 | logNormalInvCDF(0.00).shouldApprox == 0; 248 | logNormalInvCDF(0.25).shouldApprox == 0.5094163; 249 | logNormalInvCDF(0.50).shouldApprox == 1; 250 | logNormalInvCDF(0.75).shouldApprox == 1.963031; 251 | logNormalInvCDF(1.00).shouldApprox == double.infinity; 252 | 253 | // Can include location/scale 254 | logNormalInvCDF(0.00, 1, 2).shouldApprox == 0; 255 | logNormalInvCDF(0.25, 1, 2).shouldApprox == 0.7054076; 256 | logNormalInvCDF(0.50, 1, 2).shouldApprox == 2.718282; 257 | logNormalInvCDF(0.75, 1, 2).shouldApprox == 10.47487; 258 | logNormalInvCDF(1.00, 1, 2).shouldApprox == double.infinity; 259 | } 260 | 261 | /++ 262 | Computes the Log-normal log probability density function (LPDF). 263 | 264 | Params: 265 | x = value to evaluate LPDF 266 | 267 | See_also: 268 | $(LINK2 https://en.wikipedia.org/wiki/Log-normal_distribution, Log-normal Distribution) 269 | +/ 270 | @safe pure nothrow @nogc 271 | T logNormalLPDF(T)(const T x) 272 | if (isFloatingPoint!T) 273 | in (x >= 0, "x must be greater than or equal to zero") 274 | { 275 | import mir.math.common: log; 276 | import mir.stat.distribution.normal: normalLPDF; 277 | if (x == 0) return -T.infinity; 278 | return x.log.normalLPDF - log(x); 279 | } 280 | 281 | /++ 282 | Ditto, with location and scale parameters (by standardizing `x`). 283 | 284 | Params: 285 | x = value to evaluate LPDF 286 | mean = location parameter 287 | stdDev = scale parameter 288 | +/ 289 | @safe pure nothrow @nogc 290 | T logNormalLPDF(T)(const T x, const T mean, const T stdDev) 291 | if (isFloatingPoint!T) 292 | in (x >= 0, "x must be greater than or equal to zero") 293 | in (stdDev > 0, "stdDev must be greater than zero") 294 | { 295 | import mir.math.common: log; 296 | import mir.stat.distribution.normal: normalLPDF; 297 | if (x == 0) return -T.infinity; 298 | return x.log.normalLPDF(mean, stdDev) - log(x); 299 | } 300 | 301 | /// 302 | version(mir_stat_test) 303 | @safe pure nothrow @nogc 304 | unittest { 305 | import mir.math.common: log; 306 | import mir.test: shouldApprox; 307 | 308 | logNormalLPDF(1.0).shouldApprox == log(0.3989423); 309 | logNormalLPDF(2.0).shouldApprox == log(0.156874); 310 | logNormalLPDF(3.0).shouldApprox == log(0.07272826); 311 | 312 | // Can include location/scale 313 | logNormalLPDF(1.0, 1, 2).shouldApprox == log(0.1760327); 314 | logNormalLPDF(2.0, 1, 2).shouldApprox == log(0.09856858); 315 | logNormalLPDF(3.0, 1, 2).shouldApprox == log(0.06640961); 316 | } 317 | 318 | 319 | // check zero 320 | version(mir_stat_test) 321 | @safe pure nothrow @nogc 322 | unittest { 323 | import mir.test: shouldApprox; 324 | 325 | logNormalLPDF(0.0).shouldApprox == -double.infinity; 326 | logNormalLPDF(0.0, 1, 2).shouldApprox == -double.infinity; 327 | } 328 | -------------------------------------------------------------------------------- /source/mir/stat/distribution/logistic.d: -------------------------------------------------------------------------------- 1 | /++ 2 | This module contains algorithms for the $(LINK2 https://en.wikipedia.org/wiki/Logistic_distribution, Logistic Distribution). 3 | 4 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 | 6 | Authors: John Michael Hall 7 | 8 | Copyright: 2023 Mir Stat Authors. 9 | 10 | +/ 11 | 12 | module mir.stat.distribution.logistic; 13 | 14 | import mir.internal.utility: isFloatingPoint; 15 | 16 | /++ 17 | Computes the Logistic probability density function (PDF). 18 | 19 | Params: 20 | x = value to evaluate PDF 21 | 22 | See_also: 23 | $(LINK2 https://en.wikipedia.org/wiki/Logistic_distribution, Logistic Distribution) 24 | +/ 25 | @safe pure nothrow @nogc 26 | T logisticPDF(T)(const T x) 27 | if (isFloatingPoint!T) 28 | { 29 | import mir.math.common: exp; 30 | 31 | const T exp_x = exp(-x); 32 | return exp_x / ((1 + exp_x) * (1 + exp_x)); 33 | } 34 | 35 | /++ 36 | Ditto, with location and scale parameters (by standardizing `x`). 37 | 38 | Params: 39 | x = value to evaluate PDF 40 | location = location parameter 41 | scale = scale parameter 42 | +/ 43 | @safe pure nothrow @nogc 44 | T logisticPDF(T)(const T x, const T location, const T scale) 45 | if (isFloatingPoint!T) 46 | in (scale > 0, "scale must be greater than zero") 47 | { 48 | return logisticPDF((x - location) / scale) / scale; 49 | } 50 | 51 | /// 52 | version(mir_stat_test) 53 | @safe pure nothrow @nogc 54 | unittest 55 | { 56 | import mir.test: shouldApprox; 57 | 58 | logisticPDF(-2.0).shouldApprox == 0.1049936; 59 | logisticPDF(-1.0).shouldApprox == 0.1966119; 60 | logisticPDF(-0.5).shouldApprox == 0.2350037; 61 | logisticPDF(0.0).shouldApprox == 0.25; 62 | logisticPDF(0.5).shouldApprox == 0.2350037; 63 | logisticPDF(1.0).shouldApprox == 0.1966119; 64 | logisticPDF(2.0).shouldApprox == 0.1049936; 65 | 66 | // Can also provide location/scale parameters 67 | logisticPDF(-1.0, 2.0, 3.0).shouldApprox == 0.06553731; 68 | logisticPDF(1.0, 2.0, 3.0).shouldApprox == 0.08106072; 69 | logisticPDF(4.0, 2.0, 3.0).shouldApprox == 0.07471913; 70 | } 71 | 72 | /++ 73 | Computes the Logistic cumulative distribution function (CDF). 74 | 75 | Params: 76 | x = value to evaluate CDF 77 | 78 | See_also: 79 | $(LINK2 https://en.wikipedia.org/wiki/Logistic_distribution, Logistic Distribution) 80 | +/ 81 | @safe pure nothrow @nogc 82 | T logisticCDF(T)(const T x) 83 | if (isFloatingPoint!T) 84 | { 85 | import mir.math.common: exp; 86 | 87 | return 1 / (1 + exp(-x)); 88 | } 89 | 90 | /++ 91 | Ditto, with location and scale parameters (by standardizing `x`). 92 | 93 | Params: 94 | x = value to evaluate CDF 95 | location = location parameter 96 | scale = scale parameter 97 | +/ 98 | @safe pure nothrow @nogc 99 | T logisticCDF(T)(const T x, const T location, const T scale) 100 | if (isFloatingPoint!T) 101 | in (scale > 0, "scale must be greater than zero") 102 | { 103 | return logisticCDF((x - location) / scale); 104 | } 105 | 106 | /// 107 | version(mir_stat_test) 108 | @safe pure nothrow @nogc 109 | unittest 110 | { 111 | import mir.test: shouldApprox; 112 | 113 | logisticCDF(-2.0).shouldApprox == 0.1192029; 114 | logisticCDF(-1.0).shouldApprox == 0.2689414; 115 | logisticCDF(-0.5).shouldApprox == 0.3775407; 116 | logisticCDF(0.0).shouldApprox == 0.5; 117 | logisticCDF(0.5).shouldApprox == 0.6224593; 118 | logisticCDF(1.0).shouldApprox == 0.7310586; 119 | logisticCDF(2.0).shouldApprox == 0.8807971; 120 | 121 | // Can also provide location/scale parameters 122 | logisticCDF(-1.0, 2.0, 3.0).shouldApprox == 0.2689414; 123 | logisticCDF(1.0, 2.0, 3.0).shouldApprox == 0.4174298; 124 | logisticCDF(4.0, 2.0, 3.0).shouldApprox == 0.6607564; 125 | } 126 | 127 | /++ 128 | Computes the Logistic complementary cumulative distribution function (CCDF). 129 | 130 | Params: 131 | x = value to evaluate CCDF 132 | 133 | See_also: 134 | $(LINK2 https://en.wikipedia.org/wiki/Logistic_distribution, Logistic Distribution) 135 | +/ 136 | @safe pure nothrow @nogc 137 | T logisticCCDF(T)(const T x) 138 | if (isFloatingPoint!T) 139 | { 140 | import mir.math.common: exp; 141 | 142 | const T exp_x = exp(-x); 143 | return exp_x / (1 + exp_x); 144 | } 145 | 146 | /++ 147 | Ditto, with location and scale parameters (by standardizing `x`). 148 | 149 | Params: 150 | x = value to evaluate CCDF 151 | location = location parameter 152 | scale = scale parameter 153 | +/ 154 | @safe pure nothrow @nogc 155 | T logisticCCDF(T)(const T x, const T location, const T scale) 156 | if (isFloatingPoint!T) 157 | in (scale > 0, "scale must be greater than zero") 158 | { 159 | return logisticCCDF((x - location) / scale); 160 | } 161 | 162 | /// 163 | version(mir_stat_test) 164 | @safe pure nothrow @nogc 165 | unittest 166 | { 167 | import mir.test: shouldApprox; 168 | 169 | logisticCCDF(-2.0).shouldApprox == 0.8807971; 170 | logisticCCDF(-1.0).shouldApprox == 0.7310586; 171 | logisticCCDF(-0.5).shouldApprox == 0.6224593; 172 | logisticCCDF(0.0).shouldApprox == 0.5; 173 | logisticCCDF(0.5).shouldApprox == 0.3775407; 174 | logisticCCDF(1.0).shouldApprox == 0.2689414; 175 | logisticCCDF(2.0).shouldApprox == 0.1192029; 176 | 177 | // Can also provide location/scale parameters 178 | logisticCCDF(-1.0, 2.0, 3.0).shouldApprox == 0.7310586; 179 | logisticCCDF(1.0, 2.0, 3.0).shouldApprox == 0.5825702; 180 | logisticCCDF(4.0, 2.0, 3.0).shouldApprox == 0.3392436; 181 | } 182 | 183 | /++ 184 | Computes the Logistic inverse cumulative distribution function (InvCDF). 185 | 186 | Params: 187 | p = value to evaluate InvCDF 188 | 189 | See_also: 190 | $(LINK2 https://en.wikipedia.org/wiki/Logistic_distribution, Logistic Distribution) 191 | +/ 192 | @safe pure nothrow @nogc 193 | T logisticInvCDF(T)(const T p) 194 | if (isFloatingPoint!T) 195 | in (p >= 0, "p must be greater than or equal to 0") 196 | in (p <= 1, "p must be less than or equal to 1") 197 | { 198 | import mir.math.common: log; 199 | 200 | return log(p / (1 - p)); 201 | } 202 | 203 | /++ 204 | Ditto, with location and scale parameters (by standardizing `x`). 205 | 206 | Params: 207 | p = value to evaluate InvCDF 208 | location = location parameter 209 | scale = scale parameter 210 | 211 | See_also: 212 | $(LINK2 https://en.wikipedia.org/wiki/Logistic_distribution, Logistic Distribution) 213 | +/ 214 | @safe pure nothrow @nogc 215 | T logisticInvCDF(T)(const T p, const T location, const T scale) 216 | if (isFloatingPoint!T) 217 | in (p >= 0, "p must be greater than or equal to 0") 218 | in (p <= 1, "p must be less than or equal to 1") 219 | in (scale > 0, "scale must be greater than zero") 220 | { 221 | return location + scale * logisticInvCDF(p); 222 | } 223 | 224 | /// 225 | version(mir_stat_test) 226 | @safe pure nothrow @nogc 227 | unittest { 228 | import mir.test: shouldApprox; 229 | 230 | logisticInvCDF(0.0).shouldApprox == -double.infinity; 231 | logisticInvCDF(0.25).shouldApprox == -1.098612; 232 | logisticInvCDF(0.5).shouldApprox == 0.0; 233 | logisticInvCDF(0.75).shouldApprox == 1.098612; 234 | logisticInvCDF(1.0).shouldApprox == double.infinity; 235 | 236 | // Can also provide location/scale parameters 237 | logisticInvCDF(0.2, 2, 3).shouldApprox == -2.158883; 238 | logisticInvCDF(0.4, 2, 3).shouldApprox == 0.7836047; 239 | logisticInvCDF(0.6, 2, 3).shouldApprox == 3.216395; 240 | logisticInvCDF(0.8, 2, 3).shouldApprox == 6.158883; 241 | } 242 | 243 | /++ 244 | Computes the Logistic log probability density function (LPDF). 245 | 246 | Params: 247 | x = value to evaluate LPDF 248 | 249 | See_also: 250 | $(LINK2 https://en.wikipedia.org/wiki/Logistic_distribution, Logistic Distribution) 251 | +/ 252 | @safe pure nothrow @nogc 253 | T logisticLPDF(T)(const T x) 254 | if (isFloatingPoint!T) 255 | { 256 | import mir.math.common: exp, log; 257 | 258 | return -x - 2 * log(1 + exp(-x)); 259 | } 260 | 261 | /++ 262 | Ditto, with location and scale parameters (by standardizing `x`). 263 | 264 | Params: 265 | x = value to evaluate LPDF 266 | location = location parameter 267 | scale = scale parameter 268 | 269 | See_also: 270 | $(LINK2 https://en.wikipedia.org/wiki/Logistic_distribution, Logistic Distribution) 271 | +/ 272 | @safe pure nothrow @nogc 273 | T logisticLPDF(T)(const T x, const T location, const T scale) 274 | if (isFloatingPoint!T) 275 | in (scale > 0, "scale must be greater than zero") 276 | { 277 | import mir.math.common: log; 278 | 279 | return logisticLPDF((x - location) / scale) - log(scale); 280 | } 281 | 282 | /// 283 | version(mir_stat_test) 284 | @safe pure nothrow @nogc 285 | unittest 286 | { 287 | import mir.math.common: log; 288 | import mir.test: shouldApprox; 289 | 290 | logisticLPDF(-2.0).shouldApprox == log(0.1049936); 291 | logisticLPDF(-1.0).shouldApprox == log(0.1966119); 292 | logisticLPDF(-0.5).shouldApprox == log(0.2350037); 293 | logisticLPDF(0.0).shouldApprox == log(0.25); 294 | logisticLPDF(0.5).shouldApprox == log(0.2350037); 295 | logisticLPDF(1.0).shouldApprox == log(0.1966119); 296 | logisticLPDF(2.0).shouldApprox == log(0.1049936); 297 | 298 | // Can also provide location/scale parameters 299 | logisticLPDF(-1.0, 2.0, 3.0).shouldApprox == log(0.06553731); 300 | logisticLPDF(1.0, 2.0, 3.0).shouldApprox == log(0.08106072); 301 | logisticLPDF(4.0, 2.0, 3.0).shouldApprox == log(0.07471913); 302 | } 303 | -------------------------------------------------------------------------------- /source/mir/stat/distribution/normal.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Normal Distribution 3 | * 4 | * Copyright: Based on the CEPHES math library, which is 5 | * Copyright (C) 1994 Stephen L. Moshier (moshier@world.std.com). 6 | * Authors: Stephen L. Moshier, ported to D by Don Clugston and David Nadlinger. Adopted to Mir by Ilya Yaroshenko. 7 | */ 8 | /++ 9 | This module contains algorithms for the $(LINK2 https://en.wikipedia.org/wiki/Normal_distribution, Normal Distribution). 10 | 11 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 12 | 13 | Authors: John Michael Hall 14 | 15 | Copyright: 2022-3 Mir Stat Authors. 16 | 17 | +/ 18 | 19 | module mir.stat.distribution.normal; 20 | 21 | /// 22 | public import mir.math.func.normal: normalPDF, normalCDF, normalInvCDF; 23 | 24 | import mir.internal.utility: isFloatingPoint; 25 | 26 | /++ 27 | Computes the normal complementary cumulative distribution function (CCDF) 28 | 29 | Params: 30 | x = value to evaluate CCDF 31 | mean = mean 32 | stdDev = standard deviation 33 | 34 | See_also: 35 | $(LINK2 https://en.wikipedia.org/wiki/Normal_distribution, Normal Distribution) 36 | +/ 37 | @safe pure nothrow @nogc 38 | T normalCCDF(T)(const T x, const T mean, const T stdDev) 39 | if (isFloatingPoint!T) 40 | { 41 | return normalCCDF((x - mean) / stdDev); 42 | } 43 | 44 | /// ditto 45 | @safe pure nothrow @nogc 46 | T normalCCDF(T)(const T a) 47 | if (isFloatingPoint!T) 48 | { 49 | return normalCDF(-a); 50 | } 51 | 52 | /// 53 | version(mir_stat_test) 54 | @safe pure nothrow @nogc 55 | unittest { 56 | import mir.math.common: approxEqual; 57 | 58 | assert(0.5.normalCCDF.approxEqual(1 - normalCDF(0.5))); 59 | assert(0.5.normalCCDF(0, 1.5).approxEqual(1 - normalCDF(0.5, 0, 1.5))); 60 | assert(1.5.normalCCDF(1, 3).approxEqual(1 - normalCDF(1.5, 1, 3))); 61 | } 62 | 63 | version(mir_stat_test) 64 | @safe pure nothrow @nogc 65 | unittest { 66 | import mir.math.common: approxEqual; 67 | 68 | assert((-3.0).normalCCDF.approxEqual(1 - normalCDF(-3.0))); 69 | assert(3.0.normalCCDF.approxEqual(1 - normalCDF(3.0))); 70 | assert((-7.0).normalCCDF(1, 4).approxEqual(1 - normalCDF(-7.0, 1, 4))); 71 | assert(9.0.normalCCDF(1, 4).approxEqual(1 - normalCDF(9.0, 1, 4))); 72 | } 73 | 74 | version(mir_stat_test) 75 | @safe pure nothrow @nogc 76 | unittest { 77 | import mir.math.common: approxEqual; 78 | assert(normalCCDF(double.infinity).approxEqual(1 - normalCDF(double.infinity))); 79 | assert(normalCCDF(-double.infinity).approxEqual(1 - normalCDF(-double.infinity))); 80 | } 81 | 82 | /++ 83 | Computes the normal log probability density function (LPDF) 84 | 85 | Params: 86 | x = value to evaluate LPDF 87 | mean = mean 88 | stdDev = standard deviation 89 | 90 | See_also: 91 | $(LINK2 https://en.wikipedia.org/wiki/Normal_distribution, Normal Distribution) 92 | +/ 93 | @safe pure nothrow @nogc 94 | T normalLPDF(T)(const T x, const T mean, const T stdDev) 95 | if (isFloatingPoint!T) 96 | { 97 | import mir.math.common: log; 98 | return normalLPDF((x - mean) / stdDev) - log(stdDev); 99 | } 100 | 101 | /// ditto 102 | @safe pure nothrow @nogc 103 | T normalLPDF(T)(const T x) 104 | if (isFloatingPoint!T) 105 | { 106 | import mir.math.common: pow; 107 | import mir.stat.constant: LOGSQRT2PI; 108 | 109 | return -0.5 * pow(x, 2) - T(LOGSQRT2PI); 110 | } 111 | 112 | /// 113 | version(mir_stat_test) 114 | @safe pure nothrow @nogc 115 | unittest { 116 | import mir.math.common: approxEqual, log; 117 | assert(0.5.normalLPDF.approxEqual(log(normalPDF(0.5)))); 118 | assert(0.5.normalLPDF(0, 1.5).approxEqual(log(normalPDF(0.5, 0, 1.5)))); 119 | assert(1.5.normalLPDF(1, 3).approxEqual(log(normalPDF(1.5, 1, 3)))); 120 | } 121 | -------------------------------------------------------------------------------- /source/mir/stat/distribution/package.d: -------------------------------------------------------------------------------- 1 | /++ 2 | This package publicly imports `mir.stat.distribution.*` modules. 3 | 4 | ------ 5 | import mir.math.common: pow; 6 | import mir.stat.distribution; 7 | import mir.test: shouldApprox; 8 | 9 | 4.binomialPMF(6, 2.0 / 3).shouldApprox == (15.0 * pow(2.0 / 3, 4) * pow(1.0 / 3, 2)); 10 | ------ 11 | 12 | Each individual distribution module contains - where feasible - functions for: 13 | - Probability density/mass functions (e.g. `mir.stat.distribution.*PDF`/`mir.stat.distribution.*PMF`) 14 | - Cumulative distriution functions (e.g. `mir.stat.distribution.*CDF`) 15 | - Complementary cumulative distribution functions (e.g. `mir.stat.distribution.*CCDF`) 16 | - Inverse cumulative distribution functions (e.g. `mir.stat.distribution.*InvCDF`) 17 | - Log probaiity density/mass functions (e.g. `mir.stat.distribution.*LPDF`/`mir.stat.distribution.*LPMF`) 18 | 19 | In addition, convenience modules are provided ($(MREF mir,stat,distribution,pdf), 20 | $(MREF mir,stat,distribution,cdf), $(MREF mir,stat,distribution,invcdf)) that publicly 21 | import only the respective functions from each individual distribution module 22 | (note: the pdf module also contains pmfs). 23 | 24 | Some (discrete) distributions include multiple algorithms for calculating the 25 | included functions. The default is a direct calculation with others being 26 | approximations. As a convention, these modules leave it to the user to determine 27 | when to switch between the different approximations. Care should be taken if 28 | more extreme parameters are used as it can have an impact on speed. 29 | 30 | $(BOOKTABLE , 31 |     $(TR 32 |         $(TH Modules) 33 |         $(TH Description) 34 |     ) 35 |     $(LEADINGROW Convenience Modules (with public imports)) 36 |     $(TR $(TDNW $(MREF mir,stat,distribution)) $(TD Statistical Distributions )) 37 |     $(TR $(TDNW $(MREF mir,stat,distribution,pdf)) $(TD Probability Density/Mass Functions )) 38 |     $(TR $(TDNW $(MREF mir,stat,distribution,cdf)) $(TD Cumulative Distribution Functions )) 39 |     $(TR $(TDNW $(MREF mir,stat,distribution,invcdf)) $(TD Inverse Cumulative Distribution Functions )) 40 |     $(LEADINGROW Univariate Discrete Distributions) 41 |     $(TR $(TDNW $(MREF mir,stat,distribution,bernoulli)) $(TD Bernoulli Distribution )) 42 |     $(TR $(TDNW $(MREF mir,stat,distribution,binomial)) $(TD Binomial Distribution )) 43 |     $(TR $(TDNW $(MREF mir,stat,distribution,geometric)) $(TD Geometric Distribution )) 44 |     $(TR $(TDNW $(MREF mir,stat,distribution,hypergeometric)) $(TD Hypergeometric Distribution )) 45 |     $(TR $(TDNW $(MREF mir,stat,distribution,negative_binomial)) $(TD Negative Binomial Distribution )) 46 |     $(TR $(TDNW $(MREF mir,stat,distribution,poisson)) $(TD Poisson Distribution )) 47 |     $(TR $(TDNW $(MREF mir,stat,distribution,uniform_discrete)) $(TD Discrete Uniform Distribution )) 48 |     $(LEADINGROW Univariate Continuous Distributions) 49 |     $(TR $(TDNW $(MREF mir,stat,distribution,beta)) $(TD Beta Distribution )) 50 |     $(TR $(TDNW $(MREF mir,stat,distribution,beta_proportion)) $(TD Beta Proportion Distribution )) 51 |     $(TR $(TDNW $(MREF mir,stat,distribution,cauchy)) $(TD Cauchy Distribution )) 52 |     $(TR $(TDNW $(MREF mir,stat,distribution,chi2)) $(TD Chi-squared Distribution )) 53 |     $(TR $(TDNW $(MREF mir,stat,distribution,cornish_fisher)) $(TD Cornish-Fisher Expansion )) 54 |     $(TR $(TDNW $(MREF mir,stat,distribution,exponential)) $(TD Exponential Distribution )) 55 |     $(TR $(TDNW $(MREF mir,stat,distribution,f)) $(TD F Distribution )) 56 |     $(TR $(TDNW $(MREF mir,stat,distribution,gamma)) $(TD Gamma Distribution )) 57 |     $(TR $(TDNW $(MREF mir,stat,distribution,generalized_pareto)) $(TD Generalized Pareto Distribution )) 58 |     $(TR $(TDNW $(MREF mir,stat,distribution,gev)) $(TD Generalized Extreme Value (GEV) Distribution )) 59 |     $(TR $(TDNW $(MREF mir,stat,distribution,laplace)) $(TD Laplace Distribution )) 60 |     $(TR $(TDNW $(MREF mir,stat,distribution,log_normal)) $(TD Log-normal Distribution )) 61 |     $(TR $(TDNW $(MREF mir,stat,distribution,logistic)) $(TD Logistic Distribution )) 62 |     $(TR $(TDNW $(MREF mir,stat,distribution,normal)) $(TD Normal Distribution )) 63 |     $(TR $(TDNW $(MREF mir,stat,distribution,pareto)) $(TD Pareto Distribution )) 64 |     $(TR $(TDNW $(MREF mir,stat,distribution,rayleigh)) $(TD Rayleigh Distribution )) 65 |     $(TR $(TDNW $(MREF mir,stat,distribution,students_t)) $(TD Student's t Distribution )) 66 |     $(TR $(TDNW $(MREF mir,stat,distribution,uniform)) $(TD Continuous Uniform Distribution )) 67 |     $(TR $(TDNW $(MREF mir,stat,distribution,weibull)) $(TD Weibull Distribution )) 68 |     $(LEADINGROW Multivariate Distributions) 69 |     $(TR $(TDNW $(MREF mir,stat,distribution,categorical)) $(TD Categorical Distribution )) 70 | ) 71 | 72 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 73 | 74 | Authors: John Michael Hall, Ilya Yaroshenko 75 | 76 | Copyright: 2022-3 Mir Stat Authors. 77 | 78 | +/ 79 | 80 | module mir.stat.distribution; 81 | 82 | /// 83 | public import mir.stat.distribution.bernoulli; 84 | /// 85 | public import mir.stat.distribution.beta; 86 | /// 87 | public import mir.stat.distribution.beta_proportion; 88 | /// 89 | public import mir.stat.distribution.binomial; 90 | /// 91 | public import mir.stat.distribution.cauchy; 92 | /// 93 | public import mir.stat.distribution.chi2; 94 | /// 95 | public import mir.stat.distribution.cornish_fisher; 96 | /// 97 | public import mir.stat.distribution.exponential; 98 | /// 99 | public import mir.stat.distribution.f; 100 | /// 101 | public import mir.stat.distribution.gamma; 102 | /// 103 | public import mir.stat.distribution.generalized_pareto; 104 | /// 105 | public import mir.stat.distribution.geometric; 106 | /// 107 | public import mir.stat.distribution.gev; 108 | /// 109 | public import mir.stat.distribution.hypergeometric; 110 | /// 111 | public import mir.stat.distribution.laplace; 112 | /// 113 | public import mir.stat.distribution.log_normal; 114 | /// 115 | public import mir.stat.distribution.logistic; 116 | /// 117 | public import mir.stat.distribution.negative_binomial; 118 | /// 119 | public import mir.stat.distribution.normal; 120 | /// 121 | public import mir.stat.distribution.pareto; 122 | /// 123 | public import mir.stat.distribution.poisson; 124 | /// 125 | public import mir.stat.distribution.rayleigh; 126 | /// 127 | public import mir.stat.distribution.students_t; 128 | /// 129 | public import mir.stat.distribution.uniform; 130 | /// 131 | public import mir.stat.distribution.uniform_discrete; 132 | /// 133 | public import mir.stat.distribution.weibull; 134 | 135 | // Match comment above to ensure no errors 136 | version(mir_stat_test) 137 | @safe pure nothrow @nogc 138 | unittest 139 | { 140 | import mir.math.common: pow; 141 | import mir.stat.distribution; 142 | import mir.test: shouldApprox; 143 | 144 | 4.binomialPMF(6, 2.0 / 3).shouldApprox == (15.0 * pow(2.0 / 3, 4) * pow(1.0 / 3, 2)); 145 | } -------------------------------------------------------------------------------- /source/mir/stat/distribution/pareto.d: -------------------------------------------------------------------------------- 1 | /++ 2 | This module contains algorithms for the $(LINK2 https://en.wikipedia.org/wiki/Pareto_distribution, Pareto Distribution). 3 | 4 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 | 6 | Authors: John Michael Hall 7 | 8 | Copyright: 2022-3 Mir Stat Authors. 9 | 10 | +/ 11 | 12 | module mir.stat.distribution.pareto; 13 | 14 | import mir.internal.utility: isFloatingPoint; 15 | 16 | /++ 17 | Computes the pareto probability density function (PDF). 18 | 19 | Params: 20 | x = value to evaluate PDF 21 | xMin = scale parameter 22 | alpha = shape parameter 23 | 24 | See_also: 25 | $(LINK2 https://en.wikipedia.org/wiki/Pareto_distribution, Pareto Distribution) 26 | +/ 27 | @safe pure nothrow @nogc 28 | T paretoPDF(T)(const T x, const T xMin, const T alpha) 29 | if (isFloatingPoint!T) 30 | in (x >= xMin, "x must be greater than or equal to xMin") 31 | in (xMin > 0, "xMin must be greater than zero") 32 | in (alpha > 0, "alpha must be greater than zero") 33 | { 34 | import mir.math.common: pow; 35 | 36 | return alpha * pow(xMin, alpha) / pow(x, alpha + 1); 37 | } 38 | 39 | /// 40 | version(mir_stat_test) 41 | @safe pure nothrow @nogc 42 | unittest { 43 | import mir.test: shouldApprox; 44 | 45 | 1.0.paretoPDF(1, 3).shouldApprox == 3; 46 | 2.0.paretoPDF(1, 3).shouldApprox == 0.1875; 47 | 3.0.paretoPDF(2, 4).shouldApprox == 0.2633745; 48 | } 49 | 50 | /++ 51 | Computes the pareto cumulative distribution function (CDF). 52 | 53 | Params: 54 | x = value to evaluate CDF 55 | xMin = scale parameter 56 | alpha = shape parameter 57 | 58 | See_also: 59 | $(LINK2 https://en.wikipedia.org/wiki/Pareto_distribution, Pareto Distribution) 60 | +/ 61 | @safe pure nothrow @nogc 62 | T paretoCDF(T)(const T x, const T xMin, const T alpha) 63 | if (isFloatingPoint!T) 64 | in (x >= xMin, "x must be greater than or equal to xMin") 65 | in (xMin > 0, "xMin must be greater than zero") 66 | in (alpha > 0, "alpha must be greater than zero") 67 | { 68 | import mir.math.common: pow; 69 | 70 | return 1 - pow(xMin / x, alpha); 71 | } 72 | 73 | /// 74 | version(mir_stat_test) 75 | @safe pure nothrow @nogc 76 | unittest { 77 | import mir.test: shouldApprox; 78 | 79 | 1.0.paretoCDF(1, 3).shouldApprox == 0; 80 | 2.0.paretoCDF(1, 3).shouldApprox == 0.875; 81 | 3.0.paretoCDF(2, 4).shouldApprox == 0.8024691; 82 | } 83 | 84 | /++ 85 | Computes the pareto complementary cumulative distribution function (CCDF). 86 | 87 | Params: 88 | x = value to evaluate CCDF 89 | xMin = scale parameter 90 | alpha = shape parameter 91 | 92 | See_also: 93 | $(LINK2 https://en.wikipedia.org/wiki/Pareto_distribution, Pareto Distribution) 94 | +/ 95 | @safe pure nothrow @nogc 96 | T paretoCCDF(T)(const T x, const T xMin, const T alpha) 97 | if (isFloatingPoint!T) 98 | in (x >= xMin, "x must be greater than or equal to xMin") 99 | in (xMin > 0, "xMin must be greater than zero") 100 | in (alpha > 0, "alpha must be greater than zero") 101 | { 102 | import mir.math.common: pow; 103 | 104 | return pow(xMin / x, alpha); 105 | } 106 | 107 | /// 108 | version(mir_stat_test) 109 | @safe pure nothrow @nogc 110 | unittest { 111 | import mir.test: shouldApprox; 112 | 113 | 1.0.paretoCCDF(1, 3).shouldApprox == 1; 114 | 2.0.paretoCCDF(1, 3).shouldApprox == 0.125; 115 | 3.0.paretoCCDF(2, 4).shouldApprox == 0.1975309; 116 | } 117 | 118 | /++ 119 | Computes the pareto inverse cumulative distribution function (InvCDF). 120 | 121 | Params: 122 | p = value to evaluate InvCDF 123 | xMin = scale parameter 124 | alpha = shape parameter 125 | 126 | See_also: 127 | $(LINK2 https://en.wikipedia.org/wiki/Pareto_distribution, Pareto Distribution) 128 | +/ 129 | @safe pure nothrow @nogc 130 | T paretoInvCDF(T)(const T p, const T xMin, const T alpha) 131 | if (isFloatingPoint!T) 132 | in (p >= 0, "p must be greater than or equal to 0") 133 | in (p <= 1, "p must be less than or equal to 1") 134 | in (xMin > 0, "xMin must be greater than zero") 135 | in (alpha > 0, "alpha must be greater than zero") 136 | { 137 | import mir.math.common: pow; 138 | 139 | return xMin / pow(1 - p, cast(T) 1 / alpha); 140 | } 141 | 142 | /// 143 | version(mir_stat_test) 144 | @safe pure nothrow @nogc 145 | unittest { 146 | import mir.test: shouldApprox; 147 | 148 | 0.0.paretoInvCDF(1, 3).shouldApprox == 1; 149 | 0.875.paretoInvCDF(1, 3).shouldApprox == 2; 150 | 0.8024691.paretoInvCDF(2, 4).shouldApprox == 3; 151 | } 152 | 153 | /++ 154 | Computes the pareto log probability density function (LPDF). 155 | 156 | Params: 157 | x = value to evaluate LPDF 158 | xMin = scale parameter 159 | alpha = shape parameter 160 | 161 | See_also: 162 | $(LINK2 https://en.wikipedia.org/wiki/Pareto_distribution, Pareto Distribution) 163 | +/ 164 | @safe pure nothrow @nogc 165 | T paretoLPDF(T)(const T x, const T xMin, const T alpha) 166 | if (isFloatingPoint!T) 167 | in (x >= xMin, "x must be greater than or equal to xMin") 168 | in (xMin > 0, "xMin must be greater than zero") 169 | in (alpha > 0, "alpha must be greater than zero") 170 | { 171 | import mir.math.common: log; 172 | import mir.math.internal.xlogy: xlogy; 173 | 174 | return log(alpha) + xlogy(alpha, xMin) - xlogy(alpha + 1, x); 175 | } 176 | 177 | /// 178 | version(mir_stat_test) 179 | @safe pure nothrow @nogc 180 | unittest { 181 | import mir.math.common: log; 182 | import mir.test: shouldApprox; 183 | 184 | 1.0.paretoLPDF(1, 3).shouldApprox == log(paretoPDF(1.0, 1, 3)); 185 | 2.0.paretoLPDF(1, 3).shouldApprox == log(paretoPDF(2.0, 1, 3)); 186 | 3.0.paretoLPDF(2, 4).shouldApprox == log(paretoPDF(3.0, 2, 4)); 187 | } 188 | -------------------------------------------------------------------------------- /source/mir/stat/distribution/pdf.d: -------------------------------------------------------------------------------- 1 | /++ 2 | This package publicly imports `mir.stat.distribution.*PDF` & `.*PMF` modules. 3 | 4 | $(BOOKTABLE , 5 |     $(TR 6 |         $(TH Functions) 7 |         $(TH Description) 8 |     ) 9 |     $(LEADINGROW Univariate Discrete Distributions) 10 |     $(TR $(TDNW $(SUBREF bernoulli, bernoulliPMF)) $(TD Bernoulli PMF )) 11 |     $(TR $(TDNW $(SUBREF binomial, binomialPMF)) $(TD Binomial PMF )) 12 |     $(TR $(TDNW $(SUBREF geometric, geometricPMF)) $(TD Geometric PMF )) 13 |     $(TR $(TDNW $(SUBREF hypergeometric, hypergeometricPMF)) $(TD Hypergeometric PMF )) 14 |     $(TR $(TDNW $(SUBREF negative_binomial, negativeBinomialPMF)) $(TD Negative Binomial PMF )) 15 |     $(TR $(TDNW $(SUBREF poisson, poissonPMF)) $(TD Poisson PMF )) 16 |     $(TR $(TDNW $(SUBREF uniform_discrete, uniformDiscretePMF)) $(TD Discrete Uniform PMF )) 17 |     $(LEADINGROW Univariate Continuous Distributions) 18 |     $(TR $(TDNW $(SUBREF beta, betaPDF)) $(TD Beta PDF )) 19 |     $(TR $(TDNW $(SUBREF beta_proportion, betaProportionPDF)) $(TD Beta Proportion PDF )) 20 |     $(TR $(TDNW $(SUBREF cauchy, cauchyPDF)) $(TD Cauchy PDF )) 21 |     $(TR $(TDNW $(SUBREF chi2, chi2PDF)) $(TD Chi-squared PDF )) 22 |     $(TR $(TDNW $(SUBREF exponential, exponentialPDF)) $(TD Exponential PDF )) 23 |     $(TR $(TDNW $(SUBREF f, fPDF)) $(TD F PDF )) 24 |     $(TR $(TDNW $(SUBREF gamma, gammaPDF)) $(TD Gamma PDF )) 25 |     $(TR $(TDNW $(SUBREF generalized_pareto, generalizedParetoPDF)) $(TD Generalized Pareto PDF )) 26 |     $(TR $(TDNW $(SUBREF gev, gevPDF)) $(TD Generalized Extreme Value (GEV) PDF )) 27 |     $(TR $(TDNW $(SUBREF laplace, laplacePDF)) $(TD Laplace PDF )) 28 |     $(TR $(TDNW $(SUBREF log_normal, logNormalPDF)) $(TD Log-normal PDF )) 29 |     $(TR $(TDNW $(SUBREF logistic, logisticPDF)) $(TD Logistic PDF )) 30 |     $(TR $(TDNW $(SUBREF normal, normalPDF)) $(TD Normal PDF )) 31 |     $(TR $(TDNW $(SUBREF pareto, paretoPDF)) $(TD Pareto PDF )) 32 |     $(TR $(TDNW $(SUBREF rayleigh, rayleighPDF)) $(TD Rayleigh PDF )) 33 |     $(TR $(TDNW $(SUBREF students_t, studentsTPDF)) $(TD Student's t PDF )) 34 |     $(TR $(TDNW $(SUBREF uniform, uniformPDF)) $(TD Continuous Uniform PDF )) 35 |     $(TR $(TDNW $(SUBREF weibull, weibullPDF)) $(TD Weibull PDF )) 36 |     $(LEADINGROW Multivariate Distributions) 37 |     $(TR $(TDNW $(SUBREF categorical, categoricalPMF)) $(TD Categorical PMF )) 38 | ) 39 | 40 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 41 | 42 | Authors: John Michael Hall, Ilya Yaroshenko 43 | 44 | Copyright: 2022-3 Mir Stat Authors. 45 | 46 | Macros: 47 | SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, stat, distribution, $1)$(NBSP) 48 | 49 | +/ 50 | 51 | module mir.stat.distribution.pdf; 52 | 53 | /// 54 | public import mir.stat.distribution.bernoulli: bernoulliPMF; 55 | /// 56 | public import mir.stat.distribution.beta: betaPDF; 57 | /// 58 | public import mir.stat.distribution.beta_proportion: betaProportionPDF; 59 | /// 60 | public import mir.stat.distribution.binomial: binomialPMF; 61 | /// 62 | public import mir.stat.distribution.categorical: categoricalPMF; 63 | /// 64 | public import mir.stat.distribution.cauchy: cauchyPDF; 65 | /// 66 | public import mir.stat.distribution.chi2: chi2PDF; 67 | /// 68 | public import mir.stat.distribution.exponential: exponentialPDF; 69 | /// 70 | public import mir.stat.distribution.f: fPDF; 71 | /// 72 | public import mir.stat.distribution.gamma: gammaPDF; 73 | /// 74 | public import mir.stat.distribution.generalized_pareto: generalizedParetoPDF; 75 | /// 76 | public import mir.stat.distribution.geometric: geometricPMF; 77 | /// 78 | public import mir.stat.distribution.gev: gevPDF; 79 | /// 80 | public import mir.stat.distribution.hypergeometric: hypergeometricPMF; 81 | /// 82 | public import mir.stat.distribution.laplace: laplacePDF; 83 | /// 84 | public import mir.stat.distribution.log_normal: logNormalPDF; 85 | /// 86 | public import mir.stat.distribution.logistic: logisticPDF; 87 | /// 88 | public import mir.stat.distribution.negative_binomial: negativeBinomialPMF; 89 | /// 90 | public import mir.stat.distribution.normal: normalPDF; 91 | /// 92 | public import mir.stat.distribution.pareto: paretoPDF; 93 | /// 94 | public import mir.stat.distribution.poisson: poissonPMF; 95 | /// 96 | public import mir.stat.distribution.rayleigh: rayleighPDF; 97 | /// 98 | public import mir.stat.distribution.students_t: studentsTPDF; 99 | /// 100 | public import mir.stat.distribution.uniform: uniformPDF; 101 | /// 102 | public import mir.stat.distribution.uniform_discrete: uniformDiscretePMF; 103 | /// 104 | public import mir.stat.distribution.weibull: weibullPDF; 105 | -------------------------------------------------------------------------------- /source/mir/stat/distribution/rayleigh.d: -------------------------------------------------------------------------------- 1 | /++ 2 | This module contains algorithms for the $(LINK2 https://en.wikipedia.org/wiki/Rayleigh_distribution, Rayleigh Distribution). 3 | 4 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 | 6 | Authors: John Michael Hall 7 | 8 | Copyright: 2023 Mir Stat Authors. 9 | 10 | +/ 11 | 12 | module mir.stat.distribution.rayleigh; 13 | 14 | import mir.internal.utility: isFloatingPoint; 15 | 16 | /++ 17 | Computes the Rayleigh probability density function (PDF). 18 | 19 | Params: 20 | x = value to evaluate PDF 21 | 22 | See_also: 23 | $(LINK2 https://en.wikipedia.org/wiki/Rayleigh_distribution, Rayleigh Distribution) 24 | +/ 25 | @safe pure nothrow @nogc 26 | T rayleighPDF(T)(const T x) 27 | if (isFloatingPoint!T) 28 | { 29 | import mir.math.common: exp; 30 | 31 | return x * exp(-0.5 * x * x); 32 | } 33 | 34 | /++ 35 | Ditto, with scale parameter. 36 | 37 | Params: 38 | x = value to evaluate PDF 39 | scale = scale parameter 40 | +/ 41 | @safe pure nothrow @nogc 42 | T rayleighPDF(T)(const T x, const T scale) 43 | if (isFloatingPoint!T) 44 | in (scale > 0, "scale must be greater than zero") 45 | { 46 | import mir.math.common: exp; 47 | 48 | const T scale2 = scale * scale; 49 | return x / scale2 * exp(-0.5 * x * x / scale2); 50 | } 51 | 52 | /// 53 | version(mir_stat_test) 54 | @safe pure nothrow @nogc 55 | unittest 56 | { 57 | import mir.test: shouldApprox; 58 | 59 | 0.0.rayleighPDF.shouldApprox == 0.0; 60 | 0.5.rayleighPDF.shouldApprox == 0.4412485; 61 | 1.0.rayleighPDF.shouldApprox == 0.6065307; 62 | 2.0.rayleighPDF.shouldApprox == 0.2706706; 63 | 64 | // Can also provide scale parameter 65 | 0.5.rayleighPDF(2.0).shouldApprox == 0.1211541; 66 | 1.0.rayleighPDF(2.0).shouldApprox == 0.2206242; 67 | 4.0.rayleighPDF(2.0).shouldApprox == 0.1353353; 68 | } 69 | 70 | /++ 71 | Computes the Rayleigh cumulative distribution function (CDF). 72 | 73 | Params: 74 | x = value to evaluate CDF 75 | 76 | See_also: 77 | $(LINK2 https://en.wikipedia.org/wiki/Rayleigh_distribution, Rayleigh Distribution) 78 | +/ 79 | @safe pure nothrow @nogc 80 | T rayleighCDF(T)(const T x) 81 | if (isFloatingPoint!T) 82 | { 83 | import mir.math.common: exp; 84 | 85 | return 1 - exp(-0.5 * x * x); 86 | } 87 | 88 | /++ 89 | Ditto, with scale parameter. 90 | 91 | Params: 92 | x = value to evaluate CDF 93 | scale = scale parameter 94 | +/ 95 | T rayleighCDF(T)(const T x, const T scale) 96 | if (isFloatingPoint!T) 97 | in (scale > 0, "scale must be greater than zero") 98 | { 99 | return rayleighCDF(x / scale); 100 | } 101 | 102 | /// 103 | version(mir_stat_test) 104 | @safe pure nothrow @nogc 105 | unittest 106 | { 107 | import mir.test: shouldApprox; 108 | 109 | 0.0.rayleighCDF.shouldApprox == 0.0; 110 | 0.5.rayleighCDF.shouldApprox == 0.1175031; 111 | 1.0.rayleighCDF.shouldApprox == 0.3934693; 112 | 2.0.rayleighCDF.shouldApprox == 0.8646647; 113 | 114 | // Can also provide scale parameter 115 | 0.5.rayleighCDF(2.0).shouldApprox == 0.03076677; 116 | 1.0.rayleighCDF(2.0).shouldApprox == 0.1175031; 117 | 4.0.rayleighCDF(2.0).shouldApprox == 0.8646647; 118 | } 119 | 120 | /++ 121 | Computes the Rayleigh complementary cumulative distribution function (CCDF). 122 | 123 | Params: 124 | x = value to evaluate CCDF 125 | 126 | See_also: 127 | $(LINK2 https://en.wikipedia.org/wiki/Rayleigh_distribution, Rayleigh Distribution) 128 | +/ 129 | @safe pure nothrow @nogc 130 | T rayleighCCDF(T)(const T x) 131 | if (isFloatingPoint!T) 132 | { 133 | import mir.math.common: exp; 134 | 135 | return exp(-0.5 * x * x); 136 | } 137 | 138 | /++ 139 | Ditto, with scale parameter. 140 | 141 | Params: 142 | x = value to evaluate CCDF 143 | scale = scale parameter 144 | +/ 145 | @safe pure nothrow @nogc 146 | T rayleighCCDF(T)(const T x, const T scale) 147 | if (isFloatingPoint!T) 148 | in (scale > 0, "scale must be greater than zero") 149 | { 150 | return rayleighCCDF(x / scale); 151 | } 152 | 153 | /// 154 | version(mir_stat_test) 155 | @safe pure nothrow @nogc 156 | unittest 157 | { 158 | import mir.test: shouldApprox; 159 | 160 | 0.0.rayleighCCDF.shouldApprox == 1.0; 161 | 0.5.rayleighCCDF.shouldApprox == 0.8824969; 162 | 1.0.rayleighCCDF.shouldApprox == 0.6065307; 163 | 2.0.rayleighCCDF.shouldApprox == 0.1353353; 164 | 165 | // Can also provide scale parameter 166 | 0.5.rayleighCCDF(2.0).shouldApprox == 0.9692332; 167 | 1.0.rayleighCCDF(2.0).shouldApprox == 0.8824969; 168 | 4.0.rayleighCCDF(2.0).shouldApprox == 0.1353353; 169 | } 170 | 171 | /++ 172 | Computes the Rayleigh inverse cumulative distribution function (InvCDF). 173 | 174 | Params: 175 | p = value to evaluate InvCDF 176 | 177 | See_also: 178 | $(LINK2 https://en.wikipedia.org/wiki/Rayleigh_distribution, Rayleigh Distribution) 179 | +/ 180 | @safe pure nothrow @nogc 181 | T rayleighInvCDF(T)(const T p) 182 | if (isFloatingPoint!T) 183 | in (p >= 0, "p must be greater than or equal to 0") 184 | in (p <= 1, "p must be less than or equal to 1") 185 | { 186 | import mir.math.common: log, sqrt; 187 | 188 | return sqrt(-2 * log(1 - p)); 189 | } 190 | 191 | /++ 192 | Ditto, with scale parameter. 193 | 194 | Params: 195 | p = value to evaluate InvCDF 196 | scale = scale parameter 197 | 198 | See_also: 199 | $(LINK2 https://en.wikipedia.org/wiki/Logistic_distribution, Logistic probability distribution) 200 | +/ 201 | @safe pure nothrow @nogc 202 | T rayleighInvCDF(T)(const T p, const T scale) 203 | if (isFloatingPoint!T) 204 | in (p >= 0, "p must be greater than or equal to 0") 205 | in (p <= 1, "p must be less than or equal to 1") 206 | in (scale > 0, "scale must be greater than zero") 207 | { 208 | return scale * rayleighInvCDF(p); 209 | } 210 | 211 | /// 212 | version(mir_stat_test) 213 | @safe pure nothrow @nogc 214 | unittest { 215 | import mir.test: shouldApprox; 216 | 217 | rayleighInvCDF(0.0).shouldApprox == 0.0; 218 | rayleighInvCDF(0.25).shouldApprox == 0.7585276; 219 | rayleighInvCDF(0.5).shouldApprox == 1.17741; 220 | rayleighInvCDF(0.75).shouldApprox == 1.665109; 221 | rayleighInvCDF(1.0).shouldApprox == double.infinity; 222 | 223 | // Can also provide scale parameter 224 | rayleighInvCDF(0.2, 2).shouldApprox == 1.336094; 225 | rayleighInvCDF(0.4, 2).shouldApprox == 2.021535; 226 | rayleighInvCDF(0.6, 2).shouldApprox == 2.707457; 227 | rayleighInvCDF(0.8, 2).shouldApprox == 3.588245; 228 | } 229 | 230 | /++ 231 | Computes the Rayleigh log probability density function (LPDF). 232 | 233 | Params: 234 | x = value to evaluate LPDF 235 | 236 | See_also: 237 | $(LINK2 https://en.wikipedia.org/wiki/Rayleigh_distribution, Rayleigh Distribution) 238 | +/ 239 | @safe pure nothrow @nogc 240 | T rayleighLPDF(T)(const T x) 241 | if (isFloatingPoint!T) 242 | { 243 | import mir.math.common: log; 244 | 245 | return log(x) - 0.5 * x * x; 246 | } 247 | 248 | /++ 249 | Ditto, with scale parameter. 250 | 251 | Params: 252 | x = value to evaluate LPDF 253 | scale = scale parameter 254 | 255 | See_also: 256 | $(LINK2 https://en.wikipedia.org/wiki/Rayleigh_distribution, Rayleigh Distribution) 257 | +/ 258 | @safe pure nothrow @nogc 259 | T rayleighLPDF(T)(const T x, const T scale) 260 | if (isFloatingPoint!T) 261 | in (scale > 0, "shape must be greater than zero") 262 | { 263 | import mir.math.common: log; 264 | 265 | return log(x) - 2 * log(scale) - 0.5 * x * x / (scale * scale); 266 | } 267 | 268 | /// 269 | version(mir_stat_test) 270 | @safe pure nothrow @nogc 271 | unittest 272 | { 273 | import mir.math.common: log; 274 | import mir.test: shouldApprox; 275 | 276 | 0.0.rayleighLPDF.shouldApprox == -double.infinity; 277 | 0.5.rayleighLPDF.shouldApprox == log(0.4412485); 278 | 1.0.rayleighLPDF.shouldApprox == log(0.6065307); 279 | 2.0.rayleighLPDF.shouldApprox == log(0.2706706); 280 | 281 | // Can also provide scale parameter 282 | 0.5.rayleighLPDF(2.0).shouldApprox == log(0.1211541); 283 | 1.0.rayleighLPDF(2.0).shouldApprox == log(0.2206242); 284 | 4.0.rayleighLPDF(2.0).shouldApprox == log(0.1353353); 285 | } 286 | -------------------------------------------------------------------------------- /source/mir/stat/distribution/uniform.d: -------------------------------------------------------------------------------- 1 | /++ 2 | This module contains algorithms for the continuous $(LINK2 https://en.wikipedia.org/wiki/Continuous_uniform_distribution, Uniform Distribution). 3 | 4 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 | 6 | Authors: John Michael Hall 7 | 8 | Copyright: 2022-3 Mir Stat Authors. 9 | 10 | +/ 11 | 12 | module mir.stat.distribution.uniform; 13 | 14 | import mir.internal.utility: isFloatingPoint; 15 | 16 | /++ 17 | Computes the uniform probability density function (PDF). 18 | 19 | Params: 20 | x = value to evaluate PDF 21 | lower = lower bound 22 | upper = upper bound 23 | 24 | See_also: 25 | $(LINK2 https://en.wikipedia.org/wiki/Continuous_uniform_distribution, Uniform Distribution) 26 | +/ 27 | @safe pure nothrow @nogc 28 | T uniformPDF(T)(const T x, const T lower = 0, const T upper = 1) 29 | if (isFloatingPoint!T) 30 | in (x >= lower, "x must be greater than or equal to lower bound") 31 | in (x <= upper, "x must be less than or equal to upper bound") 32 | in (lower < upper, "lower must be less than upper") 33 | { 34 | return 1.0L / (upper - lower); 35 | } 36 | 37 | /// 38 | version(mir_stat_test) 39 | @safe pure nothrow @nogc 40 | unittest { 41 | import mir.math.common: approxEqual; 42 | assert(0.5.uniformPDF == 1); 43 | assert(0.5.uniformPDF(0.0, 1.5).approxEqual(2.0 / 3)); 44 | assert(2.5.uniformPDF(1.0, 3.0).approxEqual(0.5)); 45 | } 46 | 47 | /++ 48 | Computes the uniform cumulative distribution function (CDF). 49 | 50 | Params: 51 | x = value to evaluate CDF 52 | lower = lower bound 53 | upper = upper bound 54 | 55 | See_also: 56 | $(LINK2 https://en.wikipedia.org/wiki/Continuous_uniform_distribution, Uniform Distribution) 57 | +/ 58 | @safe pure nothrow @nogc 59 | T uniformCDF(T)(const T x, const T lower = 0, const T upper = 1) 60 | if (isFloatingPoint!T) 61 | in (x >= lower, "x must be greater than or equal to lower bound") 62 | in (x <= upper, "x must be less than or equal to upper bound") 63 | in (lower < upper, "lower must be less than upper") 64 | { 65 | return (x - lower) / (upper - lower); 66 | } 67 | 68 | /// 69 | version(mir_stat_test) 70 | @safe pure nothrow @nogc 71 | unittest { 72 | import mir.math.common: approxEqual; 73 | assert(0.5.uniformCDF == 0.5); 74 | assert(0.5.uniformCDF(0.0, 1.5).approxEqual(1.0 / 3)); 75 | assert(2.5.uniformCDF(1.0, 3.0).approxEqual(3.0 / 4)); 76 | } 77 | 78 | /++ 79 | Computes the uniform complementary cumulative distribution function (CCDF). 80 | 81 | Params: 82 | x = value to evaluate CCDF 83 | lower = lower bound 84 | upper = upper bound 85 | 86 | See_also: 87 | $(LINK2 https://en.wikipedia.org/wiki/Continuous_uniform_distribution, Uniform Distribution) 88 | +/ 89 | @safe pure nothrow @nogc 90 | T uniformCCDF(T)(const T x, const T lower = 0, const T upper = 1) 91 | if (isFloatingPoint!T) 92 | in (x >= lower, "x must be greater than or equal to lower bound") 93 | in (x <= upper, "x must be less than or equal to upper bound") 94 | in (lower < upper, "lower must be less than upper") 95 | { 96 | return (upper - x) / (upper - lower); 97 | } 98 | 99 | /// 100 | version(mir_stat_test) 101 | @safe pure nothrow @nogc 102 | unittest { 103 | import mir.math.common: approxEqual; 104 | assert(0.5.uniformCCDF == 0.5); 105 | assert(0.5.uniformCCDF(0.0, 1.5).approxEqual(2.0 / 3)); 106 | assert(2.5.uniformCCDF(1.0, 3.0).approxEqual(1.0 / 4)); 107 | } 108 | 109 | /++ 110 | Computes the uniform inverse cumulative distribution function (InvCDF) 111 | 112 | Params: 113 | p = value to evaluate InvCDF 114 | lower = lower bound 115 | upper = upper bound 116 | 117 | See_also: 118 | $(LINK2 https://en.wikipedia.org/wiki/Continuous_uniform_distribution, Uniform Distribution) 119 | +/ 120 | @safe pure nothrow @nogc 121 | T uniformInvCDF(T)(const T p, const T lower = 0, const T upper = 1) 122 | if (isFloatingPoint!T) 123 | in (p >= 0, "p must be greater than or equal to 0") 124 | in (p <= 1, "p must be less than or equal to 1") 125 | in (lower < upper, "lower must be less than upper") 126 | { 127 | return lower + p * (upper - lower); 128 | } 129 | 130 | /// 131 | version(mir_stat_test) 132 | @safe pure nothrow @nogc 133 | unittest { 134 | import mir.math.common: approxEqual; 135 | assert(0.5.uniformInvCDF == 0.5); 136 | assert((1.0 / 3).uniformInvCDF(0.0, 1.5).approxEqual(0.5)); 137 | assert((3.0 / 4).uniformInvCDF(1.0, 3.0).approxEqual(2.5)); 138 | } 139 | 140 | /++ 141 | Computes the uniform log probability density function (LPDF) 142 | 143 | Params: 144 | x = value to evaluate LPDF 145 | lower = lower bound 146 | upper = upper bound 147 | 148 | See_also: 149 | $(LINK2 https://en.wikipedia.org/wiki/Continuous_uniform_distribution, Uniform Distribution) 150 | +/ 151 | @safe pure nothrow @nogc 152 | T uniformLPDF(T)(const T x, const T lower = 0, const T upper = 1) 153 | if (isFloatingPoint!T) 154 | in (x >= lower, "x must be greater than or equal to lower bound") 155 | in (x <= upper, "x must be less than or equal to upper bound") 156 | in (lower < upper, "lower must be less than upper") 157 | { 158 | import mir.math.common: log; 159 | 160 | return -log(upper - lower); 161 | } 162 | 163 | /// 164 | version(mir_stat_test) 165 | @safe pure nothrow @nogc 166 | unittest { 167 | import mir.math.common: approxEqual, log; 168 | assert(0.5.uniformLPDF == 0); 169 | assert(0.5.uniformLPDF(0.0, 1.5).approxEqual(-log(1.5))); 170 | assert(1.5.uniformLPDF(1.0, 3.0).approxEqual(-log(2.0))); 171 | } 172 | -------------------------------------------------------------------------------- /source/mir/stat/distribution/uniform_discrete.d: -------------------------------------------------------------------------------- 1 | /++ 2 | This module contains algorithms for the $(LINK2 https://en.wikipedia.org/wiki/Discrete_uniform_distribution, Discrete Uniform Distribution). 3 | 4 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 | 6 | Authors: John Michael Hall 7 | 8 | Copyright: 2022-3 Mir Stat Authors. 9 | 10 | +/ 11 | 12 | module mir.stat.distribution.uniform_discrete; 13 | 14 | import mir.internal.utility: isFloatingPoint; 15 | 16 | /++ 17 | Computes the discrete uniform probability mass function (PMF). 18 | 19 | Params: 20 | x = value to evaluate PMF 21 | lower = lower bound 22 | upper = upper bound 23 | 24 | See_also: 25 | $(LINK2 https://en.wikipedia.org/wiki/Discrete_uniform_distribution, Discrete Uniform Distribution) 26 | +/ 27 | @safe pure nothrow @nogc 28 | double uniformDiscretePMF(const size_t x, const size_t lower = 0, const size_t upper = 1) 29 | in (x >= lower, "x must be greater than or equal to lower bound") 30 | in (x <= upper, "x must be less than or equal to upper bound") 31 | in (lower <= upper, "lower must be less than or equal to upper") 32 | { 33 | return 1.0 / (upper - lower + 1); 34 | } 35 | 36 | /// 37 | version(mir_stat_test) 38 | @safe pure nothrow @nogc 39 | unittest { 40 | import mir.test: shouldApprox; 41 | 42 | 1.uniformDiscretePMF.shouldApprox == 0.5; 43 | 2.uniformDiscretePMF(1, 3).shouldApprox == 1.0 / 3; 44 | } 45 | 46 | /++ 47 | Computes the discrete uniform cumulative distribution function (CDF). 48 | 49 | Params: 50 | x = value to evaluate CDF 51 | lower = lower bound 52 | upper = upper bound 53 | 54 | See_also: 55 | $(LINK2 https://en.wikipedia.org/wiki/Discrete_uniform_distribution, Discrete Uniform Distribution) 56 | +/ 57 | @safe pure nothrow @nogc 58 | double uniformDiscreteCDF(const size_t x, const size_t lower = 0, const size_t upper = 1) 59 | in (x >= lower, "x must be greater than or equal to lower bound") 60 | in (x <= upper, "x must be less than or equal to upper bound") 61 | in (lower <= upper, "lower must be less than or equal to upper") 62 | { 63 | return (cast(double) x - lower + 1) / (upper - lower + 1); 64 | } 65 | 66 | /// 67 | version(mir_stat_test) 68 | @safe pure nothrow @nogc 69 | unittest { 70 | import mir.test: shouldApprox; 71 | 72 | 0.uniformDiscreteCDF.shouldApprox == 0.5; 73 | 1.uniformDiscreteCDF.shouldApprox == 1.0; 74 | 75 | 1.uniformDiscreteCDF(1, 3).shouldApprox == 1.0 / 3; 76 | 2.uniformDiscreteCDF(1, 3).shouldApprox == 2.0 / 3; 77 | 3.uniformDiscreteCDF(1, 3).shouldApprox == 1.0; 78 | } 79 | 80 | /++ 81 | Computes the discrete uniform complementary cumulative distribution function (CCDF). 82 | 83 | Params: 84 | x = value to evaluate CCDF 85 | lower = lower bound 86 | upper = upper bound 87 | 88 | See_also: 89 | $(LINK2 https://en.wikipedia.org/wiki/Discrete_uniform_distribution, Discrete Uniform Distribution) 90 | +/ 91 | @safe pure nothrow @nogc 92 | double uniformDiscreteCCDF(const size_t x, const size_t lower = 0, const size_t upper = 1) 93 | in (x >= lower, "x must be greater than or equal to lower bound") 94 | in (x <= upper, "x must be less than or equal to upper bound") 95 | in (lower <= upper, "lower must be less than or equal to upper") 96 | { 97 | return (cast(double) upper - x) / (upper - lower + 1); 98 | } 99 | 100 | /// 101 | version(mir_stat_test) 102 | @safe pure nothrow @nogc 103 | unittest { 104 | import mir.test: shouldApprox; 105 | 106 | 0.uniformDiscreteCCDF.shouldApprox == 0.5; 107 | 1.uniformDiscreteCCDF.shouldApprox == 0.0; 108 | 109 | 1.uniformDiscreteCCDF(1, 3).shouldApprox == 2.0 / 3; 110 | 2.uniformDiscreteCCDF(1, 3).shouldApprox == 1.0 / 3; 111 | 3.uniformDiscreteCCDF(1, 3).shouldApprox == 0.0; 112 | } 113 | 114 | /++ 115 | Computes the discrete uniform inverse cumulative distribution function (InvCDF) 116 | 117 | Params: 118 | p = value to evaluate InvCDF 119 | lower = lower bound 120 | upper = upper bound 121 | 122 | See_also: 123 | $(LINK2 https://en.wikipedia.org/wiki/Discrete_uniform_distribution, Discrete Uniform Distribution) 124 | +/ 125 | @safe pure nothrow @nogc 126 | size_t uniformDiscreteInvCDF(T)(const T p, const size_t lower = 0, const size_t upper = 1) 127 | if (isFloatingPoint!T) 128 | in (p >= 0, "p must be greater than or equal to 0") 129 | in (p <= 1, "p must be less than or equal to 1") 130 | in (lower < upper, "lower must be less than upper") 131 | { 132 | size_t n = upper - lower + 1; 133 | if (p * n <= 1) { 134 | return lower; 135 | } 136 | return cast(size_t) (p * n + lower - 1); 137 | } 138 | 139 | ///. 140 | version(mir_stat_test) 141 | @safe pure nothrow @nogc 142 | unittest { 143 | import mir.test: should; 144 | 145 | 0.0.uniformDiscreteInvCDF.should == 0; 146 | 0.5.uniformDiscreteInvCDF.should == 0; 147 | 1.0.uniformDiscreteInvCDF.should == 1; 148 | 149 | 0.0.uniformDiscreteInvCDF(1, 3).should == 1; 150 | 0.2.uniformDiscreteInvCDF(1, 3).should == 1; 151 | (1.0 / 3).uniformDiscreteInvCDF(1, 3).should == 1; 152 | 0.5.uniformDiscreteInvCDF(1, 3).should == 1; 153 | (2.0 / 3).uniformDiscreteInvCDF(1, 3).should == 2; 154 | 1.0.uniformDiscreteInvCDF(1, 3).should == 3; 155 | } 156 | 157 | /++ 158 | Computes the discrete uniform log probability distribution function (LPDF) 159 | 160 | Params: 161 | x = value to evaluate LPDF 162 | lower = lower bound 163 | upper = upper bound 164 | 165 | See_also: 166 | $(LINK2 https://en.wikipedia.org/wiki/Discrete_uniform_distribution, Discrete Uniform Distribution) 167 | +/ 168 | @safe pure nothrow @nogc 169 | double uniformDiscreteLPMF(const size_t x, const size_t lower = 0, const size_t upper = 1) 170 | in (x >= lower, "x must be greater than or equal to lower bound") 171 | in (x <= upper, "x must be less than or equal to upper bound") 172 | in (lower < upper, "lower must be less than upper") 173 | { 174 | import mir.math.common: log; 175 | 176 | return -log(cast(double) upper - lower + 1); 177 | } 178 | 179 | /// 180 | version(mir_stat_test) 181 | @safe pure nothrow @nogc 182 | unittest { 183 | import mir.math.common: log; 184 | import mir.test: shouldApprox; 185 | 186 | 1.uniformDiscreteLPMF.shouldApprox == -log(2.0); 187 | 2.uniformDiscreteLPMF(1, 3).shouldApprox == -log(3.0); 188 | } 189 | -------------------------------------------------------------------------------- /source/mir/stat/distribution/weibull.d: -------------------------------------------------------------------------------- 1 | /++ 2 | This module contains algorithms for the $(LINK2 https://en.wikipedia.org/wiki/Weibull_distribution, Weibull Distribution). 3 | 4 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 | 6 | Authors: John Michael Hall 7 | 8 | Copyright: 2023 Mir Stat Authors. 9 | 10 | +/ 11 | 12 | module mir.stat.distribution.weibull; 13 | 14 | import mir.internal.utility: isFloatingPoint; 15 | 16 | /++ 17 | Computes the Weibull probability density function (PDF). 18 | 19 | Params: 20 | x = value to evaluate PDF 21 | shape = shape parameter 22 | scale = scale parameter 23 | 24 | See_also: 25 | $(LINK2 https://en.wikipedia.org/wiki/Weibull_distribution, Weibull Distribution) 26 | +/ 27 | @safe pure nothrow @nogc 28 | T weibullPDF(T)(const T x, const T shape, const T scale = 1) 29 | if (isFloatingPoint!T) 30 | in (x >= 0, "x must be greater than or equal to zero") 31 | in (shape > 0, "shape must be greater than zero") 32 | in (scale > 0, "scale must be greater than zero") 33 | { 34 | import mir.math.common: exp, pow; 35 | 36 | const T x_scale = x / scale; 37 | return (shape / scale) * pow(x_scale, shape - 1) * exp(-pow(x_scale, shape)); 38 | } 39 | 40 | /// 41 | version(mir_stat_test) 42 | @safe pure nothrow @nogc 43 | unittest 44 | { 45 | import mir.test: shouldApprox; 46 | 47 | 0.0.weibullPDF(3.0).shouldApprox == 0; 48 | 0.5.weibullPDF(3.0).shouldApprox == 0.6618727; 49 | 1.0.weibullPDF(3.0).shouldApprox == 1.103638; 50 | 1.5.weibullPDF(3.0).shouldApprox == 0.2309723; 51 | 52 | // Can also provide scale parameter 53 | 0.5.weibullPDF(2.0, 3.0).shouldApprox == 0.1080672; 54 | 1.0.weibullPDF(2.0, 3.0).shouldApprox == 0.1988532; 55 | 1.5.weibullPDF(2.0, 3.0).shouldApprox == 0.2596003; 56 | } 57 | 58 | // 59 | version(mir_stat_test) 60 | @safe pure nothrow @nogc 61 | unittest 62 | { 63 | import mir.test: shouldApprox; 64 | 65 | 0.0.weibullPDF(1.0, 3.0).shouldApprox == 0.3333333; 66 | } 67 | 68 | /++ 69 | Computes the Weibull cumulative distribution function (CDF). 70 | 71 | Params: 72 | x = value to evaluate CDF 73 | shape = shape parameter 74 | scale = scale parameter 75 | 76 | See_also: 77 | $(LINK2 https://en.wikipedia.org/wiki/Weibull_distribution, Weibull Distribution) 78 | +/ 79 | @safe pure nothrow @nogc 80 | T weibullCDF(T)(const T x, const T shape, const T scale = 1) 81 | if (isFloatingPoint!T) 82 | in (x >= 0, "x must be greater than or equal to zero") 83 | in (shape > 0, "shape must be greater than zero") 84 | in (scale > 0, "scale must be greater than zero") 85 | { 86 | import mir.math.common: exp, pow; 87 | 88 | return 1 - exp(-pow(x / scale, shape)); 89 | } 90 | 91 | /// 92 | version(mir_stat_test) 93 | @safe pure nothrow @nogc 94 | unittest 95 | { 96 | import mir.test: shouldApprox; 97 | 98 | 0.0.weibullCDF(3.0).shouldApprox == 0; 99 | 0.5.weibullCDF(3.0).shouldApprox == 0.1175031; 100 | 1.0.weibullCDF(3.0).shouldApprox == 0.6321206; 101 | 1.5.weibullCDF(3.0).shouldApprox == 0.9657819; 102 | 103 | // Can also provide scale parameter 104 | 0.5.weibullCDF(2.0, 3.0).shouldApprox == 0.02739552; 105 | 1.0.weibullCDF(2.0, 3.0).shouldApprox == 0.1051607; 106 | 1.5.weibullCDF(2.0, 3.0).shouldApprox == 0.2211992; 107 | } 108 | 109 | /++ 110 | Computes the Weibull complementary cumulative distribution function (CCDF). 111 | 112 | Params: 113 | x = value to evaluate CCDF 114 | shape = shape parameter 115 | scale = scale parameter 116 | 117 | See_also: 118 | $(LINK2 https://en.wikipedia.org/wiki/Weibull_distribution, Weibull Distribution) 119 | +/ 120 | @safe pure nothrow @nogc 121 | T weibullCCDF(T)(const T x, const T shape, const T scale = 1) 122 | if (isFloatingPoint!T) 123 | in (x >= 0, "x must be greater than or equal to zero") 124 | in (shape > 0, "shape must be greater than zero") 125 | in (scale > 0, "scale must be greater than zero") 126 | { 127 | import mir.math.common: exp, pow; 128 | 129 | return exp(-pow(x / scale, shape)); 130 | } 131 | 132 | /// 133 | version(mir_stat_test) 134 | @safe pure nothrow @nogc 135 | unittest 136 | { 137 | import mir.test: shouldApprox; 138 | 139 | 0.0.weibullCCDF(3.0).shouldApprox == 1; 140 | 0.5.weibullCCDF(3.0).shouldApprox == 0.8824969; 141 | 1.0.weibullCCDF(3.0).shouldApprox == 0.3678794; 142 | 1.5.weibullCCDF(3.0).shouldApprox == 0.03421812; 143 | 144 | // Can also provide scale parameter 145 | 0.5.weibullCCDF(2.0, 3.0).shouldApprox == 0.9726045; 146 | 1.0.weibullCCDF(2.0, 3.0).shouldApprox == 0.8948393; 147 | 1.5.weibullCCDF(2.0, 3.0).shouldApprox == 0.7788008; 148 | } 149 | 150 | /++ 151 | Computes the Weibull inverse cumulative distribution function (InvCDF). 152 | 153 | Params: 154 | p = value to evaluate InvCDF 155 | shape = shape parameter 156 | scale = scale parameter 157 | 158 | See_also: 159 | $(LINK2 https://en.wikipedia.org/wiki/Weibull_distribution, Weibull Distribution) 160 | +/ 161 | @safe pure nothrow @nogc 162 | T weibullInvCDF(T)(const T p, const T shape, const T scale = 1) 163 | if (isFloatingPoint!T) 164 | in (p >= 0, "p must be greater than or equal to 0") 165 | in (p <= 1, "p must be less than or equal to 1") 166 | in (shape > 0, "shape must be greater than zero") 167 | in (scale > 0, "scale must be greater than zero") 168 | { 169 | import mir.math.common: log, pow; 170 | 171 | return scale * pow(-log(1 - p), T(1) / shape); 172 | } 173 | 174 | /// 175 | version(mir_stat_test) 176 | @safe pure nothrow @nogc 177 | unittest { 178 | import mir.test: shouldApprox; 179 | 180 | weibullInvCDF(0.0, 3).shouldApprox == 0.0; 181 | weibullInvCDF(0.25, 3).shouldApprox == 0.6601424; 182 | weibullInvCDF(0.5, 3).shouldApprox == 0.884997; 183 | weibullInvCDF(0.75, 3).shouldApprox == 1.115026; 184 | weibullInvCDF(1.0, 3).shouldApprox == double.infinity; 185 | 186 | // Can also provide scale parameter 187 | weibullInvCDF(0.2, 2, 3).shouldApprox == 1.417142; 188 | weibullInvCDF(0.4, 2, 3).shouldApprox == 2.144162; 189 | weibullInvCDF(0.6, 2, 3).shouldApprox == 2.871692; 190 | weibullInvCDF(0.8, 2, 3).shouldApprox == 3.805909; 191 | } 192 | 193 | 194 | /++ 195 | Computes the Weibull log probability density function (LPDF). 196 | 197 | Params: 198 | x = value to evaluate LPDF 199 | shape = shape parameter 200 | scale = scale parameter 201 | 202 | See_also: 203 | $(LINK2 https://en.wikipedia.org/wiki/Weibull_distribution, Weibull Distribution) 204 | +/ 205 | @safe pure nothrow @nogc 206 | T weibullLPDF(T)(const T x, const T shape, const T scale = 1) 207 | if (isFloatingPoint!T) 208 | in (x >= 0, "x must be greater than or equal to zero") 209 | in (shape > 0, "shape must be greater than zero") 210 | in (scale > 0, "scale must be greater than zero") 211 | { 212 | import mir.math.common: log, pow; 213 | import mir.math.internal.xlogy: xlogy; 214 | 215 | const T x_scale = x / scale; 216 | return log(shape / scale) + xlogy(shape - 1, x_scale) - pow(x_scale, shape); 217 | } 218 | 219 | /// 220 | version(mir_stat_test) 221 | @safe pure nothrow @nogc 222 | unittest 223 | { 224 | import mir.math.common: log; 225 | import mir.test: shouldApprox; 226 | 227 | 0.0.weibullLPDF(3.0).shouldApprox == log(0.0); 228 | 0.5.weibullLPDF(3.0).shouldApprox == log(0.6618727); 229 | 1.0.weibullLPDF(3.0).shouldApprox == log(1.103638); 230 | 1.5.weibullLPDF(3.0).shouldApprox == log(0.2309723); 231 | 232 | // Can also provide scale parameter 233 | 0.5.weibullLPDF(2.0, 3.0).shouldApprox == log(0.1080672); 234 | 1.0.weibullLPDF(2.0, 3.0).shouldApprox == log(0.1988532); 235 | 1.5.weibullLPDF(2.0, 3.0).shouldApprox == log(0.2596003); 236 | } 237 | -------------------------------------------------------------------------------- /source/mir/stat/inference.d: -------------------------------------------------------------------------------- 1 | /++ 2 | This module contains statistical inference algorithms. 3 | 4 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 | 6 | Authors: Ilia Ki, John Michael Hall 7 | 8 | Copyright: 2022 Mir Stat Authors. 9 | 10 | +/ 11 | module mir.stat.inference; 12 | 13 | public import mir.stat.descriptive.univariate: KurtosisAlgo, Summation; 14 | import mir.internal.utility: isFloatingPoint; 15 | 16 | /++ 17 | Tests that a sample comes from a normal distribution. 18 | 19 | Params: 20 | kurtosisAlgo = algorithm for calculating skewness and kurtosis (default: KurtosisAlgo.online) 21 | summation = algorithm for calculating sums (default: Summation.appropriate) 22 | Returns: 23 | The kurtosis of the input, must be floating point 24 | References: 25 | D’Agostino, R. B. (1971), “An omnibus test of normality for moderate and large sample size”, Biometrika, 58, 341-348; 26 | D’Agostino, R. and Pearson, E. S. (1973), “Tests for departure from normality”, Biometrika, 60, 613-622 27 | +/ 28 | template dAgostinoPearsonTest( 29 | KurtosisAlgo kurtosisAlgo = KurtosisAlgo.online, 30 | Summation summation = Summation.appropriate) 31 | { 32 | import std.traits: isIterable; 33 | 34 | /++ 35 | Params: 36 | r = range, must be finite iterable 37 | p = null hypothesis probability 38 | +/ 39 | F dAgostinoPearsonTest(Range, F)(Range r, out F p) 40 | if(isFloatingPoint!F && isIterable!Range) 41 | { 42 | import core.lifetime: move; 43 | import mir.stat.descriptive.univariate: KurtosisAccumulator; 44 | import mir.stat.distribution.chi2: chi2CCDF; 45 | import mir.math.sum: ResolveSummationType; 46 | 47 | KurtosisAccumulator!(F, kurtosisAlgo, ResolveSummationType!(summation, Range, F)) kurtosisAccumulator = r; 48 | auto kurtosisStat = kurtosisTestImpl!F(kurtosisAccumulator); 49 | 50 | auto skewnessStat = skewnessTestImpl!F(kurtosisAccumulator); 51 | auto stat = skewnessStat * skewnessStat + kurtosisStat * kurtosisStat; 52 | p = chi2CCDF(stat, 2); 53 | return stat; 54 | } 55 | } 56 | 57 | /// ditto 58 | template dAgostinoPearsonTest(string kurtosisAlgo, string summation = "appropriate") 59 | { 60 | mixin("alias dAgostinoPearsonTest = .dAgostinoPearsonTest!(KurtosisAlgo." ~ kurtosisAlgo ~ ", Summation." ~ summation ~ ");"); 61 | } 62 | 63 | /// 64 | unittest 65 | { 66 | import mir.math.common: approxEqual, pow; 67 | import mir.math.sum: Summation; 68 | import mir.ndslice.slice: sliced; 69 | import mir.test; 70 | 71 | auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25, 72 | 2.0, 7.5, 5.0, 1.0, 1.5, 0.0]; 73 | 74 | double p; 75 | x.dAgostinoPearsonTest(p).shouldApprox == 4.151936053369771; 76 | p.shouldApprox == 0.12543494432988342; 77 | 78 | p = p.nan; 79 | x.dAgostinoPearsonTest!"threePass"(p).shouldApprox == 4.151936053369771; 80 | p.shouldApprox == 0.12543494432988342; 81 | } 82 | 83 | private F skewnessTestImpl(F, Accumulator)(ref const Accumulator acc) 84 | if (isFloatingPoint!F) 85 | { 86 | import mir.math.common: sqrt, log; 87 | auto b2 = acc.skewness!F(true); 88 | auto n = acc.count; 89 | assert(n > 7, "skewnessTestImpl: count must be larger than seven"); 90 | auto y = b2 * sqrt((F(n + 1) * (n + 3)) / (6 * (n - 2))); 91 | auto beta2 = 3 * (F(n) * n + 27 * n - 70) * (n + 1) * (n + 3) / (F(n - 2) * (n + 5) * (n + 7) * (n + 9)); 92 | auto w2 = -1 + sqrt(2 * (beta2 - 1)); 93 | auto delta = 1 / sqrt(0.5f * log(w2)); 94 | auto alpha = sqrt(2 / (w2 - 1)); 95 | auto y_alpha = y / alpha; 96 | return delta * log(y_alpha + sqrt(y_alpha * y_alpha + 1)); 97 | } 98 | 99 | version(mir_stat_test) 100 | @safe pure nothrow 101 | unittest 102 | { 103 | import mir.stat.descriptive.univariate: SkewnessAccumulator, SkewnessAlgo; 104 | import mir.math.common: approxEqual, pow; 105 | import mir.math.sum: Summation; 106 | import mir.ndslice.slice: sliced; 107 | import mir.test; 108 | 109 | auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25, 110 | 2.0, 7.5, 5.0, 1.0, 1.5, 0.0]; 111 | SkewnessAccumulator!(double, SkewnessAlgo.naive, Summation.naive) v = x; 112 | 113 | auto zsk = v.skewnessTestImpl!double; 114 | zsk.shouldApprox == 1.7985465327962042; 115 | import mir.stat.distribution.normal: normalCCDF; 116 | auto p = zsk.normalCCDF * 2; 117 | p.shouldApprox == 0.07209044155600682; 118 | } 119 | 120 | private F kurtosisTestImpl(F, Accumulator)(ref const Accumulator acc) 121 | if (isFloatingPoint!F) 122 | { 123 | import mir.math.common: copysign, sqrt, fabs, pow; 124 | auto b2 = acc.kurtosis!F(true, true); 125 | auto n = acc.count; 126 | assert(n > 7, "kurtosisTestImpl: count must be larger than seven"); 127 | auto varb2 = F(24) * n * (F(n - 2) * (n - 3)) / (F(n + 1) * (n + 1) * F((n + 3) * (n + 5))); 128 | auto x = (b2 - 3 * (n - 1) / F(n + 1)) / sqrt(varb2); 129 | auto beta1sqrt = 6 * (F(n) * n - 5 * n + 2) / (F(n + 7) * (n + 9)) * sqrt((6 * (n + 3) * F(n + 5)) / (n * F(n - 2) * (n - 3))); 130 | auto a = 6 + 8 / beta1sqrt * (2 / beta1sqrt + sqrt(1 + 4 / (beta1sqrt * beta1sqrt))); 131 | auto t1 = 1 - 2 / (9 * a); 132 | auto denom = 1 + x * sqrt(2 / (a - 4)); 133 | auto t2 = pow((1 - 2 / a) / denom.fabs, 1 / F(3)).copysign(denom); 134 | assert(denom); 135 | return (t1 - t2) * sqrt(F(4.5) * a); 136 | } 137 | 138 | version(mir_stat_test) 139 | @safe pure nothrow 140 | unittest 141 | { 142 | import mir.stat.descriptive.univariate: KurtosisAccumulator; 143 | import mir.math.common: approxEqual, pow; 144 | import mir.math.sum: Summation; 145 | import mir.ndslice.slice: sliced; 146 | import mir.test; 147 | 148 | auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25, 149 | 2.0, 7.5, 5.0, 1.0, 1.5, 0.0]; 150 | KurtosisAccumulator!(double, KurtosisAlgo.naive, Summation.naive) v = x; 151 | 152 | auto zku = v.kurtosisTestImpl!double; 153 | zku.shouldApprox == 0.9576880612895426; 154 | import mir.stat.distribution.normal: normalCCDF; 155 | auto p = zku.normalCCDF * 2; 156 | p.shouldApprox == 0.3382200786902009; 157 | } 158 | -------------------------------------------------------------------------------- /source/mir/stat/package.d: -------------------------------------------------------------------------------- 1 | /++ 2 | This package publicly imports `mir.stat.*` modules. 3 | 4 | $(BOOKTABLE , 5 | $(TR 6 | $(TH Modules) 7 | $(TH Description) 8 | ) 9 | $(TR $(TDNW $(MREF mir,stat,constant)) $(TD Constants used in other statistical modules )) 10 | $(TR $(TDNW $(MREF mir,stat,descriptive)) $(TD Descriptive Statistics )) 11 | $(TR $(TDNW $(MREF mir,stat,distribution)) $(TD Statistical Distributions )) 12 | $(TR $(TDNW $(MREF mir,stat,inference)) $(TD Probability Density/Mass Functions )) 13 | $(TR $(TDNW $(MREF mir,stat,transform)) $(TD Algorithms for statistical inference )) 14 | ) 15 | 16 | ## Example 17 | ------ 18 | import mir.algorithm.iteration: all; 19 | import mir.math.common: approxEqual, pow; 20 | import mir.stat; 21 | import mir.test: shouldApprox; 22 | 23 | // mir.stat.descriptive 24 | auto x = [1.0, 2, 3, 4]; 25 | x.mean.shouldApprox == 2.5; 26 | x.kurtosis.shouldApprox == -1.2; 27 | 28 | // mir.stat.distribution 29 | 4.binomialPMF(6, 2.0 / 3).shouldApprox == (15.0 * pow(2.0 / 3, 4) * pow(1.0 / 3, 2)); 30 | 31 | // mir.stat.transform 32 | assert(x.zscore.all!approxEqual([-1.161895, -0.387298, 0.387298, 1.161895])); 33 | 34 | // mir.stat.inference 35 | auto y = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25, 36 | 2.0, 7.5, 5.0, 1.0, 1.5, 0.0]; 37 | double p; 38 | y.dAgostinoPearsonTest(p).shouldApprox == 4.151936053369771; 39 | ------ 40 | 41 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 42 | 43 | Authors: John Michael Hall, Ilya Yaroshenko 44 | 45 | Copyright: 2022 Mir Stat Authors. 46 | 47 | Macros: 48 | SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, stat, $1)$(NBSP) 49 | MATHREF = $(GREF_ALTTEXT mir-algorithm, $(TT $2), $2, mir, math, $1)$(NBSP) 50 | NDSLICEREF = $(GREF_ALTTEXT mir-algorithm, $(TT $2), $2, mir, ndslice, $1)$(NBSP) 51 | T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) 52 | T4=$(TR $(TDNW $(LREF $1)) $(TD $2) $(TD $3) $(TD $4)) 53 | 54 | +/ 55 | 56 | module mir.stat; 57 | 58 | /// 59 | public import mir.stat.constant; 60 | /// 61 | public import mir.stat.descriptive; 62 | /// 63 | public import mir.stat.distribution; 64 | /// 65 | public import mir.stat.inference; 66 | /// 67 | public import mir.stat.transform; 68 | 69 | // Match comment above to ensure no errors 70 | version(mir_stat_test) 71 | @safe pure nothrow 72 | unittest 73 | { 74 | import mir.algorithm.iteration: all; 75 | import mir.math.common: approxEqual, pow; 76 | import mir.stat; 77 | import mir.test: shouldApprox; 78 | 79 | // mir.stat.descriptive 80 | auto x = [1.0, 2, 3, 4]; 81 | x.mean.shouldApprox == 2.5; 82 | x.kurtosis.shouldApprox == -1.2; 83 | 84 | // mir.stat.distribution 85 | 4.binomialPMF(6, 2.0 / 3).shouldApprox == (15.0 * pow(2.0 / 3, 4) * pow(1.0 / 3, 2)); 86 | 87 | // mir.stat.transform 88 | assert(x.zscore.all!approxEqual([-1.161895, -0.387298, 0.387298, 1.161895])); 89 | 90 | // mir.stat.inference 91 | auto y = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25, 92 | 2.0, 7.5, 5.0, 1.0, 1.5, 0.0]; 93 | double p; 94 | y.dAgostinoPearsonTest(p).shouldApprox == 4.151936053369771; 95 | } 96 | -------------------------------------------------------------------------------- /subprojects/cblas-d.wrap: -------------------------------------------------------------------------------- 1 | [wrap-git] 2 | directory=cblas-d 3 | url=https://github.com/DlangScience/cblas.git 4 | revision=head -------------------------------------------------------------------------------- /subprojects/lapack-d.wrap: -------------------------------------------------------------------------------- 1 | [wrap-git] 2 | directory=lapack-d 3 | url=https://github.com/libmir/lapack.git 4 | revision=head -------------------------------------------------------------------------------- /subprojects/mir-algorithm.wrap: -------------------------------------------------------------------------------- 1 | [wrap-git] 2 | directory=mir-algorithm 3 | url=https://github.com/libmir/mir-algorithm.git 4 | revision=head 5 | -------------------------------------------------------------------------------- /subprojects/mir-blas.wrap: -------------------------------------------------------------------------------- 1 | [wrap-git] 2 | directory=mir-blas 3 | url=https://github.com/libmir/mir-blas.git 4 | revision=head -------------------------------------------------------------------------------- /subprojects/mir-core.wrap: -------------------------------------------------------------------------------- 1 | [wrap-git] 2 | directory=mir-core 3 | url=https://github.com/libmir/mir-core.git 4 | revision=head 5 | -------------------------------------------------------------------------------- /subprojects/mir-lapack.wrap: -------------------------------------------------------------------------------- 1 | [wrap-git] 2 | directory=mir-lapack 3 | url=https://github.com/libmir/mir-lapack.git 4 | revision=head -------------------------------------------------------------------------------- /subprojects/mir-linux-kernel.wrap: -------------------------------------------------------------------------------- 1 | [wrap-git] 2 | directory=mir-linux-kernel 3 | url=https://github.com/libmir/mir-linux-kernel.git 4 | revision=head -------------------------------------------------------------------------------- /subprojects/mir-random.wrap: -------------------------------------------------------------------------------- 1 | [wrap-git] 2 | directory=mir-random 3 | url=https://github.com/libmir/mir-random.git 4 | revision=head --------------------------------------------------------------------------------