File Coverage

blib/lib/Algorithm/RandomMatrixGeneration.pm
Criterion Covered Total %
statement 106 118 89.8
branch 51 60 85.0
condition 3 3 100.0
subroutine 5 5 100.0
pod 0 1 0.0
total 165 187 88.2


line stmt bran cond sub pod time code
1             package Algorithm::RandomMatrixGeneration;
2              
3 4     4   136195 use 5.006;
  4         15  
  4         154  
4 4     4   22 use strict;
  4         10  
  4         150  
5 4     4   26 use warnings;
  4         15  
  4         109  
6 4     4   46077 use Math::BigFloat;
  4         288306  
  4         28  
7              
8             require Exporter;
9              
10             our @ISA = qw(Exporter);
11              
12             our @EXPORT = qw( generateMatrix );
13              
14             our $VERSION = '0.06';
15              
16             # add check for type of marginal values
17             # modify output format to sparse.
18              
19             sub generateMatrix
20             {
21 4     4 0 141 my $ref_rmar = shift;
22 4         11 my $ref_cmar = shift;
23              
24 4         8 my $precision = shift;
25 4         11 my $seed = shift;
26              
27 4         15 my @tmp_rmar = @$ref_rmar;
28 4         12 my @tmp_cmar = @$ref_cmar;
29              
30 4         7 my $n = $#tmp_rmar;
31 4         10 my $m = $#tmp_cmar;
32              
33             # error checks
34 4 50       18 if(!$n)
35             {
36 0         0 print STDERR "No row marginals provided.\n";
37 0         0 exit 1;
38             }
39              
40 4 50       20 if(!$m)
41             {
42 0         0 print STDERR "No column marginals provided.\n";
43 0         0 exit 1;
44             }
45              
46 4 50       22 if(!$precision)
47             {
48 0         0 print STDERR "Precision not provided.\n";
49 0         0 exit 1;
50             }
51              
52 4 50       25 if(defined $seed)
53             {
54 4         14 srand($seed);
55             }
56              
57             # find the type of marginals values: integers/real
58             # assume integer and loop through row and col marginals.
59             # check each value for decimal values.
60             # break on first occurrence of real number.
61 4         9 my $format = "integer";
62              
63             # also find if any of the marginal is negative
64             # assume positive and then loop through to find a contradiction
65 4         11 my $signValues = "positive";
66              
67             # for each row (0..n)
68 4         20 for(my $i=0; $i<=$n; $i++)
69             {
70 16 100       74 if($tmp_rmar[$i] =~ /\.0*[1-9]+/)
71             {
72 2         6 $format = "real";
73 2 50       8 if($signValues eq "negative")
74             {
75 0         0 last;
76             }
77             }
78 16 100       69 if($tmp_rmar[$i] =~ /^-/)
79             {
80 3         6 $signValues = "negative";
81 3 100       14 if($format eq "real")
82             {
83 1         2 last;
84             }
85             }
86             }
87              
88 4 100 100     31 if($format eq "integer" || $signValues eq "positive")
89             {
90             # for each col (0..m)
91 3         17 for(my $j=0; $j<=$m; $j++)
92             {
93 12 100       53 if($tmp_cmar[$j] =~ /\.0*[1-9]+/)
94             {
95 2         3 $format = "real";
96 2 50       9 if($signValues eq "negative")
97             {
98 0         0 last;
99             }
100             }
101 12 100       57 if($tmp_cmar[$j] =~ /^-/)
102             {
103 2         4 $signValues = "negative";
104 2 50       9 if($format eq "real")
105             {
106 0         0 last;
107             }
108             }
109             }
110             }
111              
112 4         11 my $fmt_str;
113             my $add_str;
114 0         0 my $regex;
115              
116 4 100       19 if($format eq "real")
117             {
118 2 50       13 if($precision !~ /^[0-9]+$/)
119             {
120 0         0 print STDERR "Please specify integer value for precision.\n";
121 0         0 exit 1;
122             }
123              
124             # for precision
125 2         11 $fmt_str = "%.$precision" . "f";
126            
127 2         4 $add_str = "0.";
128 2         10 for(my $i=1; $i<$precision; $i++)
129             {
130 6         15 $add_str .= "0";
131             }
132 2         4 $add_str .= "1";
133            
134 2         13 $regex = "(\\d+\\.?\\d{0,$precision})";
135             }
136              
137             # array to hold the generated matrix
138 4         26 my @ref = ();
139              
140 4         8 my $rem_col_marg = 0;
141            
142             # for each cell C(i,j)
143             # for each row (0..n)
144 4         21 for(my $i=0; $i<=$n; $i++)
145             {
146             # for each col (0..m)
147 25         60 for(my $j=0; $j<=$m; $j++)
148             {
149             # compute the min and max (range) for the cell value
150            
151             # max = MIN(row_marg[i], col_marg[j])
152 140         184 my $max = $tmp_rmar[$i];
153 140 100       328 if($tmp_cmar[$j] < $max)
154             {
155 59         74 $max = $tmp_cmar[$j];
156             }
157            
158             # if max = 0 then min = 0
159             # else assign min a value based on the remaining row_marginal and col_marginals to be satisfied
160 140         168 my $min = 0;
161 140 100       224 if($max != 0)
162             {
163             # sum-up the col_marginals for all the columns beyond the current column
164 133         147 $rem_col_marg = 0;
165 133         283 for(my $k=$j+1; $k<=$m; $k++)
166             {
167 365         692 $rem_col_marg = $rem_col_marg + $tmp_cmar[$k];
168             }
169            
170             # based on the row_marg and the sum_of_col_marg decide the value for min
171 133         209 $min = $tmp_rmar[$i] - $rem_col_marg;
172              
173 133 100       273 if($signValues eq "positive")
174             {
175 43 100       97 if($min < 0)
176             {
177 19         29 $min = 0;
178             }
179             }
180             }
181             else
182             {
183 7         9 $min = $max;
184             }
185            
186 140 100       260 if($format eq "real")
187             {
188 104         308 $min = sprintf($fmt_str, $min);
189 104         257 $max = sprintf($fmt_str, $max);
190             }
191              
192 140 100       287 if($signValues eq "negative")
193             {
194 92 100       198 if($min > $max)
195             {
196 88         93 my $tmp_min = $min;
197 88         79 $min = $max;
198 88         100 $max = $tmp_min;
199             }
200             }
201              
202 140         186 my $rand_num = 0;
203 140 100       252 if($min != $max)
204             {
205             # generate a random number between the min and max (range)
206 116 100       168 if($format eq "real")
207             {
208 94         115 my $rand_max = $max-$min+$add_str;
209 94         99 $rand_num = rand($rand_max);
210              
211 94         271 my $bigfloat = Math::BigFloat->new($rand_num);
212 94         14951 my $rand_num_str = $bigfloat->bstr();
213            
214 94         4249 $rand_num_str =~/$regex/;
215 94         336 $rand_num = $1;
216             }
217             else
218             {
219 22         32 my $rand_max = $max-$min+1;
220 22         37 $rand_num = int(rand($rand_max));
221             }
222              
223 116         204 $rand_num += $min;
224             }
225             else
226             {
227 24         35 $rand_num = $min;
228             }
229            
230 140 100       334 if($signValues eq "negative")
231             {
232             #last col of the row
233 92 100       160 if($j == $m)
234             {
235 13         24 $rand_num = $tmp_rmar[$i];
236             }
237            
238             #last row of the cols
239 92 100       147 if($i == $n)
240             {
241 12         17 $rand_num = $tmp_cmar[$j];
242             }
243             }
244              
245 140 100       220 if($format eq "real")
246             {
247 104         382 my $val = sprintf($fmt_str,$rand_num);
248              
249 104 50       240 if($val !~ /^0\.?0*$/)
250             {
251 104         583 $ref[$i][$j] = $val;
252              
253             # adjust the marginals
254 104         955 $tmp_rmar[$i] = sprintf($fmt_str, $tmp_rmar[$i] - $ref[$i][$j]);
255 104         484 $tmp_cmar[$j] = sprintf($fmt_str, $tmp_cmar[$j] - $ref[$i][$j]);
256             }
257            
258             }
259             else
260             {
261 36 100       91 if($rand_num)
262             {
263 26         48 $ref[$i][$j] = $rand_num;
264            
265             # adjust the marginals
266 26         49 $tmp_rmar[$i] = $tmp_rmar[$i] - $ref[$i][$j];
267 26         83 $tmp_cmar[$j] = $tmp_cmar[$j] - $ref[$i][$j];
268             }
269             }
270             }
271             }
272              
273 4         59 return @ref;
274             }
275              
276             1;
277             __END__