File Coverage

blib/lib/Math/Random/SkewNormal.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


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