File Coverage

blib/lib/Math/Random/SkewNormal.pm
Criterion Covered Total %
statement 9 11 81.8
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 13 15 86.6


line stmt bran cond sub pod time code
1             # For Emacs: -*- mode:cperl; mode:folding; -*-
2             #
3             # (c) Jiri Vaclavik
4             #
5             package Math::Random::SkewNormal;
6              
7             # {{{ use
8              
9 1     1   45684 use 5.008;
  1         19  
10              
11 1     1   5 use strict;
  1         1  
  1         23  
12 1     1   4 use warnings;
  1         4  
  1         31  
13              
14 1     1   219 use Readonly;
  0            
  0            
15              
16             # }}}
17             # {{{ variables
18              
19             our @ISA = qw(Exporter);
20             our @EXPORT_OK = qw(generate_sn generate_sn_multi);
21              
22             my Readonly $pi = 3.14159265358979323846;
23              
24             our $VERSION = '0.03';
25              
26             # }}}
27              
28             # {{{ generate_sn exported
29              
30             sub generate_sn {
31             my $skewness = shift // 0;
32             my $a = generate_n();
33             my $b = generate_n();
34             my $sconst = $skewness / sqrt(1 + $skewness ** 2);
35             my $c = $sconst * $a + sqrt(1 - $sconst ** 2) * $b;
36             return $a > 0 ? $c
37             : -$c
38             ;
39             }
40              
41             # }}}
42             # {{{ generate_sn_multi exported
43              
44             sub generate_sn_multi {
45             my $delta = shift // return;
46             my $omega = shift // return;
47              
48             my $n = @$delta;
49             return if($n != @$omega);
50              
51             # build correlation matrix
52             my $cor = [];
53             for (0 .. $n-1){
54             $cor->[$_][$_] = 1;
55             }
56             for (1 .. $n){
57             $cor->[0][$_] = $delta->[$_-1];
58             $cor->[$_][0] = $delta->[$_-1];
59             }
60             for my $i (1 .. $n){
61             for my $j (1 .. $n){
62             $cor->[$i][$j] = $omega->[$i-1][$j-1];
63             }
64             }
65              
66             my $sample = generate_n_multi($cor);
67              
68             @$sample = map {-$_} @$sample if shift @$sample > 0;
69              
70             return $sample;
71             }
72              
73             # }}}
74              
75             # {{{ generate_r internal
76              
77             sub generate_r {
78             return rand;
79             }
80              
81             # }}}
82             # {{{ generate_n internal
83              
84             sub generate_n {
85             my $u = generate_r();
86             my $v = generate_r();
87             return sqrt(-2 * log $u) * cos(2 * $pi * $v);
88             }
89              
90             # }}}
91             # {{{ generate_n_multi internal
92              
93             sub generate_n_multi {
94             my $cov = shift // return;
95             my $n = @$cov || return;
96             my $independent = [];
97             my $result = [];
98             my $sqrt = _choleski($cov) // return;
99             for (0 .. $n-1){
100             $independent->[$_] = generate_n;
101             }
102             for my $i (0 .. $n-1){
103             $result->[$i] = 0;
104             for my $j (0 .. $n-1){
105             $result->[$i] += $independent->[$j] * $sqrt->[$i][$j];
106             }
107             }
108              
109             return $result;
110             }
111              
112             # }}}
113             # {{{ _choleski internal
114              
115             # choleski's decomposition
116             sub _choleski {
117             my $A = shift // return;
118             my $L = [];
119              
120             for my $c (0 .. @$A-1) {
121             my $sum = 0;
122             for my $j (0 .. $c - 1){
123             my $i = $c - 1 - $j;
124             $sum += $L->[$c][$i] ** 2;
125             }
126             $L->[$c][$c] = sqrt($A->[$c][$c] - $sum);
127             for my $r ($c+1 .. @$A-1) {
128             my $sum = 0;
129             for my $j (0 .. $c-1){
130             my $i = $c - 1 - $j;
131             $sum += $L->[$r][$i] * $L->[$c][$i];
132             }
133             $L->[$r][$c] = ($A->[$r][$c] - $sum) / $L->[$c][$c];
134             }
135             for my $r (0 .. $c-1) {
136             $L->[$r][$c] = 0;
137             }
138             }
139              
140             return $L;
141             }
142              
143             # }}}
144              
145             1;
146              
147             __END__