File Coverage

blib/lib/Math/Matrix/Banded/Rectangular.pm
Criterion Covered Total %
statement 168 177 94.9
branch 43 50 86.0
condition 20 25 80.0
subroutine 16 17 94.1
pod 0 8 0.0
total 247 277 89.1


line stmt bran cond sub pod time code
1             package Math::Matrix::Banded::Rectangular;
2 9     9   70318 use 5.014;
  9         43  
3              
4             =pod
5              
6             =head1 NAME
7              
8             Math::Matrix::Banded::Rectangular - banded non-square matrix
9              
10             =head1 VERSION
11              
12             Version 0.004
13              
14             =cut
15              
16             our $VERSION = '0.004';
17              
18 9     9   705 use Moo;
  9         12287  
  9         52  
19 9     9   4811 use Try::Tiny;
  9         1456  
  9         17834  
20              
21              
22             =pod
23              
24             =head1 SYNOPSIS
25              
26             Quick summary of what the module does.
27              
28             Perhaps a little code snippet.
29              
30             use Math::Matrix::Banded::Rectangular;
31              
32             my $foo = Math::Matrix::Banded::Rectangular->new();
33             ...
34              
35             =head1 DESCRIPTION
36              
37              
38             =head1 ATTRIBUTES
39              
40             =head3 data
41              
42             =cut
43              
44             has 'M' => (
45             is => 'ro',
46             required => 1,
47             );
48              
49             has 'N' => (
50             is => 'ro',
51             required => 1,
52             );
53              
54             has '_data' => (
55             is => 'lazy',
56             builder => sub {
57 27     27   289 my ($self) = @_;
58 27         64 my $M = $self->M;
59 27         50 my $data = [];
60              
61 27         75 for (my $i=0;$i<$M;$i++) {
62 124         319 push(@$data, [undef, []]);
63             }
64              
65 27         73 return $data;
66             },
67             );
68              
69             =pod
70              
71             =head1 METHODS
72              
73             =cut
74              
75             sub element {
76 222     222 0 96090 my ($self, $i, $j, @args) = @_;
77 222         3815 my $data = $self->_data;
78              
79 222         1283 my ($offset, $row_data) = @{$data->[$i]};
  222         446  
80 222         391 my $j_eff_max = $#$row_data;
81 222 100       453 my $j_eff = defined($offset) ? $j - $offset : undef;
82 222 100       498 if (@args > 0) {
83 207 100       490 if (!defined($j_eff)) {
    100          
    100          
84 102         215 $offset = $self->_adjust_offset($i, $j);
85 102         176 $j_eff = 0;
86             }
87             elsif ($j_eff < 0) {
88 2         6 $offset = $self->_adjust_offset($i, $offset + $j_eff);
89 2         5 $j_eff = 0;
90             }
91             elsif ($j_eff > $j_eff_max) {
92 52         120 $j_eff_max = $self->_adjust_range($i, $j_eff);
93             }
94 207         351 $row_data->[$j_eff] = $args[0];
95             }
96              
97 222 100 100     811 if (($j_eff // -1) < 0 or $j_eff > $j_eff_max) { return 0 }
  106   100     323  
98 116   50     419 else { return($row_data->[$j_eff] // 0) }
99             }
100              
101              
102             sub _set_offset {
103 123     123   206 my ($self, $i, $new_offset) = @_;
104 123         1828 my $data = $self->_data;
105 123         762 my ($offset, $row_data) = @{$data->[$i]};
  123         249  
106              
107             # should not happen
108 123 50 66     291 return if (defined($offset) and $offset <= $new_offset);
109              
110 123 100       252 if (@$row_data) {
111             # this implies that $offset is set
112 8         11 my $k = $offset - $new_offset;
113 8         20 my @zeroes = map { 0 } (1..$k);
  11         24  
114 8         20 unshift(@$row_data, @zeroes);
115             }
116 123         233 $data->[$i]->[0] = $new_offset;
117             }
118              
119              
120             sub _set_range {
121 130     130   212 my ($self, $i, $new_range) = @_;
122 130         2053 my $data = $self->_data;
123 130         800 my ($offset, $row_data) = @{$data->[$i]};
  130         259  
124              
125             # should not happen
126 130 50 33     479 return if (!defined($offset) or $#$row_data >= $new_range);
127              
128 130         265 my $k = $new_range - $#$row_data;
129 130         260 my @zeroes = map { 0 } (1..$k);
  199         411  
130 130         321 push(@$row_data, @zeroes);
131             }
132              
133              
134             sub _maintain_band_structure {
135 156     156   261 my ($self, $i) = @_;
136 156         346 my $M = $self->M;
137 156         2446 my $data = $self->_data;
138              
139             # should not happen
140 156 50       1085 return if (!defined($data->[$i]->[0]));
141              
142 156         234 my $k = $i;
143 156         355 while ($k > 0) {
144 129         208 my $max_offset = $data->[$k]->[0];
145 129         229 my $cur_offset = $data->[$k-1]->[0];
146 129 100 100     417 if (!defined($cur_offset) or $cur_offset > $max_offset) {
147 19         53 $self->_set_offset($k - 1, $max_offset);
148 19         43 $k--;
149             }
150 110         194 else { last }
151             }
152              
153 156         319 while ($k < $M) {
154 314 100       687 my $last_row = $k > 0 ? $data->[$k-1] : [0, []];
155 314         476 my $this_row = $data->[$k];
156 314 100       623 last if (!defined($this_row->[0]));
157              
158             my $min_range =
159 188         260 $#{$last_row->[1]} - ($this_row->[0] - $last_row->[0]);
  188         366  
160 188         310 my $cur_range = $#{$this_row->[1]};
  188         347  
161 188 100       377 if ($cur_range < $min_range) {
162 78         159 $self->_set_range($k, $min_range);
163             }
164 188         434 $k++;
165             }
166             }
167              
168              
169             sub _adjust_offset {
170 104     104   190 my ($self, $i, $new_offset) = @_;
171 104         1608 my $data = $self->_data;
172 104         659 my ($offset, $row_data) = @{$data->[$i]};
  104         208  
173              
174             # offset small enough; nothing to do here nor in rows above
175 104 50 66     262 if (defined($offset) and $offset <= $new_offset) {
176 0         0 return $offset;
177             }
178              
179 104         281 $self->_set_offset($i, $new_offset);
180 104         257 $self->_maintain_band_structure($i);
181              
182 104         169 return $new_offset;
183             }
184              
185              
186             sub _adjust_range {
187 52     52   98 my ($self, $i, $new_range) = @_;
188 52         799 my $data = $self->_data;
189 52         334 my ($offset, $row_data) = @{$data->[$i]};
  52         102  
190              
191 52 50       118 return undef if (!defined($offset));
192 52 50       129 return $#$row_data if ($#$row_data >= $new_range);
193              
194 52         139 $self->_set_range($i, $new_range);
195 52         141 $self->_maintain_band_structure($i);
196              
197 52         128 return $new_range;
198             }
199              
200              
201             sub row {
202 9     9 0 4498 my ($self, $i) = @_;
203 9         24 my $N = $self->N;
204 9         212 my $data = $self->_data;
205              
206 9         69 my ($offset, $row_data) = @{$data->[$i]};
  9         18  
207 9         19 my $j_eff_max = $#$row_data;
208 9         19 my $row = [];
209 9         22 for (my $j=0;$j<$N;$j++) {
210 63 100       164 my $j_eff = defined($offset) ? $j - $offset : undef;
211 63 100 100     238 push(
212             @$row,
213             (($j_eff // -1) < 0 || $j_eff > $j_eff_max)
214             ? 0 : $row_data->[$j_eff],
215             );
216             }
217              
218 9         65 return $row;
219             }
220              
221              
222             sub column {
223 7     7 0 3599 my ($self, $j) = @_;
224 7         16 my $M = $self->M;
225 7         16 my $N = $self->N;
226 7         168 my $data = $self->_data;
227              
228 7         53 my $col = [];
229 7         23 for (my $i=0;$i<$M;$i++) {
230 63         88 my ($offset, $row_data) = @{$data->[$i]};
  63         120  
231 63 100       114 my $j_eff = defined($offset) ? $j - $offset : undef;
232 63         88 my $j_eff_max = $#$row_data;
233 63 100 100     249 push(
234             @$col,
235             (($j_eff // -1) < 0 || $j_eff > $j_eff_max)
236             ? 0 : $row_data->[$j_eff],
237             );
238             }
239              
240 7         23 return $col;
241             }
242              
243              
244             sub as_string {
245 0     0 0 0 my ($self) = @_;
246 0         0 my $M = $self->M;
247              
248             return join(
249             qq{\n},
250 0         0 map { join(' ', map { sprintf(q{%7.3f}, $_) } @$_) }
  0         0  
251 0         0 map { $self->row($_) }
  0         0  
252             (0..$M-1),
253             );
254             }
255              
256              
257             sub transpose {
258 12     12 0 15912 my ($self) = @_;
259 12         44 my $M = $self->M;
260 12         23 my $N = $self->N;
261 12         276 my $data = $self->_data;
262              
263 12         254 my $t = Math::Matrix::Banded::Rectangular->new(
264             M => $N,
265             N => $M,
266             );
267 12         270 for (my $i=0;$i<$M;$i++) {
268 32         48 my ($offset, $row_data) = @{$data->[$i]};
  32         66  
269 32         72 for (my $j_eff=0;$j_eff<@$row_data;$j_eff++) {
270 68         108 my $j = $offset + $j_eff;
271 68         140 $t->element($j, $i, $row_data->[$j_eff]);
272             }
273             }
274              
275 12         85 return $t;
276             }
277              
278              
279             sub multiply_vector {
280 8     8 0 27 my ($self, $v) = @_;
281 8         20 my $M = $self->M;
282 8         17 my $N = $self->N;
283 8         205 my $data = $self->_data;
284              
285 8         61 my $w = [];
286 8         25 for (my $i=0;$i<$M;$i++) {
287 72         91 my ($offset, $row_data) = @{$data->[$i]};
  72         122  
288 72 50       136 if (!defined($offset)) {
289 0         0 $w->[$i] = 0 * $v->[0];
290 0         0 next;
291             }
292              
293 72         96 my $j_min = $offset;
294 72         97 my $j_max = $offset + $#$row_data;
295 72         113 my $w_i = 0 * $v->[0];
296 72         121 for (my $j=$j_min;$j<=$j_max;$j++) {
297 240         331 my $j_eff = $j - $offset;
298 240         442 $w_i += $row_data->[$j_eff] * $v->[$j];
299             }
300 72         159 $w->[$i] = $w_i;
301             }
302              
303 8         62 return $w;
304             }
305              
306              
307             sub AAt {
308 8     8 0 101 my ($self) = @_;
309 8         20 my $M = $self->M;
310 8         16 my $N = $self->N;
311 8         138 my $data = $self->_data;
312              
313 8         156 my $C = Math::Matrix::Banded::Square->new(
314             N => $M,
315             symmetric => 1,
316             );
317 8         2539 for (my $i=0;$i<$M;$i++) {
318 20         30 my ($offset_i, $row_data_i) = @{$data->[$i]};
  20         43  
319 20 100       47 last if (!defined($offset_i));
320              
321 18         31 my $k_max_i = $offset_i + $#$row_data_i;
322 18         45 for (my $j=$i;$j<$M;$j++) {
323             # We multiply the ith row of A with jth column of At,
324             # which is the jth row of A.
325 37         51 my ($offset_j, $row_data_j) = @{$data->[$j]};
  37         88  
326 37 100       74 last if (!defined($offset_j));
327              
328 36         46 my $k_min_j = $offset_j;
329 36 100       72 last if ($k_min_j > $k_max_i);
330              
331 32         43 my $c_ij = 0;
332 32         67 for (my $k=$k_min_j;$k<=$k_max_i;$k++) {
333 46         67 my $k_eff_i = $k - $offset_i;
334 46         65 my $k_eff_j = $k - $offset_j;
335 46         100 $c_ij +=
336             $row_data_i->[$k_eff_i] * $row_data_j->[$k_eff_j];
337             }
338 32         73 $C->element($i, $j, $c_ij);
339             }
340             }
341              
342 8         51 return $C;
343             }
344              
345              
346             sub AtA {
347 3     3 0 892 my ($self) = @_;
348              
349 3         10 return $self->transpose->AAt;
350             }
351              
352              
353             1;
354              
355             __END__