Commit | Line | Data |
---|---|---|
86530b38 AT |
1 | # Copyright (c) 1996, 1997 by Steffen Beyer. All rights reserved. |
2 | # Copyright (c) 1999 by Rodolphe Ortalo. All rights reserved. | |
3 | # Copyright (c) 2001,2002 by Jonathan Leto. All rights reserved. | |
4 | # This package is free software; you can redistribute it and/or | |
5 | # modify it under the same terms as Perl itself. | |
6 | ||
7 | package Math::MatrixReal; | |
8 | ||
9 | use strict; | |
10 | use Carp; | |
11 | use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION); | |
12 | require Exporter; | |
13 | ||
14 | @ISA = qw(Exporter); | |
15 | @EXPORT = qw(); | |
16 | @EXPORT_OK = qw(min max); | |
17 | %EXPORT_TAGS = (all => [@EXPORT_OK]); | |
18 | $VERSION = '1.9'; | |
19 | ||
20 | use overload | |
21 | 'neg' => '_negate', | |
22 | '~' => '_transpose', | |
23 | 'bool' => '_boolean', | |
24 | '!' => '_not_boolean', | |
25 | '""' => '_stringify', | |
26 | 'abs' => '_norm', | |
27 | '+' => '_add', | |
28 | '-' => '_subtract', | |
29 | '*' => '_multiply', | |
30 | '**' => '_exponent', | |
31 | '+=' => '_assign_add', | |
32 | '-=' => '_assign_subtract', | |
33 | '*=' => '_assign_multiply', | |
34 | '**=' => '_assign_exponent', | |
35 | '==' => '_equal', | |
36 | '!=' => '_not_equal', | |
37 | '<' => '_less_than', | |
38 | '<=' => '_less_than_or_equal', | |
39 | '>' => '_greater_than', | |
40 | '>=' => '_greater_than_or_equal', | |
41 | 'eq' => '_equal', | |
42 | 'ne' => '_not_equal', | |
43 | 'lt' => '_less_than', | |
44 | 'le' => '_less_than_or_equal', | |
45 | 'gt' => '_greater_than', | |
46 | 'ge' => '_greater_than_or_equal', | |
47 | '=' => '_clone', | |
48 | 'exp' => '_exp', | |
49 | 'sin' => '_sin', | |
50 | 'cos' => '_cos', | |
51 | 'fallback' => undef; | |
52 | ||
53 | sub new | |
54 | { | |
55 | croak "Usage: \$new_matrix = Math::MatrixReal->new(\$rows,\$columns);" | |
56 | if (@_ != 3); | |
57 | ||
58 | my ($proto,$rows,$cols) = @_; | |
59 | my $class = ref($proto) || $proto || 'Math::MatrixReal'; | |
60 | my($i,$j,$this); | |
61 | ||
62 | croak "Math::MatrixReal::new(): number of rows must be integer > 0" | |
63 | unless ($rows > 0 and $rows == int($rows) ); | |
64 | ||
65 | croak "Math::MatrixReal::new(): number of columns must be integer > 0" | |
66 | unless ($cols > 0 and $cols == int($cols) ); | |
67 | ||
68 | $this = [ [ ], $rows, $cols ]; | |
69 | ||
70 | # Creates first empty row | |
71 | my $empty = [ ]; | |
72 | $#$empty = $cols - 1; # Lengthens the array | |
73 | for (my $j = 0; $j < $cols; $j++) | |
74 | { | |
75 | $empty->[$j] = 0.0; | |
76 | } | |
77 | $this->[0][0] = $empty; | |
78 | # Creates other rows (by copying) | |
79 | for (my $i = 1; $i < $rows; $i++) | |
80 | { | |
81 | my $arow = [ ]; | |
82 | @$arow = @$empty; | |
83 | $this->[0][$i] = $arow; | |
84 | } | |
85 | bless($this, $class); | |
86 | return($this); | |
87 | } | |
88 | sub new_diag { | |
89 | croak "Usage: \$new_matrix = Math::MatrixReal->new_diag( [ 1, 2, 3] );" | |
90 | unless (@_ == 2 ); | |
91 | my ($proto,$diag) = @_; | |
92 | my $class = ref($proto) || $proto || 'Math::MatrixReal'; | |
93 | my $matrix; | |
94 | my $n = scalar(@$diag); | |
95 | ||
96 | croak "Math::MatrixReal::new_diag(): Third argument must be an arrayref" | |
97 | unless (ref($diag) eq "ARRAY"); | |
98 | ||
99 | $matrix = Math::MatrixReal->new($n,$n); | |
100 | $matrix = $matrix->each_diag( sub { shift @$diag } ); | |
101 | return $matrix; | |
102 | } | |
103 | ||
104 | sub new_from_string | |
105 | { | |
106 | croak "Usage: \$new_matrix = Math::MatrixReal->new_from_string(\$string);" | |
107 | if (@_ != 2); | |
108 | ||
109 | my ($proto,$string) = @_; | |
110 | my $class = ref($proto) || $proto || 'Math::MatrixReal'; | |
111 | my($line,$values); | |
112 | my($rows,$cols); | |
113 | my($row,$col); | |
114 | my($warn,$this); | |
115 | ||
116 | $warn = $rows = $cols = 0; | |
117 | ||
118 | $values = [ ]; | |
119 | while ($string =~ m!^\s* | |
120 | \[ \s+ ( (?: [+-]? \d+ (?: \. \d* )? (?: E [+-]? \d+ )? \s+ )+ ) \] \s*? \n | |
121 | !ix) | |
122 | { | |
123 | $line = $1; | |
124 | $string = $'; | |
125 | $values->[$rows] = [ ]; | |
126 | @{$values->[$rows]} = split(' ', $line); | |
127 | $col = @{$values->[$rows]}; | |
128 | if ($col != $cols) | |
129 | { | |
130 | unless ($cols == 0) { $warn = 1; } | |
131 | if ($col > $cols) { $cols = $col; } | |
132 | } | |
133 | $rows++; | |
134 | } | |
135 | if ($string !~ m!^\s*$!) | |
136 | { | |
137 | #croak | |
138 | print "Math::MatrixReal::new_from_string(): syntax error in input string"; | |
139 | print "String is\n$string\n---\n"; | |
140 | croak; | |
141 | } | |
142 | if ($rows == 0) | |
143 | { | |
144 | croak "Math::MatrixReal::new_from_string(): empty input string"; | |
145 | } | |
146 | if ($warn) | |
147 | { | |
148 | warn "Math::MatrixReal::new_from_string(): missing elements will be set to zero!\n"; | |
149 | } | |
150 | $this = Math::MatrixReal::new($class,$rows,$cols); | |
151 | for ( $row = 0; $row < $rows; $row++ ) | |
152 | { | |
153 | for ( $col = 0; $col < @{$values->[$row]}; $col++ ) | |
154 | { | |
155 | $this->[0][$row][$col] = $values->[$row][$col]; | |
156 | } | |
157 | } | |
158 | return($this); | |
159 | } | |
160 | # from Math::MatrixReal::Ext1 | |
161 | sub new_from_cols { | |
162 | my $proto = shift; | |
163 | my $class = ref($proto) || $proto; | |
164 | my $ref_to_cols = shift; | |
165 | my @cols = @{$ref_to_cols}; | |
166 | ||
167 | my $cols = scalar( @cols ); | |
168 | ||
169 | my $matrix = 0; | |
170 | my $rows = 0; | |
171 | ||
172 | # each arg is a column, but we don't know what form they're | |
173 | # in yet | |
174 | ||
175 | my $col_index = 0; | |
176 | foreach my $col (@cols) { | |
177 | # it's one-based | |
178 | $col_index ++; | |
179 | my $ref = ref( $col ) ; | |
180 | ||
181 | if ( $ref =~ /^Math::MatrixReal/ ) { | |
182 | # it's already a Math::MatrixReal something | |
183 | } elsif ( $ref eq 'ARRAY' ) { | |
184 | my @array = @$col; | |
185 | my $length = scalar( @array ); | |
186 | $col = $class->new_from_string( '[ '. join( " ]\n[ ", @array) ." ]\n" ); | |
187 | } elsif ( $ref eq '' ) { | |
188 | # we hope this is a string | |
189 | $col = $class->new_from_string( $col ); | |
190 | } else { | |
191 | # we have no idea, error time! | |
192 | croak __PACKAGE__."::new_from_cols(): sorry, I have no clue what you sent me! I only know how to deal with array refs, strings, and things that are already in the Math::MatrixReal hierarchy \n"; | |
193 | } | |
194 | my ($length, $one) = $col->dim; | |
195 | croak __PACKAGE__."::new_from_cols(): This isn't a column vector" | |
196 | unless ($one == 1) ; | |
197 | ||
198 | # if we already have a height, check that this is the same | |
199 | if ($rows) { | |
200 | croak __PACKAGE__."::new_from_cols(): This column vector has $length elements and an earlier one had $rows" | |
201 | unless ($length == $rows) ; | |
202 | } | |
203 | # else, we have a new height | |
204 | # TODO: maybe this should check for zero | |
205 | else { | |
206 | $rows = $length; | |
207 | } | |
208 | ||
209 | # create the matrix the first time through | |
210 | unless ($matrix) { | |
211 | $matrix = $class->new($rows, $cols); | |
212 | } | |
213 | ||
214 | foreach my $row_index (1..$rows){ | |
215 | my $value = $col->element($row_index, 1); | |
216 | $matrix->assign($row_index, $col_index, $value); | |
217 | } | |
218 | ||
219 | } | |
220 | return $matrix; | |
221 | } | |
222 | #from Math::MatrixReal::Ext1 | |
223 | sub new_from_rows { | |
224 | my $proto = shift; | |
225 | my $class = ref($proto) || $proto; | |
226 | my $ref_to_rows = shift; | |
227 | my @rows = @{$ref_to_rows}; | |
228 | ||
229 | my $rows = scalar( @rows ); | |
230 | ||
231 | my $matrix = 0; | |
232 | my $cols = 0; | |
233 | ||
234 | # each arg is a column, but we don't know what form they're | |
235 | # in yet | |
236 | ||
237 | my $row_index = 0; | |
238 | foreach my $row (@rows) { | |
239 | # it's one-based | |
240 | $row_index ++; | |
241 | my $ref = ref( $row ) ; | |
242 | ||
243 | if ( $ref =~ /^Math::MatrixReal/ ) { | |
244 | # it's already a Math::MatrixReal something | |
245 | } | |
246 | elsif ( $ref eq 'ARRAY' ) { | |
247 | my @array = @$row; | |
248 | my $length = scalar( @array ); | |
249 | $row = $class->new_from_string( '[ '. join( " ", @array) ." ]\n" ); | |
250 | } | |
251 | elsif ( $ref eq '' ) { | |
252 | # we hope this is a string | |
253 | $row = $class->new_from_string( $row ); | |
254 | } | |
255 | else { | |
256 | # we have no idea, error time! | |
257 | croak "Math::MatrixReal::new_from_rows(): Argument must be an arrayref,string or Math::MatrixReal object"; | |
258 | } | |
259 | my ($one, $length) = $row->dim; | |
260 | croak __PACKAGE__."::new_from_rows(): This isn't a column vector" | |
261 | unless ($one == 1) ; | |
262 | ||
263 | # if we already have a height, check that this is the same | |
264 | if ($cols) { | |
265 | croak "Math::MatrixReal::new_from_rows(): Column mismatch $length != $cols" | |
266 | unless ($length == $cols) ; | |
267 | } | |
268 | # else, we have a new width FIXME maybe this should check for zero | |
269 | else { | |
270 | $cols = $length; | |
271 | } | |
272 | ||
273 | # create the matrix the first time through | |
274 | unless ($matrix) { | |
275 | $matrix = $class->new($rows, $cols); | |
276 | } | |
277 | ||
278 | foreach my $col_index (1..$cols){ | |
279 | my $value = $row->element(1, $col_index); | |
280 | $matrix->assign($row_index, $col_index, $value); | |
281 | } | |
282 | ||
283 | } | |
284 | return $matrix; | |
285 | } | |
286 | ||
287 | sub shadow | |
288 | { | |
289 | croak "Usage: \$new_matrix = \$some_matrix->shadow();" | |
290 | if (@_ != 1); | |
291 | ||
292 | my($matrix) = @_; | |
293 | my($temp); | |
294 | ||
295 | $temp = $matrix->new($matrix->[1],$matrix->[2]); | |
296 | return($temp); | |
297 | } | |
298 | ||
299 | ||
300 | sub copy | |
301 | { | |
302 | croak "Usage: \$matrix1->copy(\$matrix2);" | |
303 | if (@_ != 2); | |
304 | ||
305 | my($matrix1,$matrix2) = @_; | |
306 | my($rows1,$cols1) = ($matrix1->[1],$matrix1->[2]); | |
307 | my($rows2,$cols2) = ($matrix2->[1],$matrix2->[2]); | |
308 | my($i,$j); | |
309 | ||
310 | croak "Math::MatrixReal::copy(): matrix size mismatch" | |
311 | unless (($rows1 == $rows2) && ($cols1 == $cols2)); | |
312 | ||
313 | for ( $i = 0; $i < $rows1; $i++ ) | |
314 | { | |
315 | my $r1 = []; # New array ref | |
316 | my $r2 = $matrix2->[0][$i]; | |
317 | @$r1 = @$r2; # Copy whole array directly | |
318 | $matrix1->[0][$i] = $r1; | |
319 | } | |
320 | if (defined $matrix2->[3]) # is an LR decomposition matrix! | |
321 | { | |
322 | $matrix1->[3] = $matrix2->[3]; # $sign | |
323 | $matrix1->[4] = $matrix2->[4]; # $perm_row | |
324 | $matrix1->[5] = $matrix2->[5]; # $perm_col | |
325 | } | |
326 | } | |
327 | ||
328 | sub clone | |
329 | { | |
330 | croak "Usage: \$twin_matrix = \$some_matrix->clone();" | |
331 | if (@_ != 1); | |
332 | ||
333 | my($matrix) = @_; | |
334 | my($temp); | |
335 | ||
336 | $temp = $matrix->new($matrix->[1],$matrix->[2]); | |
337 | $temp->copy($matrix); | |
338 | return($temp); | |
339 | } | |
340 | ||
341 | ## trace() : return the sum of the diagonal elements | |
342 | sub trace { | |
343 | croak "Usage: \$trace = \$matrix->trace();" if (@_ != 1); | |
344 | ||
345 | my $matrix = shift; | |
346 | my($rows,$cols) = ($matrix->[1],$matrix->[2]); | |
347 | my $trace = 0; | |
348 | my($j); | |
349 | ||
350 | croak "Math::MatrixReal::trace(): matrix is not quadratic" | |
351 | unless ($rows == $cols); | |
352 | ||
353 | ||
354 | for ( $j = 0; $j < $cols; $j++ ) | |
355 | { | |
356 | $trace += $matrix->[0][$j][$j]; | |
357 | } | |
358 | return($trace); | |
359 | } | |
360 | ||
361 | ## return the minor corresponding to $r and $c | |
362 | ## eliminate row $r and col $c , and return the $r-1 by $c-1 matrix | |
363 | sub minor { | |
364 | croak "Usage: \$minor = \$matrix->minor(\$r,\$c);" unless (@_ == 3); | |
365 | my ($matrix,$r,$c) = @_; | |
366 | my ($rows,$cols) = $matrix->dim(); | |
367 | ||
368 | croak "Math::MatrixReal::minor(): \$matrix must be at least 2x2" | |
369 | unless ($rows > 1 and $cols > 1); | |
370 | croak "Math::MatrixReal::minor(): $r and $c must be positive" | |
371 | unless ($r > 0 and $c > 0 ); | |
372 | croak "Math::MatrixReal::minor(): matrix has no $r,$c element" | |
373 | unless ($r <= $rows and $c <= $cols ); | |
374 | ||
375 | my $minor = new Math::MatrixReal($rows-1,$cols-1); | |
376 | my ($i,$j) = (0,0); | |
377 | ||
378 | ## assign() might have been easier, but this should be faster | |
379 | ## the element can be in any of 4 regions compared to the eliminated | |
380 | ## row and col: | |
381 | ## above and to the left, above and to the right | |
382 | ## below and to the left, below and to the right | |
383 | ||
384 | for(; $i < $rows; $i++){ | |
385 | for(;$j < $rows; $j++ ){ | |
386 | if( $i >= $r && $j >= $c ){ | |
387 | $minor->[0][$i-1][$j-1] = $matrix->[0][$i][$j]; | |
388 | } elsif ( $i >= $r && $j < $c ){ | |
389 | $minor->[0][$i-1][$j] = $matrix->[0][$i][$j]; | |
390 | } elsif ( $i < $r && $j < $c ){ | |
391 | $minor->[0][$i][$j] = $matrix->[0][$i][$j]; | |
392 | } elsif ( $i < $r && $j >= $c ){ | |
393 | $minor->[0][$i][$j-1] = $matrix->[0][$i][$j]; | |
394 | } else { | |
395 | croak "Very bad things"; | |
396 | } | |
397 | } | |
398 | $j = 0; | |
399 | } | |
400 | return ($minor); | |
401 | } | |
402 | sub swap_col { | |
403 | croak "Usage: \$matrix->swap_col(\$col1,\$col2); " unless (@_ == 3); | |
404 | my ($matrix,$col1,$col2) = @_; | |
405 | my ($rows,$cols) = $matrix->dim(); | |
406 | my (@temp); | |
407 | ||
408 | croak "Math::MatrixReal::swap_col(): col index is not valid" | |
409 | unless ( $col1 <= $cols && $col2 <= $cols && | |
410 | $col1 == int($col1) && | |
411 | $col2 == int($col2) ); | |
412 | $col1--;$col2--; | |
413 | for(my $i=0;$i < $rows;$i++){ | |
414 | $temp[$i] = $matrix->[0][$i][$col1]; | |
415 | $matrix->[0][$i][$col1] = $matrix->[0][$i][$col2]; | |
416 | $matrix->[0][$i][$col2] = $temp[$i]; | |
417 | } | |
418 | } | |
419 | sub swap_row { | |
420 | croak "Usage: \$matrix->swap_row(\$row1,\$row2); " unless (@_ == 3); | |
421 | my ($matrix,$row1,$row2) = @_; | |
422 | my ($rows,$cols) = $matrix->dim(); | |
423 | my (@temp); | |
424 | ||
425 | croak "Math::MatrixReal::swap_row(): row index is not valid" | |
426 | unless ( $row1 <= $rows && $row2 <= $rows && | |
427 | $row1 == int($row1) && | |
428 | $row2 == int($row2) ); | |
429 | $row1--;$row2--; | |
430 | for(my $j=0;$j < $cols;$j++){ | |
431 | $temp[$j] = $matrix->[0][$row1][$j]; | |
432 | $matrix->[0][$row1][$j] = $matrix->[0][$row2][$j]; | |
433 | $matrix->[0][$row2][$j] = $temp[$j]; | |
434 | } | |
435 | } | |
436 | # TODO: docs | |
437 | # no worky | |
438 | sub assign_row { | |
439 | croak "Usage: \$matrix->assign_row(\$row,\$row_vec);" unless (@_ == 3); | |
440 | my ($matrix,$row,$row_vec) = @_; | |
441 | my ($rows1,$cols1) = $matrix->dim(); | |
442 | my ($rows2,$cols2) = $row_vec->dim(); | |
443 | ||
444 | croak "Math::MatrixReal::assign_row(): row mismatch" if ($rows1 != $rows2); | |
445 | croak "Math::MatrixReal::assign_row(): not a row vector" unless( $cols2 == 1); | |
446 | ||
447 | @{$matrix->[0][--$row]} = @{$row_vec->[0][0]}; | |
448 | ||
449 | } | |
450 | # returns the number of zeroes in a row | |
451 | sub _count_zeroes_row { | |
452 | my ($matrix) = @_; | |
453 | my ($rows,$cols) = $matrix->dim(); | |
454 | my $count = 0; | |
455 | croak "_count_zeroes_row(): only 1 row, buddy" unless ($rows == 1); | |
456 | ||
457 | for(my $i=0;$i < $cols;$i++){ | |
458 | $count++ unless $matrix->[0][0][$i]; | |
459 | } | |
460 | return $count; | |
461 | } | |
462 | ## divide a row by it's largest abs() element | |
463 | sub _normalize_row { | |
464 | my ($matrix) = @_; | |
465 | my ($rows,$cols) = $matrix->dim(); | |
466 | my $new_row = Math::MatrixReal->new(1,$cols); | |
467 | ||
468 | my $big = abs($matrix->[0][0][0]); | |
469 | for(my $j=0;$j < $cols; $j++ ){ | |
470 | $big = $big < abs($matrix->[0][0][$j]) | |
471 | ? abs($matrix->[0][0][$j]) : $big; | |
472 | } | |
473 | next unless $big; | |
474 | # now $big is biggest element in row | |
475 | for(my $j = 0;$j < $cols; $j++ ){ | |
476 | $new_row->[0][0][$j] = $matrix->[0][0][$j] / $big; | |
477 | } | |
478 | return $new_row; | |
479 | ||
480 | } | |
481 | #### doesn't work | |
482 | =crap | |
483 | sub row_echelon { | |
484 | my ($matrix) = @_; | |
485 | my ($rows,$cols) = $matrix->dim(); | |
486 | my $big; | |
487 | my $tmprow; | |
488 | ||
489 | for(my $i = 0;$i < $rows; $i++ ){ | |
490 | $big = abs($matrix->[0][$i][0]); | |
491 | for(my $j=0;$j < $cols; $j++ ){ | |
492 | $big = $big < abs($matrix->[0][$i][$j]) | |
493 | ? abs($matrix->[0][$i][$j]) : $big; | |
494 | } | |
495 | next unless $big; | |
496 | # now $big is biggest element in row | |
497 | for(my $j = 0;$j < $cols; $j++ ){ | |
498 | $matrix->[0][$i][$j] /= $big; | |
499 | } | |
500 | # now all elements are between [-1,1] and | |
501 | # dependence can be easily checked | |
502 | } | |
503 | ||
504 | for(my $i = 0;$i < $rows; $i++ ){ | |
505 | for(my $k = 0; $k < $rows; $k++ ){ | |
506 | next if ($i == $k); | |
507 | print "i,k = $i,$k\n"; | |
508 | $tmprow = $matrix->row($i+1) + $matrix->row($k+1); | |
509 | print "tmprow= $tmprow\n"; | |
510 | print "tmprow has " . $tmprow->_count_zeroes_row . " zeroes.\n"; | |
511 | print "i+1 has " . $matrix->row($i+1)->_count_zeroes_row . " zeroes\n"; | |
512 | print "k+1 has " . $matrix->row($k+1)->_count_zeroes_row . " zeroes\n"; | |
513 | ||
514 | $tmprow = $tmprow->_normalize_row; | |
515 | if( $tmprow->norm_sum == 0 ){ | |
516 | for(my $j=0;$j<$cols;$j++){ | |
517 | $matrix->[0][$k][$j] = 0; | |
518 | } | |
519 | } elsif ( $tmprow->_count_zeroes_row > $matrix->row($i+1)->_count_zeroes_row ){ | |
520 | print "tmprow has more zeroes, replacing row " . ($i+1) . "\n"; | |
521 | ## assign row | |
522 | $matrix->assign_row($i+1,$tmprow); | |
523 | } elsif ( $tmprow->_count_zeroes_row > $matrix->row($k+1)->_count_zeroes_row ){ | |
524 | print "tmprow has more zeroes, replacing row " . ($k+1) . "\n"; | |
525 | $matrix->assign_row($k+1,$tmprow); | |
526 | } | |
527 | ||
528 | } | |
529 | } | |
530 | return $matrix; | |
531 | } | |
532 | =cut | |
533 | ||
534 | sub cofactor { | |
535 | my ($matrix) = @_; | |
536 | my ($rows,$cols) = $matrix->dim(); | |
537 | ||
538 | croak "Math::MatrixReal::cofactor(): Matrix is not quadratic" | |
539 | unless ($rows == $cols); | |
540 | ||
541 | # black magic ahead | |
542 | my $cofactor = $matrix->each( sub { my($v,$i,$j) = @_; | |
543 | ($i+$j) % 2 == 0 ? $matrix->minor($i,$j)->det() | |
544 | : -1*$matrix->minor($i,$j)->det(); | |
545 | } ); | |
546 | return ($cofactor); | |
547 | } | |
548 | ||
549 | sub adjoint { | |
550 | my ($matrix) = @_; | |
551 | return ~($matrix->cofactor); | |
552 | } | |
553 | sub row | |
554 | { | |
555 | croak "Usage: \$row_vector = \$matrix->row(\$row);" | |
556 | if (@_ != 2); | |
557 | ||
558 | my($matrix,$row) = @_; | |
559 | my($rows,$cols) = ($matrix->[1],$matrix->[2]); | |
560 | my($temp); | |
561 | my($j); | |
562 | ||
563 | croak "Math::MatrixReal::row(): row index out of range" | |
564 | if (($row < 1) || ($row > $rows)); | |
565 | ||
566 | $row--; | |
567 | $temp = $matrix->new(1,$cols); | |
568 | for ( $j = 0; $j < $cols; $j++ ) | |
569 | { | |
570 | $temp->[0][0][$j] = $matrix->[0][$row][$j]; | |
571 | } | |
572 | return($temp); | |
573 | } | |
574 | ||
575 | sub column | |
576 | { | |
577 | croak "Usage: \$column_vector = \$matrix->column(\$column);" | |
578 | if (@_ != 2); | |
579 | ||
580 | my($matrix,$col) = @_; | |
581 | my($rows,$cols) = ($matrix->[1],$matrix->[2]); | |
582 | my($temp); | |
583 | my($i); | |
584 | ||
585 | croak "Math::MatrixReal::column(): column index out of range" | |
586 | if (($col < 1) || ($col > $cols)); | |
587 | ||
588 | $col--; | |
589 | $temp = $matrix->new($rows,1); | |
590 | for ( $i = 0; $i < $rows; $i++ ) | |
591 | { | |
592 | $temp->[0][$i][0] = $matrix->[0][$i][$col]; | |
593 | } | |
594 | return($temp); | |
595 | } | |
596 | ||
597 | sub _undo_LR | |
598 | { | |
599 | croak "Usage: \$matrix->_undo_LR();" | |
600 | if (@_ != 1); | |
601 | ||
602 | my($this) = @_; | |
603 | ||
604 | undef $this->[3]; | |
605 | undef $this->[4]; | |
606 | undef $this->[5]; | |
607 | } | |
608 | ||
609 | sub zero | |
610 | { | |
611 | croak "Usage: \$matrix->zero();" | |
612 | if (@_ != 1); | |
613 | ||
614 | my($this) = @_; | |
615 | my($rows,$cols) = ($this->[1],$this->[2]); | |
616 | my($i,$j); | |
617 | ||
618 | $this->_undo_LR(); | |
619 | ||
620 | # Zero first row | |
621 | for (my $j = 0; $j < $cols; $j++ ) | |
622 | { | |
623 | $this->[0][0][$j] = 0.0; | |
624 | } | |
625 | # Then propagate to other rows | |
626 | for (my $i = 0; $i < $rows; $i++) | |
627 | { | |
628 | @{$this->[0][$i]} = @{$this->[0][0]}; | |
629 | } | |
630 | } | |
631 | ||
632 | sub one | |
633 | { | |
634 | croak "Usage: \$matrix->one();" | |
635 | if (@_ != 1); | |
636 | ||
637 | my($this) = @_; | |
638 | my($rows,$cols) = ($this->[1],$this->[2]); | |
639 | my($i,$j); | |
640 | ||
641 | # No need for this: done by the 'zero()' | |
642 | # $this->_undo_LR(); | |
643 | $this->zero(); # We rely on zero() efficiency | |
644 | for (my $i = 0; $i < $rows; $i++ ) | |
645 | { | |
646 | $this->[0][$i][$i] = 1.0; | |
647 | } | |
648 | } | |
649 | ||
650 | sub assign | |
651 | { | |
652 | croak "Usage: \$matrix->assign(\$row,\$column,\$value);" | |
653 | if (@_ != 4); | |
654 | ||
655 | my($this,$row,$col,$value) = @_; | |
656 | my($rows,$cols) = ($this->[1],$this->[2]); | |
657 | ||
658 | croak "Math::MatrixReal::assign(): row index out of range" | |
659 | if (($row < 1) || ($row > $rows)); | |
660 | ||
661 | croak "Math::MatrixReal::assign(): column index out of range" | |
662 | if (($col < 1) || ($col > $cols)); | |
663 | ||
664 | $this->_undo_LR(); | |
665 | ||
666 | $this->[0][--$row][--$col] = $value; | |
667 | } | |
668 | ||
669 | sub element | |
670 | { | |
671 | croak "Usage: \$value = \$matrix->element(\$row,\$column);" | |
672 | if (@_ != 3); | |
673 | ||
674 | my($this,$row,$col) = @_; | |
675 | my($rows,$cols) = ($this->[1],$this->[2]); | |
676 | ||
677 | croak "Math::MatrixReal::element(): row index out of range" | |
678 | if (($row < 1) || ($row > $rows)); | |
679 | ||
680 | croak "Math::MatrixReal::element(): column index out of range" | |
681 | if (($col < 1) || ($col > $cols)); | |
682 | ||
683 | return( $this->[0][--$row][--$col] ); | |
684 | } | |
685 | ||
686 | sub dim # returns dimensions of a matrix | |
687 | { | |
688 | croak "Usage: (\$rows,\$columns) = \$matrix->dim();" | |
689 | if (@_ != 1); | |
690 | ||
691 | my($matrix) = @_; | |
692 | ||
693 | return( $matrix->[1], $matrix->[2] ); | |
694 | } | |
695 | ||
696 | sub norm_one # maximum of sums of each column | |
697 | { | |
698 | croak "Usage: \$norm_one = \$matrix->norm_one();" | |
699 | if (@_ != 1); | |
700 | ||
701 | my($this) = @_; | |
702 | my($rows,$cols) = ($this->[1],$this->[2]); | |
703 | ||
704 | my $max = 0.0; | |
705 | for (my $j = 0; $j < $cols; $j++) | |
706 | { | |
707 | my $sum = 0.0; | |
708 | for (my $i = 0; $i < $rows; $i++) | |
709 | { | |
710 | $sum += abs( $this->[0][$i][$j] ); | |
711 | } | |
712 | $max = $sum if ($sum > $max); | |
713 | } | |
714 | return($max); | |
715 | } | |
716 | ## sum of absolute value of every element | |
717 | sub norm_sum { | |
718 | croak "Usage: \$norm_sum = \$matrix->norm_sum();" | |
719 | unless (@_ == 1); | |
720 | my ($matrix) = @_; | |
721 | my $norm = 0; | |
722 | $matrix->each( sub { $norm+=abs(shift); } ); | |
723 | return $norm; | |
724 | } | |
725 | sub norm_frobenius { | |
726 | my ($m) = @_; | |
727 | my ($r,$c) = $m->dim; | |
728 | my $s=0; | |
729 | ||
730 | $m->each( sub { $s+=abs(shift)**2 } ); | |
731 | return sqrt($s); | |
732 | } | |
733 | ||
734 | # Vector Norm | |
735 | sub norm_p { | |
736 | my ($v,$p) = @_; | |
737 | # sanity check on $p | |
738 | croak "Math::MatrixReal:norm_p: argument must be a row or column vector" | |
739 | unless ( $v->is_row_vector || $v->is_col_vector ); | |
740 | croak "Math::MatrixReal::norm_p: $p must be >= 1" | |
741 | unless ($p =~ m/Inf(inity)?/i || $p >= 1); | |
742 | ||
743 | if( $p =~ m/^(Inf|Infinity)$/i ){ | |
744 | my $max = $v->element(1,1); | |
745 | $v->each ( sub { my $x=abs(shift); $max = $x if( $x > $max ); } ); | |
746 | return $max; | |
747 | } | |
748 | ||
749 | my $s=0; | |
750 | $v->each( sub { $s+= (abs(shift))**$p; } ); | |
751 | return $s ** (1/$p); | |
752 | ||
753 | } | |
754 | sub norm_max # maximum of sums of each row | |
755 | { | |
756 | croak "Usage: \$norm_max = \$matrix->norm_max();" | |
757 | if (@_ != 1); | |
758 | ||
759 | my($this) = @_; | |
760 | my($rows,$cols) = ($this->[1],$this->[2]); | |
761 | ||
762 | my $max = 0.0; | |
763 | for (my $i = 0; $i < $rows; $i++) | |
764 | { | |
765 | my $sum = 0.0; | |
766 | for (my $j = 0; $j < $cols; $j++) | |
767 | { | |
768 | $sum += abs( $this->[0][$i][$j] ); | |
769 | } | |
770 | $max = $sum if ($sum > $max); | |
771 | } | |
772 | return($max); | |
773 | } | |
774 | ||
775 | sub negate | |
776 | { | |
777 | croak "Usage: \$matrix1->negate(\$matrix2);" | |
778 | if (@_ != 2); | |
779 | ||
780 | my($matrix1,$matrix2) = @_; | |
781 | my($rows1,$cols1) = ($matrix1->[1],$matrix1->[2]); | |
782 | my($rows2,$cols2) = ($matrix2->[1],$matrix2->[2]); | |
783 | ||
784 | croak "Math::MatrixReal::negate(): matrix size mismatch" | |
785 | unless (($rows1 == $rows2) && ($cols1 == $cols2)); | |
786 | ||
787 | $matrix1->_undo_LR(); | |
788 | ||
789 | for (my $i = 0; $i < $rows1; $i++ ) | |
790 | { | |
791 | for (my $j = 0; $j < $cols1; $j++ ) | |
792 | { | |
793 | $matrix1->[0][$i][$j] = -($matrix2->[0][$i][$j]); | |
794 | } | |
795 | } | |
796 | } | |
797 | ## each(): evaluate a coderef on each element and return a new matrix | |
798 | ## of said | |
799 | sub each { | |
800 | croak "Usage: \$new_matrix = \$matrix->each( \&sub );" unless (@_ == 2 ); | |
801 | my($matrix,$function) = @_; | |
802 | my($rows,$cols) = ($matrix->[1],$matrix->[2]); | |
803 | my($new_matrix) = $matrix->clone(); | |
804 | ||
805 | croak "Math::MatrixReal::each(): argument is not a sub reference" unless ref($function); | |
806 | $new_matrix->_undo_LR(); | |
807 | ||
808 | for (my $i = 0; $i < $rows; $i++ ) { | |
809 | for (my $j = 0; $j < $cols; $j++ ) { | |
810 | no strict 'refs'; | |
811 | # $i,$j are 1-based as of 1.7 | |
812 | $new_matrix->[0][$i][$j] = &{ $function }($matrix->[0][$i][$j],$i+1,$j+1) ; | |
813 | } | |
814 | } | |
815 | return ($new_matrix); | |
816 | } | |
817 | ## each_diag(): same as each() but only diag elements | |
818 | sub each_diag { | |
819 | croak "Usage: \$new_matrix = \$matrix->each_diag( \&sub );" unless (@_ == 2 ); | |
820 | my($matrix,$function) = @_; | |
821 | my($rows,$cols) = ($matrix->[1],$matrix->[2]); | |
822 | my($new_matrix) = $matrix->clone(); | |
823 | ||
824 | croak "Math::MatrixReal::each(): argument is not a sub reference" unless ref($function); | |
825 | croak "Matrix is not quadratic" unless ($rows == $cols); | |
826 | ||
827 | $new_matrix->_undo_LR(); | |
828 | ||
829 | for (my $i = 0; $i < $rows; $i++ ) { | |
830 | for (my $j = 0; $j < $cols; $j++ ) { | |
831 | next unless ($i == $j); | |
832 | no strict 'refs'; | |
833 | # $i,$j are 1-based as of 1.7 | |
834 | $new_matrix->[0][$i][$j] = &{ $function }($matrix->[0][$i][$j],$i+1,$j+1) ; | |
835 | } | |
836 | } | |
837 | return ($new_matrix); | |
838 | } | |
839 | ||
840 | ## Make computing the inverse more user friendly | |
841 | sub inverse { | |
842 | croak "Usage: \$inverse = \$matrix->inverse();" unless (@_ == 1); | |
843 | my ($matrix) = @_; | |
844 | return $matrix->decompose_LR->invert_LR; | |
845 | } | |
846 | ||
847 | sub transpose { | |
848 | ||
849 | croak "Usage: \$matrix1->transpose(\$matrix2);" | |
850 | if (@_ != 2); | |
851 | ||
852 | my($matrix1,$matrix2) = @_; | |
853 | my($rows1,$cols1) = ($matrix1->[1],$matrix1->[2]); | |
854 | my($rows2,$cols2) = ($matrix2->[1],$matrix2->[2]); | |
855 | ||
856 | croak "Math::MatrixReal::transpose(): matrix size mismatch" | |
857 | unless (($rows1 == $cols2) && ($cols1 == $rows2)); | |
858 | ||
859 | $matrix1->_undo_LR(); | |
860 | ||
861 | if ($rows1 == $cols1) | |
862 | { | |
863 | # more complicated to make in-place possible! | |
864 | ||
865 | for (my $i = 0; $i < $rows1; $i++) | |
866 | { | |
867 | for (my $j = ($i + 1); $j < $cols1; $j++) | |
868 | { | |
869 | my $swap = $matrix2->[0][$i][$j]; | |
870 | $matrix1->[0][$i][$j] = $matrix2->[0][$j][$i]; | |
871 | $matrix1->[0][$j][$i] = $swap; | |
872 | } | |
873 | $matrix1->[0][$i][$i] = $matrix2->[0][$i][$i]; | |
874 | } | |
875 | } | |
876 | else # ($rows1 != $cols1) | |
877 | { | |
878 | for (my $i = 0; $i < $rows1; $i++) | |
879 | { | |
880 | for (my $j = 0; $j < $cols1; $j++) | |
881 | { | |
882 | $matrix1->[0][$i][$j] = $matrix2->[0][$j][$i]; | |
883 | } | |
884 | } | |
885 | } | |
886 | } | |
887 | ||
888 | sub add | |
889 | { | |
890 | croak "Usage: \$matrix1->add(\$matrix2,\$matrix3);" | |
891 | if (@_ != 3); | |
892 | ||
893 | my($matrix1,$matrix2,$matrix3) = @_; | |
894 | my($rows1,$cols1) = ($matrix1->[1],$matrix1->[2]); | |
895 | my($rows2,$cols2) = ($matrix2->[1],$matrix2->[2]); | |
896 | my($rows3,$cols3) = ($matrix3->[1],$matrix3->[2]); | |
897 | my($i,$j); | |
898 | ||
899 | croak "Math::MatrixReal::add(): matrix size mismatch" | |
900 | unless (($rows1 == $rows2) && ($rows1 == $rows3) && | |
901 | ($cols1 == $cols2) && ($cols1 == $cols3)); | |
902 | ||
903 | $matrix1->_undo_LR(); | |
904 | ||
905 | for ( $i = 0; $i < $rows1; $i++ ) | |
906 | { | |
907 | for ( $j = 0; $j < $cols1; $j++ ) | |
908 | { | |
909 | $matrix1->[0][$i][$j] = | |
910 | $matrix2->[0][$i][$j] + $matrix3->[0][$i][$j]; | |
911 | } | |
912 | } | |
913 | } | |
914 | ||
915 | sub subtract | |
916 | { | |
917 | croak "Usage: \$matrix1->subtract(\$matrix2,\$matrix3);" | |
918 | if (@_ != 3); | |
919 | ||
920 | my($matrix1,$matrix2,$matrix3) = @_; | |
921 | my($rows1,$cols1) = ($matrix1->[1],$matrix1->[2]); | |
922 | my($rows2,$cols2) = ($matrix2->[1],$matrix2->[2]); | |
923 | my($rows3,$cols3) = ($matrix3->[1],$matrix3->[2]); | |
924 | my($i,$j); | |
925 | ||
926 | croak "Math::MatrixReal::subtract(): matrix size mismatch" | |
927 | unless (($rows1 == $rows2) && ($rows1 == $rows3) && | |
928 | ($cols1 == $cols2) && ($cols1 == $cols3)); | |
929 | ||
930 | $matrix1->_undo_LR(); | |
931 | ||
932 | for ( $i = 0; $i < $rows1; $i++ ) | |
933 | { | |
934 | for ( $j = 0; $j < $cols1; $j++ ) | |
935 | { | |
936 | $matrix1->[0][$i][$j] = | |
937 | $matrix2->[0][$i][$j] - $matrix3->[0][$i][$j]; | |
938 | } | |
939 | } | |
940 | } | |
941 | ||
942 | sub multiply_scalar | |
943 | { | |
944 | croak "Usage: \$matrix1->multiply_scalar(\$matrix2,\$scalar);" | |
945 | if (@_ != 3); | |
946 | ||
947 | my($matrix1,$matrix2,$scalar) = @_; | |
948 | my($rows1,$cols1) = ($matrix1->[1],$matrix1->[2]); | |
949 | my($rows2,$cols2) = ($matrix2->[1],$matrix2->[2]); | |
950 | my($i,$j); | |
951 | ||
952 | croak "Math::MatrixReal::multiply_scalar(): matrix size mismatch" | |
953 | unless (($rows1 == $rows2) && ($cols1 == $cols2)); | |
954 | ||
955 | $matrix1->_undo_LR(); | |
956 | ||
957 | for ( $i = 0; $i < $rows1; $i++ ) | |
958 | { | |
959 | for ( $j = 0; $j < $cols1; $j++ ) | |
960 | { | |
961 | $matrix1->[0][$i][$j] = $matrix2->[0][$i][$j] * $scalar; | |
962 | } | |
963 | } | |
964 | } | |
965 | ||
966 | sub multiply | |
967 | { | |
968 | croak "Usage: \$product_matrix = \$matrix1->multiply(\$matrix2);" | |
969 | if (@_ != 2); | |
970 | ||
971 | my($matrix1,$matrix2) = @_; | |
972 | my($rows1,$cols1) = ($matrix1->[1],$matrix1->[2]); | |
973 | my($rows2,$cols2) = ($matrix2->[1],$matrix2->[2]); | |
974 | my($temp); | |
975 | ||
976 | croak "Math::MatrixReal::multiply(): matrix size mismatch" | |
977 | unless ($cols1 == $rows2); | |
978 | ||
979 | $temp = $matrix1->new($rows1,$cols2); | |
980 | for (my $i = 0; $i < $rows1; $i++ ) | |
981 | { | |
982 | for (my $j = 0; $j < $cols2; $j++ ) | |
983 | { | |
984 | my $sum = 0.0; | |
985 | for (my $k = 0; $k < $cols1; $k++ ) | |
986 | { | |
987 | $sum += ( $matrix1->[0][$i][$k] * $matrix2->[0][$k][$j] ); | |
988 | } | |
989 | $temp->[0][$i][$j] = $sum; | |
990 | } | |
991 | } | |
992 | return($temp); | |
993 | } | |
994 | ||
995 | sub exponent { | |
996 | croak "Usage: \$matrix_exp = \$matrix1->exponent(\$integer);" if(@_ != 2 ); | |
997 | my($matrix,$argument) = @_; | |
998 | my($rows,$cols) = ($matrix->[1],$matrix->[2]); | |
999 | my($name) = "'**'"; | |
1000 | my($temp) = $matrix->clone(); | |
1001 | ||
1002 | croak "Matrix is not quadratic" unless ($rows == $cols); | |
1003 | croak "Exponent must be integer" unless ($argument =~ m/^[+-]?\d+$/ ); | |
1004 | ||
1005 | return($matrix) if ($argument == 1); | |
1006 | ||
1007 | $temp->_undo_LR(); | |
1008 | ||
1009 | # negative exponent is (A^-1)^n | |
1010 | if( $argument < 0 ){ | |
1011 | my $LR = $matrix->decompose_LR(); | |
1012 | my $inverse = $LR->invert_LR(); | |
1013 | unless (defined $inverse){ | |
1014 | carp "Matrix has no inverse"; | |
1015 | return undef; | |
1016 | } | |
1017 | $temp = $inverse->clone(); | |
1018 | if( $inverse ){ | |
1019 | return($inverse) if ($argument == -1); | |
1020 | for( 2 .. abs($argument) ){ | |
1021 | $temp = multiply($inverse,$temp); | |
1022 | } | |
1023 | return($temp); | |
1024 | } else { | |
1025 | # TODO: is this the right behaviour? | |
1026 | carp "Cannot compute negative exponent, inverse does not exist"; | |
1027 | return undef; | |
1028 | } | |
1029 | # matrix to zero power is identity matrix | |
1030 | } elsif( $argument == 0 ){ | |
1031 | $temp->one(); | |
1032 | return ($temp); | |
1033 | } | |
1034 | ||
1035 | # if it is diagonal, just raise diagonal entries to power | |
1036 | if( $matrix->is_diagonal() ){ | |
1037 | $temp = $temp->each_diag( sub { (shift)**$argument } ); | |
1038 | return ($temp); | |
1039 | ||
1040 | } else { | |
1041 | for( 2 .. $argument ){ | |
1042 | $temp = multiply($matrix,$temp); | |
1043 | } | |
1044 | return ($temp); | |
1045 | } | |
1046 | } | |
1047 | ||
1048 | sub min | |
1049 | { | |
1050 | croak "Usage: \$minimum = Math::MatrixReal::min(\$number1,\$number2);" | |
1051 | if (@_ != 2); | |
1052 | ||
1053 | return( $_[0] < $_[1] ? $_[0] : $_[1] ); | |
1054 | } | |
1055 | ||
1056 | sub max | |
1057 | { | |
1058 | croak "Usage: \$maximum = Math::MatrixReal::max(\$number1,\$number2);" | |
1059 | if (@_ != 2); | |
1060 | ||
1061 | return( $_[0] > $_[1] ? $_[0] : $_[1] ); | |
1062 | } | |
1063 | ||
1064 | sub kleene | |
1065 | { | |
1066 | croak "Usage: \$minimal_cost_matrix = \$cost_matrix->kleene();" | |
1067 | if (@_ != 1); | |
1068 | ||
1069 | my($matrix) = @_; | |
1070 | my($rows,$cols) = ($matrix->[1],$matrix->[2]); | |
1071 | my($i,$j,$k,$n); | |
1072 | my($temp); | |
1073 | ||
1074 | croak "Math::MatrixReal::kleene(): matrix is not quadratic" | |
1075 | unless ($rows == $cols); | |
1076 | ||
1077 | $temp = $matrix->new($rows,$cols); | |
1078 | $temp->copy($matrix); | |
1079 | $temp->_undo_LR(); | |
1080 | $n = $rows; | |
1081 | for ( $i = 0; $i < $n; $i++ ) | |
1082 | { | |
1083 | $temp->[0][$i][$i] = min( $temp->[0][$i][$i] , 0 ); | |
1084 | } | |
1085 | for ( $k = 0; $k < $n; $k++ ) | |
1086 | { | |
1087 | for ( $i = 0; $i < $n; $i++ ) | |
1088 | { | |
1089 | for ( $j = 0; $j < $n; $j++ ) | |
1090 | { | |
1091 | $temp->[0][$i][$j] = min( $temp->[0][$i][$j] , | |
1092 | ( $temp->[0][$i][$k] + | |
1093 | $temp->[0][$k][$j] ) ); | |
1094 | } | |
1095 | } | |
1096 | } | |
1097 | return($temp); | |
1098 | } | |
1099 | ||
1100 | sub normalize | |
1101 | { | |
1102 | croak "Usage: (\$norm_matrix,\$norm_vector) = \$matrix->normalize(\$vector);" | |
1103 | if (@_ != 2); | |
1104 | ||
1105 | my($matrix,$vector) = @_; | |
1106 | my($rows,$cols) = ($matrix->[1],$matrix->[2]); | |
1107 | my($norm_matrix,$norm_vector); | |
1108 | my($max,$val); | |
1109 | my($i,$j,$n); | |
1110 | ||
1111 | croak "Math::MatrixReal::normalize(): matrix is not quadratic" | |
1112 | unless ($rows == $cols); | |
1113 | ||
1114 | $n = $rows; | |
1115 | ||
1116 | croak "Math::MatrixReal::normalize(): vector is not a column vector" | |
1117 | unless ($vector->[2] == 1); | |
1118 | ||
1119 | croak "Math::MatrixReal::normalize(): matrix and vector size mismatch" | |
1120 | unless ($vector->[1] == $n); | |
1121 | ||
1122 | $norm_matrix = $matrix->new($n,$n); | |
1123 | $norm_vector = $vector->new($n,1); | |
1124 | ||
1125 | $norm_matrix->copy($matrix); | |
1126 | $norm_vector->copy($vector); | |
1127 | ||
1128 | $norm_matrix->_undo_LR(); | |
1129 | ||
1130 | for ( $i = 0; $i < $n; $i++ ) | |
1131 | { | |
1132 | $max = abs($norm_vector->[0][$i][0]); | |
1133 | for ( $j = 0; $j < $n; $j++ ) | |
1134 | { | |
1135 | $val = abs($norm_matrix->[0][$i][$j]); | |
1136 | if ($val > $max) { $max = $val; } | |
1137 | } | |
1138 | if ($max != 0) | |
1139 | { | |
1140 | $norm_vector->[0][$i][0] /= $max; | |
1141 | for ( $j = 0; $j < $n; $j++ ) | |
1142 | { | |
1143 | $norm_matrix->[0][$i][$j] /= $max; | |
1144 | } | |
1145 | } | |
1146 | } | |
1147 | return($norm_matrix,$norm_vector); | |
1148 | } | |
1149 | ||
1150 | sub decompose_LR | |
1151 | { | |
1152 | croak "Usage: \$LR_matrix = \$matrix->decompose_LR();" | |
1153 | if (@_ != 1); | |
1154 | ||
1155 | my($matrix) = @_; | |
1156 | my($rows,$cols) = ($matrix->[1],$matrix->[2]); | |
1157 | my($perm_row,$perm_col); | |
1158 | my($row,$col,$max); | |
1159 | my($i,$j,$k,$n); | |
1160 | my($sign) = 1; | |
1161 | my($swap); | |
1162 | my($temp); | |
1163 | ||
1164 | croak "Math::MatrixReal::decompose_LR(): matrix is not quadratic" | |
1165 | unless ($rows == $cols); | |
1166 | ||
1167 | $temp = $matrix->new($rows,$cols); | |
1168 | $temp->copy($matrix); | |
1169 | $n = $rows; | |
1170 | $perm_row = [ ]; | |
1171 | $perm_col = [ ]; | |
1172 | for ( $i = 0; $i < $n; $i++ ) | |
1173 | { | |
1174 | $perm_row->[$i] = $i; | |
1175 | $perm_col->[$i] = $i; | |
1176 | } | |
1177 | NONZERO: | |
1178 | for ( $k = 0; $k < $n; $k++ ) # use Gauss's algorithm: | |
1179 | { | |
1180 | # complete pivot-search: | |
1181 | ||
1182 | $max = 0; | |
1183 | for ( $i = $k; $i < $n; $i++ ) | |
1184 | { | |
1185 | for ( $j = $k; $j < $n; $j++ ) | |
1186 | { | |
1187 | if (($swap = abs($temp->[0][$i][$j])) > $max) | |
1188 | { | |
1189 | $max = $swap; | |
1190 | $row = $i; | |
1191 | $col = $j; | |
1192 | } | |
1193 | } | |
1194 | } | |
1195 | last NONZERO if ($max == 0); # (all remaining elements are zero) | |
1196 | if ($k != $row) # swap row $k and row $row: | |
1197 | { | |
1198 | $sign = -$sign; | |
1199 | $swap = $perm_row->[$k]; | |
1200 | $perm_row->[$k] = $perm_row->[$row]; | |
1201 | $perm_row->[$row] = $swap; | |
1202 | for ( $j = 0; $j < $n; $j++ ) | |
1203 | { | |
1204 | # (must run from 0 since L has to be swapped too!) | |
1205 | ||
1206 | $swap = $temp->[0][$k][$j]; | |
1207 | $temp->[0][$k][$j] = $temp->[0][$row][$j]; | |
1208 | $temp->[0][$row][$j] = $swap; | |
1209 | } | |
1210 | } | |
1211 | if ($k != $col) # swap column $k and column $col: | |
1212 | { | |
1213 | $sign = -$sign; | |
1214 | $swap = $perm_col->[$k]; | |
1215 | $perm_col->[$k] = $perm_col->[$col]; | |
1216 | $perm_col->[$col] = $swap; | |
1217 | for ( $i = 0; $i < $n; $i++ ) | |
1218 | { | |
1219 | $swap = $temp->[0][$i][$k]; | |
1220 | $temp->[0][$i][$k] = $temp->[0][$i][$col]; | |
1221 | $temp->[0][$i][$col] = $swap; | |
1222 | } | |
1223 | } | |
1224 | for ( $i = ($k + 1); $i < $n; $i++ ) | |
1225 | { | |
1226 | # scan the remaining rows, add multiples of row $k to row $i: | |
1227 | ||
1228 | $swap = $temp->[0][$i][$k] / $temp->[0][$k][$k]; | |
1229 | if ($swap != 0) | |
1230 | { | |
1231 | # calculate a row of matrix R: | |
1232 | ||
1233 | for ( $j = ($k + 1); $j < $n; $j++ ) | |
1234 | { | |
1235 | $temp->[0][$i][$j] -= $temp->[0][$k][$j] * $swap; | |
1236 | } | |
1237 | ||
1238 | # store matrix L in same matrix as R: | |
1239 | ||
1240 | $temp->[0][$i][$k] = $swap; | |
1241 | } | |
1242 | } | |
1243 | } | |
1244 | $temp->[3] = $sign; | |
1245 | $temp->[4] = $perm_row; | |
1246 | $temp->[5] = $perm_col; | |
1247 | return($temp); | |
1248 | } | |
1249 | ||
1250 | sub solve_LR | |
1251 | { | |
1252 | croak "Usage: (\$dimension,\$x_vector,\$base_matrix) = \$LR_matrix->solve_LR(\$b_vector);" | |
1253 | if (@_ != 2); | |
1254 | ||
1255 | my($LR_matrix,$b_vector) = @_; | |
1256 | my($rows,$cols) = ($LR_matrix->[1],$LR_matrix->[2]); | |
1257 | my($dimension,$x_vector,$base_matrix); | |
1258 | my($perm_row,$perm_col); | |
1259 | my($y_vector,$sum); | |
1260 | my($i,$j,$k,$n); | |
1261 | ||
1262 | croak "Math::MatrixReal::solve_LR(): not an LR decomposition matrix" | |
1263 | unless ((defined $LR_matrix->[3]) && ($rows == $cols)); | |
1264 | ||
1265 | $n = $rows; | |
1266 | ||
1267 | croak "Math::MatrixReal::solve_LR(): vector is not a column vector" | |
1268 | unless ($b_vector->[2] == 1); | |
1269 | ||
1270 | croak "Math::MatrixReal::solve_LR(): matrix and vector size mismatch" | |
1271 | unless ($b_vector->[1] == $n); | |
1272 | ||
1273 | $perm_row = $LR_matrix->[4]; | |
1274 | $perm_col = $LR_matrix->[5]; | |
1275 | ||
1276 | $x_vector = $b_vector->new($n,1); | |
1277 | $y_vector = $b_vector->new($n,1); | |
1278 | $base_matrix = $LR_matrix->new($n,$n); | |
1279 | ||
1280 | # calculate "x" so that LRx = b ==> calculate Ly = b, Rx = y: | |
1281 | ||
1282 | for ( $i = 0; $i < $n; $i++ ) # calculate $y_vector: | |
1283 | { | |
1284 | $sum = $b_vector->[0][($perm_row->[$i])][0]; | |
1285 | for ( $j = 0; $j < $i; $j++ ) | |
1286 | { | |
1287 | $sum -= $LR_matrix->[0][$i][$j] * $y_vector->[0][$j][0]; | |
1288 | } | |
1289 | $y_vector->[0][$i][0] = $sum; | |
1290 | } | |
1291 | ||
1292 | $dimension = 0; | |
1293 | for ( $i = ($n - 1); $i >= 0; $i-- ) # calculate $x_vector: | |
1294 | { | |
1295 | if ($LR_matrix->[0][$i][$i] == 0) | |
1296 | { | |
1297 | if ($y_vector->[0][$i][0] != 0) | |
1298 | { | |
1299 | return(); # a solution does not exist! | |
1300 | } | |
1301 | else | |
1302 | { | |
1303 | $dimension++; | |
1304 | $x_vector->[0][($perm_col->[$i])][0] = 0; | |
1305 | } | |
1306 | } | |
1307 | else | |
1308 | { | |
1309 | $sum = $y_vector->[0][$i][0]; | |
1310 | for ( $j = ($i + 1); $j < $n; $j++ ) | |
1311 | { | |
1312 | $sum -= $LR_matrix->[0][$i][$j] * | |
1313 | $x_vector->[0][($perm_col->[$j])][0]; | |
1314 | } | |
1315 | $x_vector->[0][($perm_col->[$i])][0] = | |
1316 | $sum / $LR_matrix->[0][$i][$i]; | |
1317 | } | |
1318 | } | |
1319 | if ($dimension) | |
1320 | { | |
1321 | if ($dimension == $n) | |
1322 | { | |
1323 | $base_matrix->one(); | |
1324 | } | |
1325 | else | |
1326 | { | |
1327 | for ( $k = 0; $k < $dimension; $k++ ) | |
1328 | { | |
1329 | $base_matrix->[0][($perm_col->[($n-$k-1)])][$k] = 1; | |
1330 | for ( $i = ($n-$dimension-1); $i >= 0; $i-- ) | |
1331 | { | |
1332 | $sum = 0; | |
1333 | for ( $j = ($i + 1); $j < $n; $j++ ) | |
1334 | { | |
1335 | $sum -= $LR_matrix->[0][$i][$j] * | |
1336 | $base_matrix->[0][($perm_col->[$j])][$k]; | |
1337 | } | |
1338 | $base_matrix->[0][($perm_col->[$i])][$k] = | |
1339 | $sum / $LR_matrix->[0][$i][$i]; | |
1340 | } | |
1341 | } | |
1342 | } | |
1343 | } | |
1344 | return( $dimension, $x_vector, $base_matrix ); | |
1345 | } | |
1346 | ||
1347 | sub invert_LR | |
1348 | { | |
1349 | croak "Usage: \$inverse_matrix = \$LR_matrix->invert_LR();" | |
1350 | if (@_ != 1); | |
1351 | ||
1352 | my($matrix) = @_; | |
1353 | my($rows,$cols) = ($matrix->[1],$matrix->[2]); | |
1354 | my($inv_matrix,$x_vector,$y_vector); | |
1355 | my($i,$j,$n); | |
1356 | ||
1357 | croak "Math::MatrixReal::invert_LR(): not an LR decomposition matrix" | |
1358 | unless ((defined $matrix->[3]) && ($rows == $cols)); | |
1359 | ||
1360 | $n = $rows; | |
1361 | if ($matrix->[0][$n-1][$n-1] != 0) | |
1362 | { | |
1363 | $inv_matrix = $matrix->new($n,$n); | |
1364 | $y_vector = $matrix->new($n,1); | |
1365 | for ( $j = 0; $j < $n; $j++ ) | |
1366 | { | |
1367 | if ($j > 0) | |
1368 | { | |
1369 | $y_vector->[0][$j-1][0] = 0; | |
1370 | } | |
1371 | $y_vector->[0][$j][0] = 1; | |
1372 | if (($rows,$x_vector,$cols) = $matrix->solve_LR($y_vector)) | |
1373 | { | |
1374 | for ( $i = 0; $i < $n; $i++ ) | |
1375 | { | |
1376 | $inv_matrix->[0][$i][$j] = $x_vector->[0][$i][0]; | |
1377 | } | |
1378 | } | |
1379 | else | |
1380 | { | |
1381 | die "Math::MatrixReal::invert_LR(): unexpected error - please inform author!\n"; | |
1382 | } | |
1383 | } | |
1384 | return($inv_matrix); | |
1385 | } | |
1386 | else { return(); } # matrix is not invertible! | |
1387 | } | |
1388 | ||
1389 | sub condition | |
1390 | { | |
1391 | # 1st matrix MUST be the inverse of 2nd matrix (or vice-versa) | |
1392 | # for a meaningful result! | |
1393 | ||
1394 | # make this work when given no args | |
1395 | ||
1396 | croak "Usage: \$condition = \$matrix->condition(\$inverse_matrix);" | |
1397 | if (@_ != 2); | |
1398 | ||
1399 | my($matrix1,$matrix2) = @_; | |
1400 | my($rows1,$cols1) = ($matrix1->[1],$matrix1->[2]); | |
1401 | my($rows2,$cols2) = ($matrix2->[1],$matrix2->[2]); | |
1402 | ||
1403 | croak "Math::MatrixReal::condition(): 1st matrix is not quadratic" | |
1404 | unless ($rows1 == $cols1); | |
1405 | ||
1406 | croak "Math::MatrixReal::condition(): 2nd matrix is not quadratic" | |
1407 | unless ($rows2 == $cols2); | |
1408 | ||
1409 | croak "Math::MatrixReal::condition(): matrix size mismatch" | |
1410 | unless (($rows1 == $rows2) && ($cols1 == $cols2)); | |
1411 | ||
1412 | return( $matrix1->norm_one() * $matrix2->norm_one() ); | |
1413 | } | |
1414 | ||
1415 | ## easy to use determinant | |
1416 | ## very fast if matrix is diagonal or triangular | |
1417 | ||
1418 | sub det { | |
1419 | croak "Usage: \$determinant = \$matrix->det_LR();" unless (@_ == 1); | |
1420 | my ($matrix) = @_; | |
1421 | my ($rows,$cols) = $matrix->dim(); | |
1422 | my $det = 1; | |
1423 | ||
1424 | croak "Math::MatrixReal::det(): Matrix is not quadratic" | |
1425 | unless ($rows == $cols); | |
1426 | ||
1427 | # diagonal will match too | |
1428 | if( $matrix->is_upper_triangular() ){ | |
1429 | $matrix->each_diag( sub { $det*=shift; } ); | |
1430 | } elsif ( $matrix->is_lower_triangular() ){ | |
1431 | $matrix->each_diag( sub { $det*=shift; } ); | |
1432 | } else { | |
1433 | return $matrix->decompose_LR->det_LR(); | |
1434 | } | |
1435 | return $det; | |
1436 | } | |
1437 | ||
1438 | sub det_LR # determinant of LR decomposition matrix | |
1439 | { | |
1440 | croak "Usage: \$determinant = \$LR_matrix->det_LR();" | |
1441 | if (@_ != 1); | |
1442 | ||
1443 | my($matrix) = @_; | |
1444 | my($rows,$cols) = ($matrix->[1],$matrix->[2]); | |
1445 | my($k,$det); | |
1446 | ||
1447 | croak "Math::MatrixReal::det_LR(): not an LR decomposition matrix" | |
1448 | unless ((defined $matrix->[3]) && ($rows == $cols)); | |
1449 | ||
1450 | $det = $matrix->[3]; | |
1451 | for ( $k = 0; $k < $rows; $k++ ) | |
1452 | { | |
1453 | $det *= $matrix->[0][$k][$k]; | |
1454 | } | |
1455 | return($det); | |
1456 | } | |
1457 | ||
1458 | sub rank_LR { | |
1459 | return (shift)->order_LR; | |
1460 | } | |
1461 | ||
1462 | sub order_LR # order of LR decomposition matrix (number of non-zero equations) | |
1463 | { | |
1464 | croak "Usage: \$order = \$LR_matrix->order_LR();" | |
1465 | if (@_ != 1); | |
1466 | ||
1467 | my($matrix) = @_; | |
1468 | my($rows,$cols) = ($matrix->[1],$matrix->[2]); | |
1469 | my($order); | |
1470 | ||
1471 | croak "Math::MatrixReal::order_LR(): not an LR decomposition matrix" | |
1472 | unless ((defined $matrix->[3]) && ($rows == $cols)); | |
1473 | ||
1474 | ZERO: | |
1475 | for ( $order = ($rows - 1); $order >= 0; $order-- ) | |
1476 | { | |
1477 | last ZERO if ($matrix->[0][$order][$order] != 0); | |
1478 | } | |
1479 | return(++$order); | |
1480 | } | |
1481 | ||
1482 | sub scalar_product | |
1483 | { | |
1484 | croak "Usage: \$scalar_product = \$vector1->scalar_product(\$vector2);" | |
1485 | if (@_ != 2); | |
1486 | ||
1487 | my($vector1,$vector2) = @_; | |
1488 | my($rows1,$cols1) = ($vector1->[1],$vector1->[2]); | |
1489 | my($rows2,$cols2) = ($vector2->[1],$vector2->[2]); | |
1490 | my($k,$sum); | |
1491 | ||
1492 | croak "Math::MatrixReal::scalar_product(): 1st vector is not a column vector" | |
1493 | unless ($cols1 == 1); | |
1494 | ||
1495 | croak "Math::MatrixReal::scalar_product(): 2nd vector is not a column vector" | |
1496 | unless ($cols2 == 1); | |
1497 | ||
1498 | croak "Math::MatrixReal::scalar_product(): vector size mismatch" | |
1499 | unless ($rows1 == $rows2); | |
1500 | ||
1501 | $sum = 0; | |
1502 | for ( $k = 0; $k < $rows1; $k++ ) | |
1503 | { | |
1504 | $sum += $vector1->[0][$k][0] * $vector2->[0][$k][0]; | |
1505 | } | |
1506 | return($sum); | |
1507 | } | |
1508 | ||
1509 | sub vector_product | |
1510 | { | |
1511 | croak "Usage: \$vector_product = \$vector1->vector_product(\$vector2);" | |
1512 | if (@_ != 2); | |
1513 | ||
1514 | my($vector1,$vector2) = @_; | |
1515 | my($rows1,$cols1) = ($vector1->[1],$vector1->[2]); | |
1516 | my($rows2,$cols2) = ($vector2->[1],$vector2->[2]); | |
1517 | my($temp); | |
1518 | my($n); | |
1519 | ||
1520 | croak "Math::MatrixReal::vector_product(): 1st vector is not a column vector" | |
1521 | unless ($cols1 == 1); | |
1522 | ||
1523 | croak "Math::MatrixReal::vector_product(): 2nd vector is not a column vector" | |
1524 | unless ($cols2 == 1); | |
1525 | ||
1526 | croak "Math::MatrixReal::vector_product(): vector size mismatch" | |
1527 | unless ($rows1 == $rows2); | |
1528 | ||
1529 | $n = $rows1; | |
1530 | ||
1531 | croak "Math::MatrixReal::vector_product(): only defined for 3 dimensions" | |
1532 | unless ($n == 3); | |
1533 | ||
1534 | $temp = $vector1->new($n,1); | |
1535 | $temp->[0][0][0] = $vector1->[0][1][0] * $vector2->[0][2][0] - | |
1536 | $vector1->[0][2][0] * $vector2->[0][1][0]; | |
1537 | $temp->[0][1][0] = $vector1->[0][2][0] * $vector2->[0][0][0] - | |
1538 | $vector1->[0][0][0] * $vector2->[0][2][0]; | |
1539 | $temp->[0][2][0] = $vector1->[0][0][0] * $vector2->[0][1][0] - | |
1540 | $vector1->[0][1][0] * $vector2->[0][0][0]; | |
1541 | return($temp); | |
1542 | } | |
1543 | ||
1544 | sub length | |
1545 | { | |
1546 | croak "Usage: \$length = \$vector->length();" | |
1547 | if (@_ != 1); | |
1548 | ||
1549 | my($vector) = @_; | |
1550 | my($rows,$cols) = ($vector->[1],$vector->[2]); | |
1551 | my($k,$comp,$sum); | |
1552 | ||
1553 | croak "Math::MatrixReal::length(): vector is not a column vector" | |
1554 | unless ($cols == 1); | |
1555 | ||
1556 | $sum = 0; | |
1557 | for ( $k = 0; $k < $rows; $k++ ) | |
1558 | { | |
1559 | $comp = $vector->[0][$k][0]; | |
1560 | $sum += $comp * $comp; | |
1561 | } | |
1562 | return( sqrt( $sum ) ); | |
1563 | } | |
1564 | ||
1565 | sub _init_iteration | |
1566 | { | |
1567 | croak "Usage: \$which_norm = \$matrix->_init_iteration();" | |
1568 | if (@_ != 1); | |
1569 | ||
1570 | my($matrix) = @_; | |
1571 | my($rows,$cols) = ($matrix->[1],$matrix->[2]); | |
1572 | my($ok,$max,$sum,$norm); | |
1573 | my($i,$j,$n); | |
1574 | ||
1575 | croak "Math::MatrixReal::_init_iteration(): matrix is not quadratic" | |
1576 | unless ($rows == $cols); | |
1577 | ||
1578 | $ok = 1; | |
1579 | $n = $rows; | |
1580 | for ( $i = 0; $i < $n; $i++ ) | |
1581 | { | |
1582 | if ($matrix->[0][$i][$i] == 0) { $ok = 0; } | |
1583 | } | |
1584 | if ($ok) | |
1585 | { | |
1586 | $norm = 1; # norm_one | |
1587 | $max = 0; | |
1588 | for ( $j = 0; $j < $n; $j++ ) | |
1589 | { | |
1590 | $sum = 0; | |
1591 | for ( $i = 0; $i < $j; $i++ ) | |
1592 | { | |
1593 | $sum += abs($matrix->[0][$i][$j]); | |
1594 | } | |
1595 | for ( $i = ($j + 1); $i < $n; $i++ ) | |
1596 | { | |
1597 | $sum += abs($matrix->[0][$i][$j]); | |
1598 | } | |
1599 | $sum /= abs($matrix->[0][$j][$j]); | |
1600 | if ($sum > $max) { $max = $sum; } | |
1601 | } | |
1602 | $ok = ($max < 1); | |
1603 | unless ($ok) | |
1604 | { | |
1605 | $norm = -1; # norm_max | |
1606 | $max = 0; | |
1607 | for ( $i = 0; $i < $n; $i++ ) | |
1608 | { | |
1609 | $sum = 0; | |
1610 | for ( $j = 0; $j < $i; $j++ ) | |
1611 | { | |
1612 | $sum += abs($matrix->[0][$i][$j]); | |
1613 | } | |
1614 | for ( $j = ($i + 1); $j < $n; $j++ ) | |
1615 | { | |
1616 | $sum += abs($matrix->[0][$i][$j]); | |
1617 | } | |
1618 | $sum /= abs($matrix->[0][$i][$i]); | |
1619 | if ($sum > $max) { $max = $sum; } | |
1620 | } | |
1621 | $ok = ($max < 1) | |
1622 | } | |
1623 | } | |
1624 | if ($ok) { return($norm); } | |
1625 | else { return(0); } | |
1626 | } | |
1627 | ||
1628 | sub solve_GSM # Global Step Method | |
1629 | { | |
1630 | croak "Usage: \$xn_vector = \$matrix->solve_GSM(\$x0_vector,\$b_vector,\$epsilon);" | |
1631 | if (@_ != 4); | |
1632 | ||
1633 | my($matrix,$x0_vector,$b_vector,$epsilon) = @_; | |
1634 | my($rows1,$cols1) = ( $matrix->[1], $matrix->[2]); | |
1635 | my($rows2,$cols2) = ($x0_vector->[1],$x0_vector->[2]); | |
1636 | my($rows3,$cols3) = ( $b_vector->[1], $b_vector->[2]); | |
1637 | my($norm,$sum,$diff); | |
1638 | my($xn_vector); | |
1639 | my($i,$j,$n); | |
1640 | ||
1641 | croak "Math::MatrixReal::solve_GSM(): matrix is not quadratic" | |
1642 | unless ($rows1 == $cols1); | |
1643 | ||
1644 | $n = $rows1; | |
1645 | ||
1646 | croak "Math::MatrixReal::solve_GSM(): 1st vector is not a column vector" | |
1647 | unless ($cols2 == 1); | |
1648 | ||
1649 | croak "Math::MatrixReal::solve_GSM(): 2nd vector is not a column vector" | |
1650 | unless ($cols3 == 1); | |
1651 | ||
1652 | croak "Math::MatrixReal::solve_GSM(): matrix and vector size mismatch" | |
1653 | unless (($rows2 == $n) && ($rows3 == $n)); | |
1654 | ||
1655 | return() unless ($norm = $matrix->_init_iteration()); | |
1656 | ||
1657 | $xn_vector = $x0_vector->new($n,1); | |
1658 | ||
1659 | $diff = $epsilon + 1; | |
1660 | while ($diff >= $epsilon) | |
1661 | { | |
1662 | for ( $i = 0; $i < $n; $i++ ) | |
1663 | { | |
1664 | $sum = $b_vector->[0][$i][0]; | |
1665 | for ( $j = 0; $j < $i; $j++ ) | |
1666 | { | |
1667 | $sum -= $matrix->[0][$i][$j] * $x0_vector->[0][$j][0]; | |
1668 | } | |
1669 | for ( $j = ($i + 1); $j < $n; $j++ ) | |
1670 | { | |
1671 | $sum -= $matrix->[0][$i][$j] * $x0_vector->[0][$j][0]; | |
1672 | } | |
1673 | $xn_vector->[0][$i][0] = $sum / $matrix->[0][$i][$i]; | |
1674 | } | |
1675 | $x0_vector->subtract($x0_vector,$xn_vector); | |
1676 | if ($norm > 0) { $diff = $x0_vector->norm_one(); } | |
1677 | else { $diff = $x0_vector->norm_max(); } | |
1678 | for ( $i = 0; $i < $n; $i++ ) | |
1679 | { | |
1680 | $x0_vector->[0][$i][0] = $xn_vector->[0][$i][0]; | |
1681 | } | |
1682 | } | |
1683 | return($xn_vector); | |
1684 | } | |
1685 | ||
1686 | sub solve_SSM # Single Step Method | |
1687 | { | |
1688 | croak "Usage: \$xn_vector = \$matrix->solve_SSM(\$x0_vector,\$b_vector,\$epsilon);" | |
1689 | if (@_ != 4); | |
1690 | ||
1691 | my($matrix,$x0_vector,$b_vector,$epsilon) = @_; | |
1692 | my($rows1,$cols1) = ( $matrix->[1], $matrix->[2]); | |
1693 | my($rows2,$cols2) = ($x0_vector->[1],$x0_vector->[2]); | |
1694 | my($rows3,$cols3) = ( $b_vector->[1], $b_vector->[2]); | |
1695 | my($norm,$sum,$diff); | |
1696 | my($xn_vector); | |
1697 | my($i,$j,$n); | |
1698 | ||
1699 | croak "Math::MatrixReal::solve_SSM(): matrix is not quadratic" | |
1700 | unless ($rows1 == $cols1); | |
1701 | ||
1702 | $n = $rows1; | |
1703 | ||
1704 | croak "Math::MatrixReal::solve_SSM(): 1st vector is not a column vector" | |
1705 | unless ($cols2 == 1); | |
1706 | ||
1707 | croak "Math::MatrixReal::solve_SSM(): 2nd vector is not a column vector" | |
1708 | unless ($cols3 == 1); | |
1709 | ||
1710 | croak "Math::MatrixReal::solve_SSM(): matrix and vector size mismatch" | |
1711 | unless (($rows2 == $n) && ($rows3 == $n)); | |
1712 | ||
1713 | return() unless ($norm = $matrix->_init_iteration()); | |
1714 | ||
1715 | $xn_vector = $x0_vector->new($n,1); | |
1716 | $xn_vector->copy($x0_vector); | |
1717 | ||
1718 | $diff = $epsilon + 1; | |
1719 | while ($diff >= $epsilon) | |
1720 | { | |
1721 | for ( $i = 0; $i < $n; $i++ ) | |
1722 | { | |
1723 | $sum = $b_vector->[0][$i][0]; | |
1724 | for ( $j = 0; $j < $i; $j++ ) | |
1725 | { | |
1726 | $sum -= $matrix->[0][$i][$j] * $xn_vector->[0][$j][0]; | |
1727 | } | |
1728 | for ( $j = ($i + 1); $j < $n; $j++ ) | |
1729 | { | |
1730 | $sum -= $matrix->[0][$i][$j] * $xn_vector->[0][$j][0]; | |
1731 | } | |
1732 | $xn_vector->[0][$i][0] = $sum / $matrix->[0][$i][$i]; | |
1733 | } | |
1734 | $x0_vector->subtract($x0_vector,$xn_vector); | |
1735 | if ($norm > 0) { $diff = $x0_vector->norm_one(); } | |
1736 | else { $diff = $x0_vector->norm_max(); } | |
1737 | for ( $i = 0; $i < $n; $i++ ) | |
1738 | { | |
1739 | $x0_vector->[0][$i][0] = $xn_vector->[0][$i][0]; | |
1740 | } | |
1741 | } | |
1742 | return($xn_vector); | |
1743 | } | |
1744 | ||
1745 | sub solve_RM # Relaxation Method | |
1746 | { | |
1747 | croak "Usage: \$xn_vector = \$matrix->solve_RM(\$x0_vector,\$b_vector,\$weight,\$epsilon);" | |
1748 | if (@_ != 5); | |
1749 | ||
1750 | my($matrix,$x0_vector,$b_vector,$weight,$epsilon) = @_; | |
1751 | my($rows1,$cols1) = ( $matrix->[1], $matrix->[2]); | |
1752 | my($rows2,$cols2) = ($x0_vector->[1],$x0_vector->[2]); | |
1753 | my($rows3,$cols3) = ( $b_vector->[1], $b_vector->[2]); | |
1754 | my($norm,$sum,$diff); | |
1755 | my($xn_vector); | |
1756 | my($i,$j,$n); | |
1757 | ||
1758 | croak "Math::MatrixReal::solve_RM(): matrix is not quadratic" | |
1759 | unless ($rows1 == $cols1); | |
1760 | ||
1761 | $n = $rows1; | |
1762 | ||
1763 | croak "Math::MatrixReal::solve_RM(): 1st vector is not a column vector" | |
1764 | unless ($cols2 == 1); | |
1765 | ||
1766 | croak "Math::MatrixReal::solve_RM(): 2nd vector is not a column vector" | |
1767 | unless ($cols3 == 1); | |
1768 | ||
1769 | croak "Math::MatrixReal::solve_RM(): matrix and vector size mismatch" | |
1770 | unless (($rows2 == $n) && ($rows3 == $n)); | |
1771 | ||
1772 | return() unless ($norm = $matrix->_init_iteration()); | |
1773 | ||
1774 | $xn_vector = $x0_vector->new($n,1); | |
1775 | $xn_vector->copy($x0_vector); | |
1776 | ||
1777 | $diff = $epsilon + 1; | |
1778 | while ($diff >= $epsilon) | |
1779 | { | |
1780 | for ( $i = 0; $i < $n; $i++ ) | |
1781 | { | |
1782 | $sum = $b_vector->[0][$i][0]; | |
1783 | for ( $j = 0; $j < $i; $j++ ) | |
1784 | { | |
1785 | $sum -= $matrix->[0][$i][$j] * $xn_vector->[0][$j][0]; | |
1786 | } | |
1787 | for ( $j = ($i + 1); $j < $n; $j++ ) | |
1788 | { | |
1789 | $sum -= $matrix->[0][$i][$j] * $xn_vector->[0][$j][0]; | |
1790 | } | |
1791 | $xn_vector->[0][$i][0] = $weight * ( $sum / $matrix->[0][$i][$i] ) | |
1792 | + (1 - $weight) * $xn_vector->[0][$i][0]; | |
1793 | } | |
1794 | $x0_vector->subtract($x0_vector,$xn_vector); | |
1795 | if ($norm > 0) { $diff = $x0_vector->norm_one(); } | |
1796 | else { $diff = $x0_vector->norm_max(); } | |
1797 | for ( $i = 0; $i < $n; $i++ ) | |
1798 | { | |
1799 | $x0_vector->[0][$i][0] = $xn_vector->[0][$i][0]; | |
1800 | } | |
1801 | } | |
1802 | return($xn_vector); | |
1803 | } | |
1804 | ||
1805 | # Core householder reduction routine (when eigenvector | |
1806 | # are wanted). | |
1807 | # Adapted from: Numerical Recipes, 2nd edition. | |
1808 | sub _householder_vectors ($) | |
1809 | { | |
1810 | my ($Q) = @_; | |
1811 | my ($rows, $cols) = ($Q->[1], $Q->[2]); | |
1812 | ||
1813 | # Creates tridiagonal | |
1814 | # Set up tridiagonal needed elements | |
1815 | my $d = []; # N Diagonal elements 0...n-1 | |
1816 | my $e = []; # N-1 Off-Diagonal elements 0...n-2 | |
1817 | ||
1818 | my @p = (); | |
1819 | for (my $i = ($rows-1); $i > 1; $i--) | |
1820 | { | |
1821 | my $scale = 0.0; | |
1822 | # Computes norm of one column (below diagonal) | |
1823 | for (my $k = 0; $k < $i; $k++) | |
1824 | { | |
1825 | $scale += abs($Q->[0][$i][$k]); | |
1826 | } | |
1827 | if ($scale == 0.0) | |
1828 | { # skip the transformation | |
1829 | $e->[$i-1] = $Q->[0][$i][$i-1]; | |
1830 | } | |
1831 | else | |
1832 | { | |
1833 | my $h = 0.0; | |
1834 | for (my $k = 0; $k < $i; $k++) | |
1835 | { # Used scaled Q for transformation | |
1836 | $Q->[0][$i][$k] /= $scale; | |
1837 | # Form sigma in h | |
1838 | $h += $Q->[0][$i][$k] * $Q->[0][$i][$k]; | |
1839 | } | |
1840 | my $t1 = $Q->[0][$i][$i-1]; | |
1841 | my $t2 = (($t1 >= 0.0) ? -sqrt($h) : sqrt($h)); | |
1842 | $e->[$i-1] = $scale * $t2; # Update off-diagonals | |
1843 | $h -= $t1 * $t2; | |
1844 | $Q->[0][$i][$i-1] -= $t2; | |
1845 | my $f = 0.0; | |
1846 | for (my $j = 0; $j < $i; $j++) | |
1847 | { | |
1848 | $Q->[0][$j][$i] = $Q->[0][$i][$j] / $h; | |
1849 | my $g = 0.0; | |
1850 | for (my $k = 0; $k <= $j; $k++) | |
1851 | { | |
1852 | $g += $Q->[0][$j][$k] * $Q->[0][$i][$k]; | |
1853 | } | |
1854 | for (my $k = $j+1; $k < $i; $k++) | |
1855 | { | |
1856 | $g += $Q->[0][$k][$j] * $Q->[0][$i][$k]; | |
1857 | } | |
1858 | # Form elements of P | |
1859 | $p[$j] = $g / $h; | |
1860 | $f += $p[$j] * $Q->[0][$i][$j]; | |
1861 | } | |
1862 | my $hh = $f / ($h + $h); | |
1863 | for (my $j = 0; $j < $i; $j++) | |
1864 | { | |
1865 | my $t3 = $Q->[0][$i][$j]; | |
1866 | my $t4 = $p[$j] - $hh * $t3; | |
1867 | $p[$j] = $t4; | |
1868 | for (my $k = 0; $k <= $j; $k++) | |
1869 | { | |
1870 | $Q->[0][$j][$k] -= $t3 * $p[$k] | |
1871 | + $t4 * $Q->[0][$i][$k]; | |
1872 | } | |
1873 | } | |
1874 | } | |
1875 | } | |
1876 | # Updates for i == 0,1 | |
1877 | $e->[0] = $Q->[0][1][0]; | |
1878 | $d->[0] = $Q->[0][0][0]; # i==0 | |
1879 | $Q->[0][0][0] = 1.0; | |
1880 | $d->[1] = $Q->[0][1][1]; # i==1 | |
1881 | $Q->[0][1][1] = 1.0; | |
1882 | $Q->[0][1][0] = $Q->[0][0][1] = 0.0; | |
1883 | for (my $i = 2; $i < $rows; $i++) | |
1884 | { | |
1885 | for (my $j = 0; $j < $i; $j++) | |
1886 | { | |
1887 | my $g = 0.0; | |
1888 | for (my $k = 0; $k < $i; $k++) | |
1889 | { | |
1890 | $g += $Q->[0][$i][$k] * $Q->[0][$k][$j]; | |
1891 | } | |
1892 | for (my $k = 0; $k < $i; $k++) | |
1893 | { | |
1894 | $Q->[0][$k][$j] -= $g * $Q->[0][$k][$i]; | |
1895 | } | |
1896 | } | |
1897 | $d->[$i] = $Q->[0][$i][$i]; | |
1898 | # Reset row and column of Q for next iteration | |
1899 | $Q->[0][$i][$i] = 1.0; | |
1900 | for (my $j = 0; $j < $i; $j++) | |
1901 | { | |
1902 | $Q->[0][$i][$j] = $Q->[0][$j][$i] = 0.0; | |
1903 | } | |
1904 | } | |
1905 | return ($d, $e); | |
1906 | } | |
1907 | ||
1908 | # Computes sqrt(a*a + b*b), but more carefully... | |
1909 | sub _pythag ($$) | |
1910 | { | |
1911 | my ($a, $b) = @_; | |
1912 | my $aa = abs($a); | |
1913 | my $ab = abs($b); | |
1914 | if ($aa > $ab) | |
1915 | { | |
1916 | # NB: Not needed!: return 0.0 if ($aa == 0.0); | |
1917 | my $t = $ab / $aa; | |
1918 | return ($aa * sqrt(1.0 + $t*$t)); | |
1919 | } | |
1920 | else | |
1921 | { | |
1922 | return 0.0 if ($ab == 0.0); | |
1923 | my $t = $aa / $ab; | |
1924 | return ($ab * sqrt(1.0 + $t*$t)); | |
1925 | } | |
1926 | } | |
1927 | ||
1928 | # QL algorithm with implicit shifts to determine the eigenvalues | |
1929 | # of a tridiagonal matrix. Internal routine. | |
1930 | sub _tridiagonal_QLimplicit | |
1931 | { | |
1932 | my ($EV, $d, $e) = @_; | |
1933 | my ($rows, $cols) = ($EV->[1], $EV->[2]); | |
1934 | ||
1935 | $e->[$rows-1] = 0.0; | |
1936 | # Start real computation | |
1937 | for (my $l = 0; $l < $rows; $l++) | |
1938 | { | |
1939 | my $iter = 0; | |
1940 | my $m; | |
1941 | OUTER: | |
1942 | do { | |
1943 | for ($m = $l; $m < ($rows - 1); $m++) | |
1944 | { | |
1945 | my $dd = abs($d->[$m]) + abs($d->[$m+1]); | |
1946 | last if ((abs($e->[$m]) + $dd) == $dd); | |
1947 | } | |
1948 | if ($m != $l) | |
1949 | { | |
1950 | ## why only allow 30 iterations? | |
1951 | croak("Too many iterations!") if ($iter++ >= 30); | |
1952 | my $g = ($d->[$l+1] - $d->[$l]) | |
1953 | / (2.0 * $e->[$l]); | |
1954 | my $r = _pythag($g, 1.0); | |
1955 | $g = $d->[$m] - $d->[$l] | |
1956 | + $e->[$l] / ($g + (($g >= 0.0) ? abs($r) : -abs($r))); | |
1957 | my ($p,$s,$c) = (0.0, 1.0,1.0); | |
1958 | for (my $i = ($m-1); $i >= $l; $i--) | |
1959 | { | |
1960 | my $ii = $i + 1; | |
1961 | my $f = $s * $e->[$i]; | |
1962 | my $t = _pythag($f, $g); | |
1963 | $e->[$ii] = $t; | |
1964 | if ($t == 0.0) | |
1965 | { | |
1966 | $d->[$ii] -= $p; | |
1967 | $e->[$m] = 0.0; | |
1968 | next OUTER; | |
1969 | } | |
1970 | my $b = $c * $e->[$i]; | |
1971 | $s = $f / $t; | |
1972 | $c = $g / $t; | |
1973 | $g = $d->[$ii] - $p; | |
1974 | my $t2 = ($d->[$i] - $g) * $s + 2.0 * $c * $b; | |
1975 | $p = $s * $t2; | |
1976 | $d->[$ii] = $g + $p; | |
1977 | $g = $c * $t2 - $b; | |
1978 | for (my $k = 0; $k < $rows; $k++) | |
1979 | { | |
1980 | my $t1 = $EV->[0][$k][$ii]; | |
1981 | my $t2 = $EV->[0][$k][$i]; | |
1982 | $EV->[0][$k][$ii] = $s * $t2 + $c * $t1; | |
1983 | $EV->[0][$k][$i] = $c * $t2 - $s * $t1; | |
1984 | } | |
1985 | } | |
1986 | $d->[$l] -= $p; | |
1987 | $e->[$l] = $g; | |
1988 | $e->[$m] = 0.0; | |
1989 | } | |
1990 | } while ($m != $l); | |
1991 | } | |
1992 | return; | |
1993 | } | |
1994 | ||
1995 | # Core householder reduction routine (when eigenvector | |
1996 | # are NOT wanted). | |
1997 | sub _householder_values ($) | |
1998 | { | |
1999 | my ($Q) = @_; # NB: Q is destroyed on output... | |
2000 | my ($rows, $cols) = ($Q->[1], $Q->[2]); | |
2001 | ||
2002 | # Creates tridiagonal | |
2003 | # Set up tridiagonal needed elements | |
2004 | my $d = []; # N Diagonal elements 0...n-1 | |
2005 | my $e = []; # N-1 Off-Diagonal elements 0...n-2 | |
2006 | ||
2007 | my @p = (); | |
2008 | for (my $i = ($rows - 1); $i > 1; $i--) | |
2009 | { | |
2010 | my $scale = 0.0; | |
2011 | for (my $k = 0; $k < $i; $k++) | |
2012 | { | |
2013 | $scale += abs($Q->[0][$i][$k]); | |
2014 | } | |
2015 | if ($scale == 0.0) | |
2016 | { # skip the transformation | |
2017 | $e->[$i-1] = $Q->[0][$i][$i-1]; | |
2018 | } | |
2019 | else | |
2020 | { | |
2021 | my $h = 0.0; | |
2022 | for (my $k = 0; $k < $i; $k++) | |
2023 | { # Used scaled Q for transformation | |
2024 | $Q->[0][$i][$k] /= $scale; | |
2025 | # Form sigma in h | |
2026 | $h += $Q->[0][$i][$k] * $Q->[0][$i][$k]; | |
2027 | } | |
2028 | my $t = $Q->[0][$i][$i-1]; | |
2029 | my $t2 = (($t >= 0.0) ? -sqrt($h) : sqrt($h)); | |
2030 | $e->[$i-1] = $scale * $t2; # Updates off-diagonal | |
2031 | $h -= $t * $t2; | |
2032 | $Q->[0][$i][$i-1] -= $t2; | |
2033 | my $f = 0.0; | |
2034 | for (my $j = 0; $j < $i; $j++) | |
2035 | { | |
2036 | my $g = 0.0; | |
2037 | for (my $k = 0; $k <= $j; $k++) | |
2038 | { | |
2039 | $g += $Q->[0][$j][$k] * $Q->[0][$i][$k]; | |
2040 | } | |
2041 | for (my $k = $j+1; $k < $i; $k++) | |
2042 | { | |
2043 | $g += $Q->[0][$k][$j] * $Q->[0][$i][$k]; | |
2044 | } | |
2045 | # Form elements of P | |
2046 | $p[$j] = $g / $h; | |
2047 | $f += $p[$j] * $Q->[0][$i][$j]; | |
2048 | } | |
2049 | my $hh = $f / ($h + $h); | |
2050 | for (my $j = 0; $j < $i; $j++) | |
2051 | { | |
2052 | my $t = $Q->[0][$i][$j]; | |
2053 | my $g = $p[$j] - $hh * $t; | |
2054 | $p[$j] = $g; | |
2055 | for (my $k = 0; $k <= $j; $k++) | |
2056 | { | |
2057 | $Q->[0][$j][$k] -= $t * $p[$k] | |
2058 | + $g * $Q->[0][$i][$k]; | |
2059 | } | |
2060 | } | |
2061 | } | |
2062 | } | |
2063 | # Updates for i==1 | |
2064 | $e->[0] = $Q->[0][1][0]; | |
2065 | # Updates diagonal elements | |
2066 | for (my $i = 0; $i < $rows; $i++) | |
2067 | { | |
2068 | $d->[$i] = $Q->[0][$i][$i]; | |
2069 | } | |
2070 | return ($d, $e); | |
2071 | } | |
2072 | ||
2073 | # QL algorithm with implicit shifts to determine the | |
2074 | # eigenvalues ONLY. This is O(N^2) only... | |
2075 | sub _tridiagonal_QLimplicit_values | |
2076 | { | |
2077 | my ($M, $d, $e) = @_; # NB: M is not touched... | |
2078 | my ($rows, $cols) = ($M->[1], $M->[2]); | |
2079 | ||
2080 | $e->[$rows-1] = 0.0; | |
2081 | # Start real computation | |
2082 | for (my $l = 0; $l < $rows; $l++) | |
2083 | { | |
2084 | my $iter = 0; | |
2085 | my $m; | |
2086 | OUTER: | |
2087 | do { | |
2088 | for ($m = $l; $m < ($rows - 1); $m++) | |
2089 | { | |
2090 | my $dd = abs($d->[$m]) + abs($d->[$m+1]); | |
2091 | last if ((abs($e->[$m]) + $dd) == $dd); | |
2092 | } | |
2093 | if ($m != $l) | |
2094 | { | |
2095 | croak("Too many iterations!") if ($iter++ >= 30); | |
2096 | my $g = ($d->[$l+1] - $d->[$l]) | |
2097 | / (2.0 * $e->[$l]); | |
2098 | my $r = _pythag($g, 1.0); | |
2099 | $g = $d->[$m] - $d->[$l] | |
2100 | + $e->[$l] / ($g + (($g >= 0.0) ? abs($r) : -abs($r))); | |
2101 | my ($p,$s,$c) = (0.0, 1.0,1.0); | |
2102 | for (my $i = ($m-1); $i >= $l; $i--) | |
2103 | { | |
2104 | my $ii = $i + 1; | |
2105 | my $f = $s * $e->[$i]; | |
2106 | my $t = _pythag($f, $g); | |
2107 | $e->[$ii] = $t; | |
2108 | if ($t == 0.0) | |
2109 | { | |
2110 | $d->[$ii] -= $p; | |
2111 | $e->[$m] = 0.0; | |
2112 | next OUTER; | |
2113 | } | |
2114 | my $b = $c * $e->[$i]; | |
2115 | $s = $f / $t; | |
2116 | $c = $g / $t; | |
2117 | $g = $d->[$ii] - $p; | |
2118 | my $t2 = ($d->[$i] - $g) * $s + 2.0 * $c * $b; | |
2119 | $p = $s * $t2; | |
2120 | $d->[$ii] = $g + $p; | |
2121 | $g = $c * $t2 - $b; | |
2122 | } | |
2123 | $d->[$l] -= $p; | |
2124 | $e->[$l] = $g; | |
2125 | $e->[$m] = 0.0; | |
2126 | } | |
2127 | } while ($m != $l); | |
2128 | } | |
2129 | return; | |
2130 | } | |
2131 | ||
2132 | # Householder reduction of a real, symmetric matrix A. | |
2133 | # Returns a tridiagonal matrix T and the orthogonal matrix | |
2134 | # Q effecting the transformation between A and T. | |
2135 | sub householder ($) | |
2136 | { | |
2137 | my ($A) = @_; | |
2138 | my ($rows, $cols) = ($A->[1], $A->[2]); | |
2139 | ||
2140 | croak "Matrix is not quadratic" | |
2141 | unless ($rows = $cols); | |
2142 | croak "Matrix is not symmetric" | |
2143 | unless ($A->is_symmetric()); | |
2144 | ||
2145 | # Copy given matrix TODO: study if we should do in-place modification | |
2146 | my $Q = $A->clone(); | |
2147 | ||
2148 | # Do the computation of tridiagonal elements and of | |
2149 | # transformation matrix | |
2150 | my ($diag, $offdiag) = $Q->_householder_vectors(); | |
2151 | ||
2152 | # Creates the tridiagonal matrix | |
2153 | my $T = $A->shadow(); | |
2154 | for (my $i = 0; $i < $rows; $i++) | |
2155 | { # Set diagonal | |
2156 | $T->[0][$i][$i] = $diag->[$i]; | |
2157 | } | |
2158 | for (my $i = 0; $i < ($rows-1); $i++) | |
2159 | { # Set off diagonals | |
2160 | $T->[0][$i+1][$i] = $offdiag->[$i]; | |
2161 | $T->[0][$i][$i+1] = $offdiag->[$i]; | |
2162 | } | |
2163 | return ($T, $Q); | |
2164 | } | |
2165 | ||
2166 | # QL algorithm with implicit shifts to determine the eigenvalues | |
2167 | # and eigenvectors of a real tridiagonal matrix - or of a matrix | |
2168 | # previously reduced to tridiagonal form. | |
2169 | sub tri_diagonalize ($;$) | |
2170 | { | |
2171 | my ($T,$Q) = @_; # Q may be 0 if the original matrix is really tridiagonal | |
2172 | ||
2173 | my ($rows, $cols) = ($T->[1], $T->[2]); | |
2174 | ||
2175 | croak "Matrix is not quadratic" | |
2176 | unless ($rows = $cols); | |
2177 | croak "Matrix is not tridiagonal" | |
2178 | unless ($T->is_tridiagonal()); # DONE | |
2179 | ||
2180 | my $EV; | |
2181 | # Obtain/Creates the todo eigenvectors matrix | |
2182 | if ($Q) | |
2183 | { | |
2184 | $EV = $Q->clone(); | |
2185 | } | |
2186 | else | |
2187 | { | |
2188 | $EV = $T->shadow(); | |
2189 | $EV->one(); | |
2190 | } | |
2191 | # Allocates diagonal vector | |
2192 | my $diag = [ ]; | |
2193 | # Initializes it with T | |
2194 | for (my $i = 0; $i < $rows; $i++) | |
2195 | { | |
2196 | $diag->[$i] = $T->[0][$i][$i]; | |
2197 | } | |
2198 | # Allocate temporary vector for off-diagonal elements | |
2199 | my $offdiag = [ ]; | |
2200 | for (my $i = 1; $i < $rows; $i++) | |
2201 | { | |
2202 | $offdiag->[$i-1] = $T->[0][$i][$i-1]; | |
2203 | } | |
2204 | ||
2205 | # Calls the calculus routine | |
2206 | $EV->_tridiagonal_QLimplicit($diag, $offdiag); | |
2207 | ||
2208 | # Allocate eigenvalues vector | |
2209 | my $v = Math::MatrixReal->new($rows,1); | |
2210 | # Fills it | |
2211 | for (my $i = 0; $i < $rows; $i++) | |
2212 | { | |
2213 | $v->[0][$i][0] = $diag->[$i]; | |
2214 | } | |
2215 | return ($v, $EV); | |
2216 | } | |
2217 | ||
2218 | # Main routine for diagonalization of a real symmetric | |
2219 | # matrix M. Operates by transforming M into a tridiagonal | |
2220 | # matrix and then obtaining the eigenvalues and eigenvectors | |
2221 | # for that matrix (taking into account the transformation to | |
2222 | # tridiagonal). | |
2223 | sub sym_diagonalize ($) | |
2224 | { | |
2225 | my ($M) = @_; | |
2226 | my ($rows, $cols) = ($M->[1], $M->[2]); | |
2227 | ||
2228 | croak "Matrix is not quadratic" | |
2229 | unless ($rows = $cols); | |
2230 | croak "Matrix is not symmetric" | |
2231 | unless ($M->is_symmetric()); | |
2232 | ||
2233 | # Copy initial matrix | |
2234 | # TODO: study if we should allow in-place modification | |
2235 | my $VEC = $M->clone(); | |
2236 | ||
2237 | # Do the computation of tridiagonal elements and of | |
2238 | # transformation matrix | |
2239 | my ($diag, $offdiag) = $VEC->_householder_vectors(); | |
2240 | # Calls the calculus routine for diagonalization | |
2241 | $VEC->_tridiagonal_QLimplicit($diag, $offdiag); | |
2242 | ||
2243 | # Allocate eigenvalues vector | |
2244 | my $val = Math::MatrixReal->new($rows,1); | |
2245 | # Fills it | |
2246 | for (my $i = 0; $i < $rows; $i++) | |
2247 | { | |
2248 | $val->[0][$i][0] = $diag->[$i]; | |
2249 | } | |
2250 | return ($val, $VEC); | |
2251 | } | |
2252 | ||
2253 | # Householder reduction of a real, symmetric matrix A. | |
2254 | # Returns a tridiagonal matrix T equivalent to A. | |
2255 | sub householder_tridiagonal ($) | |
2256 | { | |
2257 | my ($A) = @_; | |
2258 | my ($rows, $cols) = ($A->[1], $A->[2]); | |
2259 | ||
2260 | croak "Matrix is not quadratic" | |
2261 | unless ($rows = $cols); | |
2262 | croak "Matrix is not symmetric" | |
2263 | unless ($A->is_symmetric()); | |
2264 | ||
2265 | # Copy given matrix | |
2266 | my $Q = $A->clone(); | |
2267 | ||
2268 | # Do the computation of tridiagonal elements and of | |
2269 | # transformation matrix | |
2270 | # Q is destroyed after reduction | |
2271 | my ($diag, $offdiag) = $Q->_householder_values(); | |
2272 | ||
2273 | # Creates the tridiagonal matrix in Q (avoid allocation) | |
2274 | my $T = $Q; | |
2275 | $T->zero(); | |
2276 | for (my $i = 0; $i < $rows; $i++) | |
2277 | { # Set diagonal | |
2278 | $T->[0][$i][$i] = $diag->[$i]; | |
2279 | } | |
2280 | for (my $i = 0; $i < ($rows-1); $i++) | |
2281 | { # Set off diagonals | |
2282 | $T->[0][$i+1][$i] = $offdiag->[$i]; | |
2283 | $T->[0][$i][$i+1] = $offdiag->[$i]; | |
2284 | } | |
2285 | return $T; | |
2286 | } | |
2287 | ||
2288 | # QL algorithm with implicit shifts to determine ONLY | |
2289 | # the eigenvalues a real tridiagonal matrix - or of a | |
2290 | # matrix previously reduced to tridiagonal form. | |
2291 | sub tri_eigenvalues ($;$) | |
2292 | { | |
2293 | my ($T) = @_; | |
2294 | my ($rows, $cols) = ($T->[1], $T->[2]); | |
2295 | ||
2296 | croak "Matrix is not quadratic" | |
2297 | unless ($rows = $cols); | |
2298 | croak "Matrix is not tridiagonal" | |
2299 | unless ($T->is_tridiagonal() ); # DONE | |
2300 | ||
2301 | # Allocates diagonal vector | |
2302 | my $diag = [ ]; | |
2303 | # Initializes it with T | |
2304 | for (my $i = 0; $i < $rows; $i++) | |
2305 | { | |
2306 | $diag->[$i] = $T->[0][$i][$i]; | |
2307 | } | |
2308 | # Allocate temporary vector for off-diagonal elements | |
2309 | my $offdiag = [ ]; | |
2310 | for (my $i = 1; $i < $rows; $i++) | |
2311 | { | |
2312 | $offdiag->[$i-1] = $T->[0][$i][$i-1]; | |
2313 | } | |
2314 | ||
2315 | # Calls the calculus routine (T is not touched) | |
2316 | $T->_tridiagonal_QLimplicit_values($diag, $offdiag); | |
2317 | ||
2318 | # Allocate eigenvalues vector | |
2319 | my $v = Math::MatrixReal->new($rows,1); | |
2320 | # Fills it | |
2321 | for (my $i = 0; $i < $rows; $i++) | |
2322 | { | |
2323 | $v->[0][$i][0] = $diag->[$i]; | |
2324 | } | |
2325 | return $v; | |
2326 | } | |
2327 | ||
2328 | ## more general routine than sym_eigenvalues | |
2329 | sub eigenvalues ($){ | |
2330 | my ($matrix) = @_; | |
2331 | my ($rows,$cols) = $matrix->dim(); | |
2332 | my $i=0; | |
2333 | ||
2334 | #return sym_eigenvalues($matrix) if $matrix->is_symmetric(); | |
2335 | ||
2336 | croak "Matrix is not quadratic" unless ($rows == $cols); | |
2337 | ||
2338 | if($matrix->is_upper_triangular() || $matrix->is_lower_triangular() ){ | |
2339 | my $l = Math::MatrixReal->new($rows,1); | |
2340 | for(;$i < $rows; $i++ ){ | |
2341 | $l->[0][$i][0] = $matrix->[0][$i][$i]; | |
2342 | } | |
2343 | return $l; | |
2344 | } | |
2345 | ||
2346 | return sym_eigenvalues($matrix) if $matrix->is_symmetric(); | |
2347 | ||
2348 | carp "Math::MatrixReal::eigenvalues(): Matrix is not symmetric or triangular"; | |
2349 | return undef; | |
2350 | ||
2351 | } | |
2352 | # Main routine for diagonalization of a real symmetric | |
2353 | # matrix M. Operates by transforming M into a tridiagonal | |
2354 | # matrix and then obtaining the eigenvalues and eigenvectors | |
2355 | # for that matrix (taking into account the transformation to | |
2356 | # tridiagonal). | |
2357 | sub sym_eigenvalues ($) | |
2358 | { | |
2359 | my ($M) = @_; | |
2360 | my ($rows, $cols) = ($M->[1], $M->[2]); | |
2361 | ||
2362 | croak "Matrix is not quadratic" | |
2363 | unless ($rows == $cols); | |
2364 | croak "Matrix is not symmetric" | |
2365 | unless ($M->is_symmetric()); | |
2366 | ||
2367 | # Copy matrix in temporary | |
2368 | my $A = $M->clone(); | |
2369 | # Do the computation of tridiagonal elements and of | |
2370 | # transformation matrix. A is destroyed | |
2371 | my ($diag, $offdiag) = $A->_householder_values(); | |
2372 | # Calls the calculus routine for diagonalization | |
2373 | # (M is not touched) | |
2374 | $M->_tridiagonal_QLimplicit_values($diag, $offdiag); | |
2375 | ||
2376 | # Allocate eigenvalues vector | |
2377 | my $val = Math::MatrixReal->new($rows,1); | |
2378 | # Fills it | |
2379 | for (my $i = 0; $i < $rows; $i++) | |
2380 | { | |
2381 | $val->[0][$i][0] = $diag->[$i]; | |
2382 | } | |
2383 | return $val; | |
2384 | } | |
2385 | #TODO: docs+test | |
2386 | sub is_positive_definite { | |
2387 | my ($matrix) = @_; | |
2388 | my ($r,$c) = $matrix->dim; | |
2389 | my $ev = $matrix->eigenvalues; | |
2390 | my $pos = 1; | |
2391 | $ev->each(sub { my $x = shift; if ($x <= 0){ $pos=0;return; } } ); | |
2392 | return $pos; | |
2393 | } | |
2394 | ||
2395 | sub is_row_vector { | |
2396 | my ($m) = @_; | |
2397 | my ($r,$c) = $m->dim; | |
2398 | return 1 if ($r == 1); | |
2399 | } | |
2400 | sub is_col_vector { | |
2401 | my ($m) = @_; | |
2402 | my ($r,$c) = $m->dim; | |
2403 | return 1 if ($c == 1); | |
2404 | } | |
2405 | ||
2406 | sub is_orthogonal($) { | |
2407 | my ($matrix) = @_; | |
2408 | ##croak "Math::MatrixReal::is_orthogonal(): Matrix is not quadratic" | |
2409 | ||
2410 | return 0 unless( $matrix->is_quadratic() ); | |
2411 | ||
2412 | my $one = $matrix->shadow(); | |
2413 | $one->one; | |
2414 | ||
2415 | abs(~$matrix * $matrix - $one) < 1e-12 ? return 1 : return 0; | |
2416 | ||
2417 | } | |
2418 | ||
2419 | sub is_positive($) { | |
2420 | my ($m) = @_; | |
2421 | my $pos = 1; | |
2422 | $m->each( sub { if( (shift) <= 0){ $pos = 0;return;} } ); | |
2423 | return $pos; | |
2424 | } | |
2425 | sub is_negative($) { | |
2426 | my ($m) = @_; | |
2427 | my $neg = 1; | |
2428 | $m->each( sub { if( (shift) >= 0){ $neg = 0;return;} } ); | |
2429 | return $neg; | |
2430 | } | |
2431 | ||
2432 | ||
2433 | sub is_periodic($$) { | |
2434 | my ($m,$k) = @_; | |
2435 | return 0 unless $m->is_quadratic(); | |
2436 | abs($m**(int($k)+1) - $m) < 1e-12 ? return 1 : return 0; | |
2437 | } | |
2438 | sub is_idempotent($) { | |
2439 | return (shift)->is_periodic(1); | |
2440 | } | |
2441 | ||
2442 | # Boolean check routine to see if a matrix is | |
2443 | # symmetric | |
2444 | sub is_symmetric ($) | |
2445 | { | |
2446 | my ($M) = @_; | |
2447 | my ($rows, $cols) = ($M->[1], $M->[2]); | |
2448 | # if it is not quadratic it cannot be symmetric... | |
2449 | return 0 unless ($rows == $cols); | |
2450 | # skip when $i=$j? | |
2451 | for (my $i = 1; $i < $rows; $i++) | |
2452 | { | |
2453 | for (my $j = 0; $j < $i; $j++) | |
2454 | { | |
2455 | return 0 unless ($M->[0][$i][$j] == $M->[0][$j][$i]); | |
2456 | } | |
2457 | } | |
2458 | return 1; | |
2459 | } | |
2460 | # Boolean check to see if matrix is tridiagonal | |
2461 | sub is_tridiagonal ($) { | |
2462 | my ($M) = @_; | |
2463 | my ($rows,$cols) = ($M->[1],$M->[2]); | |
2464 | my ($i,$j) = (0,0); | |
2465 | # if it is not quadratic it cannot be tridiag | |
2466 | return 0 unless ($rows == $cols); | |
2467 | ||
2468 | for(;$i < $rows; $i++ ){ | |
2469 | for(;$j < $cols; $j++ ){ | |
2470 | #print "debug: testing $i,$j = " . $M->[0][$i][$j] . "\n"; | |
2471 | # skip diag and diag+-1 | |
2472 | next if ($i == $j); | |
2473 | next if ($i+1 == $j); | |
2474 | next if ($i-1 == $j); | |
2475 | return 0 if $M->[0][$i][$j]; | |
2476 | } | |
2477 | $j = 0; | |
2478 | } | |
2479 | return 1; | |
2480 | } | |
2481 | # Boolean check to see if matrix is upper triangular | |
2482 | # i.e all nonzero elements are above main diagonal | |
2483 | sub is_upper_triangular { | |
2484 | my ($M) = @_; | |
2485 | my ($rows,$cols) = $M->dim(); | |
2486 | my ($i,$j) = (1,0); | |
2487 | return 0 unless ($rows == $cols); | |
2488 | ||
2489 | for(;$i < $rows; $i++ ){ | |
2490 | for(;$j < $cols;$j++ ){ | |
2491 | next if ($i <= $j); | |
2492 | return 0 if $M->[0][$i][$j]; | |
2493 | } | |
2494 | $j = 0; | |
2495 | } | |
2496 | return 1; | |
2497 | } | |
2498 | # Boolean check to see if matrix is lower triangular | |
2499 | # i.e all nonzero elements are lower main diagonal | |
2500 | sub is_lower_triangular { | |
2501 | my ($M) = @_; | |
2502 | my ($rows,$cols) = $M->dim(); | |
2503 | my ($i,$j) = (0,1); | |
2504 | return 0 unless ($rows == $cols); | |
2505 | ||
2506 | for(;$i < $rows; $i++ ){ | |
2507 | for(;$j < $cols;$j++ ){ | |
2508 | next if ($i >= $j); | |
2509 | return 0 if $M->[0][$i][$j]; | |
2510 | } | |
2511 | $j = 0; | |
2512 | } | |
2513 | return 1; | |
2514 | } | |
2515 | ||
2516 | # Boolean check to see if matrix is diagonal | |
2517 | sub is_diagonal ($) { | |
2518 | my ($M) = @_; | |
2519 | my ($rows,$cols) = ($M->[1],$M->[2]); | |
2520 | my ($i,$j) = (0,0); | |
2521 | return 0 unless ($rows == $cols ); | |
2522 | for(;$i < $rows; $i++ ){ | |
2523 | for(;$j < $cols; $j++ ){ | |
2524 | # skip diag elements | |
2525 | next if ($i == $j); | |
2526 | return 0 if $M->[0][$i][$j]; | |
2527 | } | |
2528 | $j = 0; | |
2529 | } | |
2530 | return 1; | |
2531 | } | |
2532 | sub is_quadratic ($) { | |
2533 | croak "Usage: \$matrix->is_quadratic()" unless (@_ == 1); | |
2534 | my ($matrix) = @_; | |
2535 | $matrix->[1] == $matrix->[2] ? return 1 : return 0; | |
2536 | } | |
2537 | ||
2538 | sub is_square($) { | |
2539 | croak "Usage: \$matrix->is_square()" unless (@_ == 1); | |
2540 | return (shift)->is_quadratic(); | |
2541 | } | |
2542 | ||
2543 | sub is_LR($) { | |
2544 | croak "Usage: \$matrix->is_LR()" unless (@_ == 1); | |
2545 | return (shift)->[3] ? 1 : 0; | |
2546 | } | |
2547 | ### | |
2548 | sub is_normal{ | |
2549 | my ($matrix) = @_; | |
2550 | my ($rows,$cols) = $matrix->dim; | |
2551 | return 0 unless ($rows == $cols); | |
2552 | ||
2553 | return 1 if ( ~$matrix * $matrix - $matrix * ~$matrix < 1e-8 ); | |
2554 | return 0; | |
2555 | ||
2556 | } | |
2557 | sub is_skew_symmetric{ | |
2558 | my ($m) = @_; | |
2559 | my ($rows, $cols) = $m->dim; | |
2560 | # if it is not quadratic it cannot be skew symmetric... | |
2561 | return 0 unless ($rows == $cols); | |
2562 | for (my $i = 1; $i < $rows; $i++) { | |
2563 | for (my $j = 0; $j < $i; $j++) { | |
2564 | return 0 unless ($m->[0][$i][$j] == -$m->[0][$j][$i]); | |
2565 | } | |
2566 | } | |
2567 | return 1; | |
2568 | ||
2569 | } | |
2570 | #### | |
2571 | sub is_gramian{ | |
2572 | my ($m) = @_; | |
2573 | my ($rows,$cols) = $m->dim; | |
2574 | my $neg=0; | |
2575 | # gramian matrix must be symmetric | |
2576 | return 0 unless $m->is_symmetric; | |
2577 | ||
2578 | # must have all non-negative eigenvalues | |
2579 | my $ev = $m->eigenvalues; | |
2580 | $ev->each(sub { $neg++ if ((shift)<0) } ); | |
2581 | ||
2582 | return $neg ? 0 : 1; | |
2583 | } | |
2584 | sub is_binary{ | |
2585 | my ($m) = @_; | |
2586 | my ($rows, $cols) = $m->dim; | |
2587 | for (my $i = 0; $i < $rows; $i++) { | |
2588 | for (my $j = 0; $j < $cols; $j++) { | |
2589 | return 0 unless ($m->[0][$i][$j] == 1 || $m->[0][$i][$j] == 0); | |
2590 | } | |
2591 | } | |
2592 | return 1; | |
2593 | ||
2594 | } | |
2595 | sub as_scilab { | |
2596 | return (shift)->as_matlab; | |
2597 | } | |
2598 | ||
2599 | sub as_matlab { | |
2600 | my ($m) = shift; | |
2601 | my %args = ( | |
2602 | format => "%s", | |
2603 | name => "", | |
2604 | semi => 0, | |
2605 | @_); | |
2606 | my ($row,$col) = $m->dim; | |
2607 | my $s = ""; | |
2608 | ||
2609 | if( $args{name} ){ | |
2610 | $s = "$args{name} = "; | |
2611 | } | |
2612 | $s .= "["; | |
2613 | $m->each( | |
2614 | sub { my($x,$i,$j) = @_; | |
2615 | $s .= sprintf(" $args{format}",$x); | |
2616 | $s .= ";\n" if( $j == $col && $i != $row); | |
2617 | } | |
2618 | ); | |
2619 | $s .= "]"; | |
2620 | $s .= ";" if $args{semi}; | |
2621 | return $s; | |
2622 | } | |
2623 | #TODO: docs+test | |
2624 | sub as_yacas{ | |
2625 | my ($m) = shift; | |
2626 | my %args = ( | |
2627 | format => "%s", | |
2628 | name => "", | |
2629 | semi => 0, | |
2630 | @_); | |
2631 | my ($row,$col) = $m->dim; | |
2632 | my $s = ""; | |
2633 | ||
2634 | if( $args{name} ){ | |
2635 | $s = "$args{name} := "; | |
2636 | } | |
2637 | $s .= "{"; | |
2638 | ||
2639 | $m->each( | |
2640 | sub { my($x,$i,$j) = @_; | |
2641 | $s .= "{" if ($j == 1); | |
2642 | $s .= sprintf("$args{format}",$x); | |
2643 | $s .= "," if( $j != $col ); | |
2644 | $s .= "}," if ($j == $col && $i != $row); | |
2645 | } | |
2646 | ); | |
2647 | $s .= "}}"; | |
2648 | ||
2649 | return $s; | |
2650 | } | |
2651 | #TODO: any ideas for a good test? | |
2652 | sub as_latex{ | |
2653 | my ($m) = shift; | |
2654 | my %args = ( | |
2655 | format => "%s", | |
2656 | name => "", | |
2657 | align => "c", | |
2658 | @_); | |
2659 | ||
2660 | ||
2661 | my ($row,$col) = $m->dim; | |
2662 | my $inside; | |
2663 | my $s = <<LATEX; | |
2664 | \$ | |
2665 | \\left( \\begin{array}{%COLS%} | |
2666 | %INSIDE%\\end{array} \\right) | |
2667 | \$ | |
2668 | LATEX | |
2669 | $args{align} = lc $args{align}; | |
2670 | if( $args{align} !~ m/^(c|l|r)$/ ){ | |
2671 | croak "Math::MatrixReal::as_latex(): Invalid alignment '$args{align}'"; | |
2672 | } | |
2673 | ||
2674 | $s =~ s/%COLS%/$args{align} x $col/em; | |
2675 | ||
2676 | if( $args{name} ){ | |
2677 | $s = "\$$args{name} = \$ " . $s; | |
2678 | } | |
2679 | $m->each( | |
2680 | sub { my($x,$i,$j)=@_; | |
2681 | ||
2682 | $x = sprintf($args{format},$x); | |
2683 | ||
2684 | # last element in each row gets a \\ | |
2685 | if ($j == $col && $i != $row){ | |
2686 | $inside .= "$x \\\\"."\n"; | |
2687 | # the annoying last line has neither | |
2688 | } elsif( $j == $col && $i == $row){ | |
2689 | $inside .= "$x\n"; | |
2690 | } else { | |
2691 | $inside .= "$x&"; | |
2692 | } | |
2693 | } | |
2694 | ); | |
2695 | $s =~ s/%INSIDE%/$inside/gm; | |
2696 | return $s; | |
2697 | } | |
2698 | #### | |
2699 | sub spectral_radius { | |
2700 | my ($matrix) = @_; | |
2701 | my ($r,$c) = $matrix->dim; | |
2702 | my $ev = $matrix->eigenvalues; | |
2703 | my $radius=0; | |
2704 | $ev->each(sub { my $x = shift; $radius = $x if (abs($x) > $radius); } ); | |
2705 | return $radius; | |
2706 | } | |
2707 | ||
2708 | ||
2709 | ######################################## | |
2710 | # # | |
2711 | # define overloaded operators section: # | |
2712 | # # | |
2713 | ######################################## | |
2714 | ||
2715 | sub _negate | |
2716 | { | |
2717 | my($object,$argument,$flag) = @_; | |
2718 | # my($name) = "neg"; | |
2719 | my($temp); | |
2720 | ||
2721 | $temp = $object->new($object->[1],$object->[2]); | |
2722 | $temp->negate($object); | |
2723 | return($temp); | |
2724 | } | |
2725 | ||
2726 | sub _transpose | |
2727 | { | |
2728 | my($object,$argument,$flag) = @_; | |
2729 | # my($name) = "'~'"; | |
2730 | my($temp); | |
2731 | ||
2732 | $temp = $object->new($object->[2],$object->[1]); | |
2733 | $temp->transpose($object); | |
2734 | return($temp); | |
2735 | ||
2736 | } | |
2737 | ||
2738 | sub _boolean | |
2739 | { | |
2740 | my($object,$argument,$flag) = @_; | |
2741 | # my($name) = "bool"; | |
2742 | my($rows,$cols) = ($object->[1],$object->[2]); | |
2743 | my($i,$j,$result); | |
2744 | ||
2745 | #TODO: ugly... | |
2746 | $result = 0; | |
2747 | BOOL: | |
2748 | for ( $i = 0; $i < $rows; $i++ ) | |
2749 | { | |
2750 | for ( $j = 0; $j < $cols; $j++ ) | |
2751 | { | |
2752 | if ($object->[0][$i][$j] != 0) | |
2753 | { | |
2754 | $result = 1; | |
2755 | last BOOL; | |
2756 | } | |
2757 | } | |
2758 | } | |
2759 | return($result); | |
2760 | } | |
2761 | #TODO: ugly copy+paste | |
2762 | sub _not_boolean | |
2763 | { | |
2764 | my($object,$argument,$flag) = @_; | |
2765 | # my($name) = "'!'"; | |
2766 | my($rows,$cols) = ($object->[1],$object->[2]); | |
2767 | my($i,$j,$result); | |
2768 | ||
2769 | $result = 1; | |
2770 | NOTBOOL: | |
2771 | for ( $i = 0; $i < $rows; $i++ ) | |
2772 | { | |
2773 | for ( $j = 0; $j < $cols; $j++ ) | |
2774 | { | |
2775 | if ($object->[0][$i][$j] != 0) | |
2776 | { | |
2777 | $result = 0; | |
2778 | last NOTBOOL; | |
2779 | } | |
2780 | } | |
2781 | } | |
2782 | return($result); | |
2783 | } | |
2784 | ||
2785 | sub _stringify | |
2786 | { | |
2787 | my($object,$argument,$flag) = @_; | |
2788 | # my($name) = '""'; | |
2789 | my($rows,$cols) = ($object->[1],$object->[2]); | |
2790 | my($i,$j,$s); | |
2791 | ||
2792 | $s = ''; | |
2793 | for ( $i = 0; $i < $rows; $i++ ) | |
2794 | { | |
2795 | $s .= "[ "; | |
2796 | for ( $j = 0; $j < $cols; $j++ ) | |
2797 | { | |
2798 | $s .= sprintf("% #-19.12E ", $object->[0][$i][$j]); | |
2799 | } | |
2800 | $s .= "]\n"; | |
2801 | } | |
2802 | return($s); | |
2803 | } | |
2804 | ||
2805 | sub _norm | |
2806 | { | |
2807 | my($object,$argument,$flag) = @_; | |
2808 | # my($name) = "abs"; | |
2809 | ||
2810 | return( $object->norm_one() ); | |
2811 | } | |
2812 | ||
2813 | sub _add | |
2814 | { | |
2815 | my($object,$argument,$flag) = @_; | |
2816 | my($name) = "'+'"; | |
2817 | my($temp); | |
2818 | ||
2819 | if ((defined $argument) && ref($argument) && | |
2820 | (ref($argument) !~ /^SCALAR$|^ARRAY$|^HASH$|^CODE$|^REF$/)) | |
2821 | { | |
2822 | if (defined $flag) | |
2823 | { | |
2824 | $temp = $object->new($object->[1],$object->[2]); | |
2825 | $temp->add($object,$argument); | |
2826 | return($temp); | |
2827 | } | |
2828 | else | |
2829 | { | |
2830 | $object->add($object,$argument); | |
2831 | return($object); | |
2832 | } | |
2833 | } | |
2834 | else | |
2835 | { | |
2836 | croak "Math::MatrixReal $name: wrong argument type"; | |
2837 | } | |
2838 | } | |
2839 | ||
2840 | sub _subtract | |
2841 | { | |
2842 | my($object,$argument,$flag) = @_; | |
2843 | my($name) = "'-'"; | |
2844 | my($temp); | |
2845 | ||
2846 | if ((defined $argument) && ref($argument) && | |
2847 | (ref($argument) !~ /^SCALAR$|^ARRAY$|^HASH$|^CODE$|^REF$/)) | |
2848 | { | |
2849 | if (defined $flag) | |
2850 | { | |
2851 | $temp = $object->new($object->[1],$object->[2]); | |
2852 | if ($flag) { $temp->subtract($argument,$object); } | |
2853 | else { $temp->subtract($object,$argument); } | |
2854 | return($temp); | |
2855 | } | |
2856 | else | |
2857 | { | |
2858 | $object->subtract($object,$argument); | |
2859 | return($object); | |
2860 | } | |
2861 | } | |
2862 | else | |
2863 | { | |
2864 | croak "Math::MatrixReal $name: wrong argument type"; | |
2865 | } | |
2866 | } | |
2867 | ||
2868 | sub _exponent | |
2869 | { | |
2870 | my($matrix,$argument,$flag) = @_; | |
2871 | my($rows,$cols) = ($matrix->[1],$matrix->[2]); | |
2872 | my($name) = "'**'"; | |
2873 | ||
2874 | return $matrix->exponent( $argument ); | |
2875 | } | |
2876 | ||
2877 | ||
2878 | sub _multiply | |
2879 | { | |
2880 | my($object,$argument,$flag) = @_; | |
2881 | my($name) = "'*'"; | |
2882 | my($temp); | |
2883 | ||
2884 | if ((defined $argument) && ref($argument) && | |
2885 | (ref($argument) !~ /^SCALAR$|^ARRAY$|^HASH$|^CODE$|^REF$/)) | |
2886 | { | |
2887 | if ((defined $flag) && $flag) | |
2888 | { | |
2889 | return( multiply($argument,$object) ); | |
2890 | } | |
2891 | else | |
2892 | { | |
2893 | return( multiply($object,$argument) ); | |
2894 | } | |
2895 | } | |
2896 | elsif ((defined $argument) && !(ref($argument))) | |
2897 | { | |
2898 | if (defined $flag) | |
2899 | { | |
2900 | $temp = $object->new($object->[1],$object->[2]); | |
2901 | $temp->multiply_scalar($object,$argument); | |
2902 | return($temp); | |
2903 | } | |
2904 | else | |
2905 | { | |
2906 | $object->multiply_scalar($object,$argument); | |
2907 | return($object); | |
2908 | } | |
2909 | } | |
2910 | else | |
2911 | { | |
2912 | croak "Math::MatrixReal $name: wrong argument type"; | |
2913 | } | |
2914 | } | |
2915 | ||
2916 | sub _assign_add | |
2917 | { | |
2918 | my($object,$argument,$flag) = @_; | |
2919 | # my($name) = "'+='"; | |
2920 | ||
2921 | return( &_add($object,$argument,undef) ); | |
2922 | } | |
2923 | ||
2924 | sub _assign_subtract | |
2925 | { | |
2926 | my($object,$argument,$flag) = @_; | |
2927 | # my($name) = "'-='"; | |
2928 | ||
2929 | return( &_subtract($object,$argument,undef) ); | |
2930 | } | |
2931 | ||
2932 | sub _assign_multiply | |
2933 | { | |
2934 | my($object,$argument,$flag) = @_; | |
2935 | # my($name) = "'*='"; | |
2936 | ||
2937 | return( &_multiply($object,$argument,undef) ); | |
2938 | } | |
2939 | ||
2940 | sub _assign_exponent { | |
2941 | my($object,$arg,$flag) = @_; | |
2942 | return ( &_exponent($object,$arg,undef) ); | |
2943 | } | |
2944 | ||
2945 | sub _equal | |
2946 | { | |
2947 | my($object,$argument,$flag) = @_; | |
2948 | my($name) = "'=='"; | |
2949 | my($rows,$cols) = ($object->[1],$object->[2]); | |
2950 | my($i,$j,$result); | |
2951 | ||
2952 | if ((defined $argument) && ref($argument) && | |
2953 | (ref($argument) !~ /^SCALAR$|^ARRAY$|^HASH$|^CODE$|^REF$/)) | |
2954 | { | |
2955 | $result = 1; | |
2956 | EQUAL: | |
2957 | for ( $i = 0; $i < $rows; $i++ ) | |
2958 | { | |
2959 | for ( $j = 0; $j < $cols; $j++ ) | |
2960 | { | |
2961 | if ($object->[0][$i][$j] != $argument->[0][$i][$j]) | |
2962 | { | |
2963 | $result = 0; | |
2964 | last EQUAL; | |
2965 | } | |
2966 | } | |
2967 | } | |
2968 | return($result); | |
2969 | } | |
2970 | else | |
2971 | { | |
2972 | croak "Math::MatrixReal $name: wrong argument type"; | |
2973 | } | |
2974 | } | |
2975 | ||
2976 | sub _not_equal | |
2977 | { | |
2978 | my($object,$argument,$flag) = @_; | |
2979 | my($name) = "'!='"; | |
2980 | my($rows,$cols) = ($object->[1],$object->[2]); | |
2981 | my($i,$j,$result); | |
2982 | ||
2983 | if ((defined $argument) && ref($argument) && | |
2984 | (ref($argument) !~ /^SCALAR$|^ARRAY$|^HASH$|^CODE$|^REF$/)) | |
2985 | { | |
2986 | $result = 0; | |
2987 | NOTEQUAL: | |
2988 | for ( $i = 0; $i < $rows; $i++ ) | |
2989 | { | |
2990 | for ( $j = 0; $j < $cols; $j++ ) | |
2991 | { | |
2992 | if ($object->[0][$i][$j] != $argument->[0][$i][$j]) | |
2993 | { | |
2994 | $result = 1; | |
2995 | last NOTEQUAL; | |
2996 | } | |
2997 | } | |
2998 | } | |
2999 | return($result); | |
3000 | } | |
3001 | else | |
3002 | { | |
3003 | croak "Math::MatrixReal $name: wrong argument type"; | |
3004 | } | |
3005 | } | |
3006 | ||
3007 | sub _less_than | |
3008 | { | |
3009 | my($object,$argument,$flag) = @_; | |
3010 | my($name) = "'<'"; | |
3011 | ||
3012 | if ((defined $argument) && ref($argument) && | |
3013 | (ref($argument) !~ /^SCALAR$|^ARRAY$|^HASH$|^CODE$|^REF$/)) | |
3014 | { | |
3015 | if ((defined $flag) && $flag) | |
3016 | { | |
3017 | return( $argument->norm_one() < $object->norm_one() ); | |
3018 | } | |
3019 | else | |
3020 | { | |
3021 | return( $object->norm_one() < $argument->norm_one() ); | |
3022 | } | |
3023 | } | |
3024 | elsif ((defined $argument) && !(ref($argument))) | |
3025 | { | |
3026 | if ((defined $flag) && $flag) | |
3027 | { | |
3028 | return( abs($argument) < $object->norm_one() ); | |
3029 | } | |
3030 | else | |
3031 | { | |
3032 | return( $object->norm_one() < abs($argument) ); | |
3033 | } | |
3034 | } | |
3035 | else | |
3036 | { | |
3037 | croak "Math::MatrixReal $name: wrong argument type"; | |
3038 | } | |
3039 | } | |
3040 | ||
3041 | sub _less_than_or_equal | |
3042 | { | |
3043 | my($object,$argument,$flag) = @_; | |
3044 | my($name) = "'<='"; | |
3045 | ||
3046 | if ((defined $argument) && ref($argument) && | |
3047 | (ref($argument) !~ /^SCALAR$|^ARRAY$|^HASH$|^CODE$|^REF$/)) | |
3048 | { | |
3049 | if ((defined $flag) && $flag) | |
3050 | { | |
3051 | return( $argument->norm_one() <= $object->norm_one() ); | |
3052 | } | |
3053 | else | |
3054 | { | |
3055 | return( $object->norm_one() <= $argument->norm_one() ); | |
3056 | } | |
3057 | } | |
3058 | elsif ((defined $argument) && !(ref($argument))) | |
3059 | { | |
3060 | if ((defined $flag) && $flag) | |
3061 | { | |
3062 | return( abs($argument) <= $object->norm_one() ); | |
3063 | } | |
3064 | else | |
3065 | { | |
3066 | return( $object->norm_one() <= abs($argument) ); | |
3067 | } | |
3068 | } | |
3069 | else | |
3070 | { | |
3071 | croak "Math::MatrixReal $name: wrong argument type"; | |
3072 | } | |
3073 | } | |
3074 | ||
3075 | sub _greater_than | |
3076 | { | |
3077 | my($object,$argument,$flag) = @_; | |
3078 | my($name) = "'>'"; | |
3079 | ||
3080 | if ((defined $argument) && ref($argument) && | |
3081 | (ref($argument) !~ /^SCALAR$|^ARRAY$|^HASH$|^CODE$|^REF$/)) | |
3082 | { | |
3083 | if ((defined $flag) && $flag) | |
3084 | { | |
3085 | return( $argument->norm_one() > $object->norm_one() ); | |
3086 | } | |
3087 | else | |
3088 | { | |
3089 | return( $object->norm_one() > $argument->norm_one() ); | |
3090 | } | |
3091 | } | |
3092 | elsif ((defined $argument) && !(ref($argument))) | |
3093 | { | |
3094 | if ((defined $flag) && $flag) | |
3095 | { | |
3096 | return( abs($argument) > $object->norm_one() ); | |
3097 | } | |
3098 | else | |
3099 | { | |
3100 | return( $object->norm_one() > abs($argument) ); | |
3101 | } | |
3102 | } | |
3103 | else | |
3104 | { | |
3105 | croak "Math::MatrixReal $name: wrong argument type"; | |
3106 | } | |
3107 | } | |
3108 | ||
3109 | sub _greater_than_or_equal | |
3110 | { | |
3111 | my($object,$argument,$flag) = @_; | |
3112 | my($name) = "'>='"; | |
3113 | ||
3114 | if ((defined $argument) && ref($argument) && | |
3115 | (ref($argument) !~ /^SCALAR$|^ARRAY$|^HASH$|^CODE$|^REF$/)) | |
3116 | { | |
3117 | if ((defined $flag) && $flag) | |
3118 | { | |
3119 | return( $argument->norm_one() >= $object->norm_one() ); | |
3120 | } | |
3121 | else | |
3122 | { | |
3123 | return( $object->norm_one() >= $argument->norm_one() ); | |
3124 | } | |
3125 | } | |
3126 | elsif ((defined $argument) && !(ref($argument))) | |
3127 | { | |
3128 | if ((defined $flag) && $flag) | |
3129 | { | |
3130 | return( abs($argument) >= $object->norm_one() ); | |
3131 | } | |
3132 | else | |
3133 | { | |
3134 | return( $object->norm_one() >= abs($argument) ); | |
3135 | } | |
3136 | } | |
3137 | else | |
3138 | { | |
3139 | croak "Math::MatrixReal $name: wrong argument type"; | |
3140 | } | |
3141 | } | |
3142 | sub _exp { | |
3143 | my ($matrix,$arg,$flag) = @_; | |
3144 | my $new_matrix = $matrix->clone(); | |
3145 | my ($rows,$cols) = $matrix->dim(); | |
3146 | ||
3147 | $new_matrix->_undo_LR(); | |
3148 | ||
3149 | croak "Math::MatrixReal::exp(): Matrix is not quadratic" unless ($rows == $cols); | |
3150 | croak "Math::MatrixReal::exp(): Only diagonal matrices supported" unless ( $matrix->is_diagonal() ); | |
3151 | ||
3152 | $new_matrix = $matrix->each_diag( sub { exp(shift) } ); | |
3153 | return $new_matrix; | |
3154 | ||
3155 | } | |
3156 | sub _cos { | |
3157 | my ($matrix,$arg,$flag) = @_; | |
3158 | my $new_matrix = $matrix->clone(); | |
3159 | my ($rows,$cols) = $matrix->dim(); | |
3160 | ||
3161 | $new_matrix->_undo_LR(); | |
3162 | ||
3163 | croak "Math::MatrixReal::cos(): Matrix is not quadratic" unless ($rows == $cols); | |
3164 | croak "Math::MatrixReal::cos(): Only diagonal matrices supported" unless ( $matrix->is_diagonal() ); | |
3165 | ||
3166 | $new_matrix = $matrix->each_diag( sub { cos(shift) } ); | |
3167 | return $new_matrix; | |
3168 | } | |
3169 | sub _sin { | |
3170 | my ($matrix,$arg,$flag) = @_; | |
3171 | my $new_matrix = $matrix->clone(); | |
3172 | my ($rows,$cols) = $matrix->dim(); | |
3173 | ||
3174 | $new_matrix->_undo_LR(); | |
3175 | ||
3176 | croak "Math::MatrixReal::sin(): Matrix is not quadratic" unless ($rows == $cols); | |
3177 | croak "Math::MatrixReal::sin(): Only diagonal matrices supported" unless ( $matrix->is_diagonal() ); | |
3178 | ||
3179 | $new_matrix = $matrix->each_diag( sub { sin(shift) } ); | |
3180 | return $new_matrix; | |
3181 | ||
3182 | } | |
3183 | ||
3184 | sub _clone | |
3185 | { | |
3186 | my($object,$argument,$flag) = @_; | |
3187 | # my($name) = "'='"; | |
3188 | my($temp); | |
3189 | ||
3190 | $temp = $object->new($object->[1],$object->[2]); | |
3191 | $temp->copy($object); | |
3192 | $temp->_undo_LR(); | |
3193 | return($temp); | |
3194 | } | |
3195 | ||
3196 | sub _trace | |
3197 | { | |
3198 | my($text,$object,$argument,$flag) = @_; | |
3199 | ||
3200 | unless (defined $object) { $object = 'undef'; }; | |
3201 | unless (defined $argument) { $argument = 'undef'; }; | |
3202 | unless (defined $flag) { $flag = 'undef'; }; | |
3203 | if (ref($object)) { $object = ref($object); } | |
3204 | if (ref($argument)) { $argument = ref($argument); } | |
3205 | print "$text: \$obj='$object' \$arg='$argument' \$flag='$flag'\n"; | |
3206 | } | |
3207 | ||
3208 | 1; | |
3209 | ||
3210 | __END__ | |
3211 | ||
3212 | =head1 NAME | |
3213 | ||
3214 | Math::MatrixReal - Matrix of Reals | |
3215 | ||
3216 | Implements the data type "matrix of reals" (and consequently also | |
3217 | "vector of reals"). | |
3218 | ||
3219 | =head1 DESCRIPTION | |
3220 | ||
3221 | Implements the data type "matrix of reals", which can be used almost | |
3222 | like any other basic Perl type thanks to B<OPERATOR OVERLOADING>, i.e., | |
3223 | ||
3224 | $product = $matrix1 * $matrix2; | |
3225 | ||
3226 | does what you would like it to do (a matrix multiplication). | |
3227 | ||
3228 | Also features many important operations and methods: matrix norm, | |
3229 | matrix transposition, matrix inverse, determinant of a matrix, order | |
3230 | and numerical condition of a matrix, scalar product of vectors, vector | |
3231 | product of vectors, vector length, projection of row and column vectors, | |
3232 | a comfortable way for reading in a matrix from a file, the keyboard or | |
3233 | your code, and many more. | |
3234 | ||
3235 | Allows to solve linear equation systems using an efficient algorithm | |
3236 | known as "L-R-decomposition" and several approximative (iterative) methods. | |
3237 | ||
3238 | Features an implementation of Kleene's algorithm to compute the minimal | |
3239 | costs for all paths in a graph with weighted edges (the "weights" being | |
3240 | the costs associated with each edge). | |
3241 | ||
3242 | =head1 SYNOPSIS | |
3243 | ||
3244 | =head2 Constructor Methods And Such | |
3245 | ||
3246 | =item * | |
3247 | ||
3248 | C<use Math::MatrixReal;> | |
3249 | ||
3250 | Makes the methods and overloaded operators of this module available | |
3251 | to your program. | |
3252 | ||
3253 | =item * | |
3254 | ||
3255 | C<$new_matrix = new Math::MatrixReal($rows,$columns);> | |
3256 | ||
3257 | The matrix object constructor method. A new matrix of size $rows by $columns | |
3258 | will be created, with the value C<0.0> for all elements. | |
3259 | ||
3260 | Note that this method is implicitly called by many of the other methods | |
3261 | in this module. | |
3262 | ||
3263 | =item * | |
3264 | ||
3265 | C<$new_matrix = $some_matrix-E<gt>>C<new($rows,$columns);> | |
3266 | ||
3267 | Another way of calling the matrix object constructor method. | |
3268 | ||
3269 | Matrix "C<$some_matrix>" is not changed by this in any way. | |
3270 | ||
3271 | =item * | |
3272 | ||
3273 | C<$new_matrix = $matrix-E<gt>new_from_cols( [ $column_vector|$array_ref|$string, ... ] )> | |
3274 | ||
3275 | Creates a new matrix given a reference to an array of any of the following: | |
3276 | ||
3277 | =over 4 | |
3278 | ||
3279 | =item * column vectors ( n by 1 Math::MatrixReal matrices ) | |
3280 | ||
3281 | =item * references to arrays | |
3282 | ||
3283 | =item * strings properly formatted to create a column with Math::MatrixReal's | |
3284 | C<new_from_string> command | |
3285 | ||
3286 | =back | |
3287 | ||
3288 | You may mix and match these as you wish. However, all must be of the | |
3289 | same dimension--no padding happens automatically. Example: | |
3290 | ||
3291 | my $matrix = Math::MatrixReal->new_from_cols( [ [1,2], [3,4] ] ); | |
3292 | print $matrix; | |
3293 | ||
3294 | will print | |
3295 | ||
3296 | [ 1.000000000000E+00 3.000000000000E+00 ] | |
3297 | [ 2.000000000000E+00 4.000000000000E+00 ] | |
3298 | ||
3299 | ||
3300 | =item * | |
3301 | ||
3302 | C<new_from_rows( [ $row_vector|$array_ref|$string, ... ] )> | |
3303 | ||
3304 | Creates a new matrix given a reference to an array of any of the following: | |
3305 | ||
3306 | =over 4 | |
3307 | ||
3308 | =item * row vectors ( 1 by n Math::MatrixReal matrices ) | |
3309 | ||
3310 | =item * references to arrays | |
3311 | ||
3312 | =item * strings properly formatted to create a row with Math::MatrixReal's C<new_from_string> command | |
3313 | ||
3314 | =back | |
3315 | ||
3316 | You may mix and match these as you wish. However, all must be of the | |
3317 | same dimension--no padding happens automatically. Example: | |
3318 | ||
3319 | my $matrix = Math::MatrixReal->new_from_rows( [ [1,2], [3,4] ] ); | |
3320 | print $matrix; | |
3321 | ||
3322 | will print | |
3323 | ||
3324 | [ 1.000000000000E+00 2.000000000000E+00 ] | |
3325 | [ 3.000000000000E+00 4.000000000000E+00 ] | |
3326 | ||
3327 | ||
3328 | =item * | |
3329 | ||
3330 | C<$new_matrix = Math::MatrixReal-E<gt>new_diag( $array_ref );> | |
3331 | ||
3332 | This method allows you to create a diagonal matrix by only specifying | |
3333 | the diagonal elements. Example: | |
3334 | ||
3335 | $matrix = Math::MatrixReal->new_diag( [ 1,2,3,4 ] ); | |
3336 | print $matrix; | |
3337 | ||
3338 | will print | |
3339 | ||
3340 | [ 1.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 ] | |
3341 | [ 0.000000000000E+00 2.000000000000E+00 0.000000000000E+00 0.000000000000E+00 ] | |
3342 | [ 0.000000000000E+00 0.000000000000E+00 3.000000000000E+00 0.000000000000E+00 ] | |
3343 | [ 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 4.000000000000E+00 ] | |
3344 | ||
3345 | ||
3346 | =item * | |
3347 | ||
3348 | C<$new_matrix = Math::MatrixReal-E<gt>>C<new_from_string($string);> | |
3349 | ||
3350 | This method allows you to read in a matrix from a string (for | |
3351 | instance, from the keyboard, from a file or from your code). | |
3352 | ||
3353 | The syntax is simple: each row must start with "C<[ >" and end with | |
3354 | "C< ]\n>" ("C<\n>" being the newline character and "C< >" a space or | |
3355 | tab) and contain one or more numbers, all separated from each other | |
3356 | by spaces or tabs. | |
3357 | ||
3358 | Additional spaces or tabs can be added at will, but no comments. | |
3359 | ||
3360 | Examples: | |
3361 | ||
3362 | $string = "[ 1 2 3 ]\n[ 2 2 -1 ]\n[ 1 1 1 ]\n"; | |
3363 | $matrix = Math::MatrixReal->new_from_string($string); | |
3364 | print "$matrix"; | |
3365 | ||
3366 | By the way, this prints | |
3367 | ||
3368 | [ 1.000000000000E+00 2.000000000000E+00 3.000000000000E+00 ] | |
3369 | [ 2.000000000000E+00 2.000000000000E+00 -1.000000000000E+00 ] | |
3370 | [ 1.000000000000E+00 1.000000000000E+00 1.000000000000E+00 ] | |
3371 | ||
3372 | But you can also do this in a much more comfortable way using the | |
3373 | shell-like "here-document" syntax: | |
3374 | ||
3375 | $matrix = Math::MatrixReal->new_from_string(<<'MATRIX'); | |
3376 | [ 1 0 0 0 0 0 1 ] | |
3377 | [ 0 1 0 0 0 0 0 ] | |
3378 | [ 0 0 1 0 0 0 0 ] | |
3379 | [ 0 0 0 1 0 0 0 ] | |
3380 | [ 0 0 0 0 1 0 0 ] | |
3381 | [ 0 0 0 0 0 1 0 ] | |
3382 | [ 1 0 0 0 0 0 -1 ] | |
3383 | MATRIX | |
3384 | ||
3385 | You can even use variables in the matrix: | |
3386 | ||
3387 | $c1 = 2 / 3; | |
3388 | $c2 = -2 / 5; | |
3389 | $c3 = 26 / 9; | |
3390 | ||
3391 | $matrix = Math::MatrixReal->new_from_string(<<"MATRIX"); | |
3392 | ||
3393 | [ 3 2 0 ] | |
3394 | [ 0 3 2 ] | |
3395 | [ $c1 $c2 $c3 ] | |
3396 | ||
3397 | MATRIX | |
3398 | ||
3399 | (Remember that you may use spaces and tabs to format the matrix to | |
3400 | your taste) | |
3401 | ||
3402 | Note that this method uses exactly the same representation for a | |
3403 | matrix as the "stringify" operator "": this means that you can convert | |
3404 | any matrix into a string with C<$string = "$matrix";> and read it back | |
3405 | in later (for instance from a file!). | |
3406 | ||
3407 | Note however that you may suffer a precision loss in this process | |
3408 | because only 13 digits are supported in the mantissa when printed!! | |
3409 | ||
3410 | If the string you supply (or someone else supplies) does not obey | |
3411 | the syntax mentioned above, an exception is raised, which can be | |
3412 | caught by "eval" as follows: | |
3413 | ||
3414 | print "Please enter your matrix (in one line): "; | |
3415 | $string = <STDIN>; | |
3416 | $string =~ s/\\n/\n/g; | |
3417 | eval { $matrix = Math::MatrixReal->new_from_string($string); }; | |
3418 | if ($@) | |
3419 | { | |
3420 | print "$@"; | |
3421 | # ... | |
3422 | # (error handling) | |
3423 | } | |
3424 | else | |
3425 | { | |
3426 | # continue... | |
3427 | } | |
3428 | ||
3429 | or as follows: | |
3430 | ||
3431 | eval { $matrix = Math::MatrixReal->new_from_string(<<"MATRIX"); }; | |
3432 | [ 3 2 0 ] | |
3433 | [ 0 3 2 ] | |
3434 | [ $c1 $c2 $c3 ] | |
3435 | MATRIX | |
3436 | if ($@) | |
3437 | # ... | |
3438 | ||
3439 | Actually, the method shown above for reading a matrix from the keyboard | |
3440 | is a little awkward, since you have to enter a lot of "\n"'s for the | |
3441 | newlines. | |
3442 | ||
3443 | A better way is shown in this piece of code: | |
3444 | ||
3445 | while (1) | |
3446 | { | |
3447 | print "\nPlease enter your matrix "; | |
3448 | print "(multiple lines, <ctrl-D> = done):\n"; | |
3449 | eval { $new_matrix = | |
3450 | Math::MatrixReal->new_from_string(join('',<STDIN>)); }; | |
3451 | if ($@) | |
3452 | { | |
3453 | $@ =~ s/\s+at\b.*?$//; | |
3454 | print "${@}Please try again.\n"; | |
3455 | } | |
3456 | else { last; } | |
3457 | } | |
3458 | ||
3459 | Possible error messages of the "new_from_string()" method are: | |
3460 | ||
3461 | Math::MatrixReal::new_from_string(): syntax error in input string | |
3462 | Math::MatrixReal::new_from_string(): empty input string | |
3463 | ||
3464 | If the input string has rows with varying numbers of columns, | |
3465 | the following warning will be printed to STDERR: | |
3466 | ||
3467 | Math::MatrixReal::new_from_string(): missing elements will be set to zero! | |
3468 | ||
3469 | If everything is okay, the method returns an object reference to the | |
3470 | (newly allocated) matrix containing the elements you specified. | |
3471 | ||
3472 | =item * | |
3473 | ||
3474 | C<$new_matrix = $some_matrix-E<gt>shadow();> | |
3475 | ||
3476 | Returns an object reference to a B<NEW> but B<EMPTY> matrix | |
3477 | (filled with zero's) of the B<SAME SIZE> as matrix "C<$some_matrix>". | |
3478 | ||
3479 | Matrix "C<$some_matrix>" is not changed by this in any way. | |
3480 | ||
3481 | =item * | |
3482 | ||
3483 | C<$matrix1-E<gt>copy($matrix2);> | |
3484 | ||
3485 | Copies the contents of matrix "C<$matrix2>" to an B<ALREADY EXISTING> | |
3486 | matrix "C<$matrix1>" (which must have the same size as matrix "C<$matrix2>"!). | |
3487 | ||
3488 | Matrix "C<$matrix2>" is not changed by this in any way. | |
3489 | ||
3490 | =item * | |
3491 | ||
3492 | C<$twin_matrix = $some_matrix-E<gt>clone();> | |
3493 | ||
3494 | Returns an object reference to a B<NEW> matrix of the B<SAME SIZE> as | |
3495 | matrix "C<$some_matrix>". The contents of matrix "C<$some_matrix>" have | |
3496 | B<ALREADY BEEN COPIED> to the new matrix "C<$twin_matrix>". This | |
3497 | is the method that the operator "=" is overloaded to when you type | |
3498 | C<$a = $b>, when C<$a> and C<$b> are matrices. | |
3499 | ||
3500 | Matrix "C<$some_matrix>" is not changed by this in any way. | |
3501 | ||
3502 | ||
3503 | =head2 Matrix Row, Column and Element operations | |
3504 | ||
3505 | =item * | |
3506 | ||
3507 | C<$row_vector = $matrix-E<gt>row($row);> | |
3508 | ||
3509 | This is a projection method which returns an object reference to | |
3510 | a B<NEW> matrix (which in fact is a (row) vector since it has only | |
3511 | one row) to which row number "C<$row>" of matrix "C<$matrix>" has | |
3512 | already been copied. | |
3513 | ||
3514 | Matrix "C<$matrix>" is not changed by this in any way. | |
3515 | ||
3516 | =item * | |
3517 | ||
3518 | C<$column_vector = $matrix-E<gt>column($column);> | |
3519 | ||
3520 | This is a projection method which returns an object reference to | |
3521 | a B<NEW> matrix (which in fact is a (column) vector since it has | |
3522 | only one column) to which column number "C<$column>" of matrix | |
3523 | "C<$matrix>" has already been copied. | |
3524 | ||
3525 | Matrix "C<$matrix>" is not changed by this in any way. | |
3526 | ||
3527 | =item * | |
3528 | ||
3529 | C<$matrix-E<gt>assign($row,$column,$value);> | |
3530 | ||
3531 | Explicitly assigns a value "C<$value>" to a single element of the | |
3532 | matrix "C<$matrix>", located in row "C<$row>" and column "C<$column>", | |
3533 | thereby replacing the value previously stored there. | |
3534 | ||
3535 | =item * | |
3536 | ||
3537 | C<$value = $matrix-E<gt>>C<element($row,$column);> | |
3538 | ||
3539 | Returns the value of a specific element of the matrix "C<$matrix>", | |
3540 | located in row "C<$row>" and column "C<$column>". | |
3541 | ||
3542 | =item * | |
3543 | ||
3544 | C<$new_matrix = $matrix-E<gt>each( \&function )>; | |
3545 | ||
3546 | Creates a new matrix by evaluating a code reference on each element of the | |
3547 | given matrix. The function is passed the element, the row index and the column | |
3548 | index, in that order. The value the function returns ( or the value of the last | |
3549 | executed statement ) is the value given to the corresponding element in $new_matrix. | |
3550 | ||
3551 | Example: | |
3552 | ||
3553 | # add 1 to every element in the matrix | |
3554 | $matrix = $matrix->each ( sub { (shift) + 1 } ); | |
3555 | ||
3556 | ||
3557 | Example: | |
3558 | ||
3559 | my $cofactor = $matrix->each( sub { my(undef,$i,$j) = @_; | |
3560 | ($i+$j) % 2 == 0 ? $matrix->minor($i,$j)->det() | |
3561 | : -1*$matrix->minor($i,$j)->det(); | |
3562 | } ); | |
3563 | ||
3564 | This code needs some explanation. For each element of $matrix, it throws away the actual value | |
3565 | and stores the row and column indexes in $i and $j. Then it sets element [$i,$j] in $cofactor | |
3566 | to the determinant of C<$matrix-E<gt>minor($i,$j)> if it is an "even" element, or C<-1*$matrix-E<gt>minor($i,$j)> | |
3567 | if it is an "odd" element. | |
3568 | ||
3569 | =item * | |
3570 | ||
3571 | C<$new_matrix = $matrix-E<gt>each_diag( \&function )>; | |
3572 | ||
3573 | Creates a new matrix by evaluating a code reference on each diagonal element of the | |
3574 | given matrix. The function is passed the element, the row index and the column | |
3575 | index, in that order. The value the function returns ( or the value of the last | |
3576 | executed statement ) is the value given to the corresponding element in $new_matrix. | |
3577 | ||
3578 | ||
3579 | =item * | |
3580 | ||
3581 | C<$matrix-E<gt>swap_col( $col1, $col2 );> | |
3582 | ||
3583 | This method takes two one-based column numbers and swaps the values of each element in each column. | |
3584 | C<$matrix-E<gt>swap_col(2,3)> would replace column 2 in $matrix with column 3, and replace column | |
3585 | 3 with column 2. | |
3586 | ||
3587 | =item * | |
3588 | ||
3589 | C<$matrix-E<gt>swap_row( $row1, $row2 );> | |
3590 | ||
3591 | This method takes two one-based row numbers and swaps the values of each element in each row. | |
3592 | C<$matrix-E<gt>swap_row(2,3)> would replace row 2 in $matrix with row 3, and replace row | |
3593 | 3 with row 2. | |
3594 | ||
3595 | ||
3596 | =head2 Matrix Operations | |
3597 | ||
3598 | =item * | |
3599 | ||
3600 | C<$det = $matrix-E<gt>det();> | |
3601 | ||
3602 | Returns the determinant of the matrix, without going through | |
3603 | the rigamarole of computing a LR decomposition. This method should | |
3604 | be much faster than LR decomposition if the matrix is diagonal or | |
3605 | triangular. Otherwise, it is just a wrapper for | |
3606 | C<$matrix-E<gt>decompose_LR-E<gt>det_LR>. If the determinant is zero, | |
3607 | there is no inverse and vice-versa. Only quadratic matrices have | |
3608 | determinants. | |
3609 | ||
3610 | =item * | |
3611 | ||
3612 | C<$inverse = $matrix-E<gt>inverse();> | |
3613 | ||
3614 | Returns the inverse of a matrix, without going through the | |
3615 | rigamarole of computing a LR decomposition. If no inverse exists, | |
3616 | undef is returned and an error is printed via C<carp()>. | |
3617 | This is nothing but a wrapper for C<$matrix-E<gt>decompose_LR-E<gt>invert_LR>. | |
3618 | ||
3619 | =item * | |
3620 | ||
3621 | C<($rows,$columns) = $matrix-E<gt>dim();> | |
3622 | ||
3623 | Returns a list of two items, representing the number of rows | |
3624 | and columns the given matrix "C<$matrix>" contains. | |
3625 | ||
3626 | =item * | |
3627 | ||
3628 | C<$norm_one = $matrix-E<gt>norm_one();> | |
3629 | ||
3630 | Returns the "one"-norm of the given matrix "C<$matrix>". | |
3631 | ||
3632 | The "one"-norm is defined as follows: | |
3633 | ||
3634 | For each column, the sum of the absolute values of the elements in the | |
3635 | different rows of that column is calculated. Finally, the maximum | |
3636 | of these sums is returned. | |
3637 | ||
3638 | Note that the "one"-norm and the "maximum"-norm are mathematically | |
3639 | equivalent, although for the same matrix they usually yield a different | |
3640 | value. | |
3641 | ||
3642 | Therefore, you should only compare values that have been calculated | |
3643 | using the same norm! | |
3644 | ||
3645 | Throughout this package, the "one"-norm is (arbitrarily) used | |
3646 | for all comparisons, for the sake of uniformity and comparability, | |
3647 | except for the iterative methods "solve_GSM()", "solve_SSM()" and | |
3648 | "solve_RM()" which use either norm depending on the matrix itself. | |
3649 | ||
3650 | =item * | |
3651 | ||
3652 | C<$norm_max = $matrix-E<gt>norm_max();> | |
3653 | ||
3654 | Returns the "maximum"-norm of the given matrix $matrix. | |
3655 | ||
3656 | The "maximum"-norm is defined as follows: | |
3657 | ||
3658 | For each row, the sum of the absolute values of the elements in the | |
3659 | different columns of that row is calculated. Finally, the maximum | |
3660 | of these sums is returned. | |
3661 | ||
3662 | Note that the "maximum"-norm and the "one"-norm are mathematically | |
3663 | equivalent, although for the same matrix they usually yield a different | |
3664 | value. | |
3665 | ||
3666 | Therefore, you should only compare values that have been calculated | |
3667 | using the same norm! | |
3668 | ||
3669 | Throughout this package, the "one"-norm is (arbitrarily) used | |
3670 | for all comparisons, for the sake of uniformity and comparability, | |
3671 | except for the iterative methods "solve_GSM()", "solve_SSM()" and | |
3672 | "solve_RM()" which use either norm depending on the matrix itself. | |
3673 | ||
3674 | =item * | |
3675 | ||
3676 | C<$norm_sum = $matrix-E<gt>norm_sum();> | |
3677 | ||
3678 | This is a very simple norm which is defined as the sum of the | |
3679 | absolute values of every element. | |
3680 | ||
3681 | =item * | |
3682 | ||
3683 | C<$p_norm> = $matrix-E<gt>norm_p($n);> | |
3684 | ||
3685 | This function returns the "p-norm" of a vector. The argument $n | |
3686 | must be a number greater than or equal to 1 or the string "Inf". | |
3687 | The p-norm is defined as (sum(x_i^p))^(1/p). In words, it raised | |
3688 | each element to the p-th power, adds them up, and then takes the | |
3689 | p-th root of that number. If the string "Inf" is passed, the | |
3690 | "infinity-norm" is computed, which is really the limit of the | |
3691 | p-norm as p goes to infinity. It is defined as the maximum element | |
3692 | of the vector. Also, note that the familiar Euclidean distance | |
3693 | between two vectors is just a special case of a p-norm, when p is | |
3694 | equal to 2. | |
3695 | ||
3696 | Example: | |
3697 | $a = Math::MatrixReal->new_from_cols([[1,2,3]]); | |
3698 | $p1 = $a->norm_p(1); | |
3699 | $p2 = $a->norm_p(2); | |
3700 | $p3 = $a->norm_p(3); | |
3701 | $pinf = $a->norm_p("Inf"); | |
3702 | ||
3703 | print "(1,2,3,Inf) norm:\n$p1\n$p2\n$p3\n$pinf\n"; | |
3704 | ||
3705 | $i1 = $a->new_from_rows([[1,0]]); | |
3706 | $i2 = $a->new_from_rows([[0,1]]); | |
3707 | ||
3708 | # this should be sqrt(2) since it is the same as the | |
3709 | # hypotenuse of a 1 by 1 right triangle | |
3710 | ||
3711 | $dist = ($i1-$i2)->norm_p(2); | |
3712 | print "Distance is $dist, which should be " . sqrt(2) . "\n"; | |
3713 | ||
3714 | Output: | |
3715 | ||
3716 | (1,2,3,Inf) norm: | |
3717 | 6 | |
3718 | 3.74165738677394139 | |
3719 | 3.30192724889462668 | |
3720 | 3 | |
3721 | ||
3722 | Distance is 1.41421356237309505, which should be 1.41421356237309505 | |
3723 | ||
3724 | ||
3725 | ||
3726 | =item * | |
3727 | ||
3728 | C<$frob_norm> = C<$matrix-E<gt>norm_frobenius();> | |
3729 | ||
3730 | This norm is similar to that of a p-norm where p is 2, except it | |
3731 | acts on a B<matrix>, not a vector. Each element of the matrix is | |
3732 | squared, this is added up, and then a square root is taken. | |
3733 | ||
3734 | =item * | |
3735 | ||
3736 | C<$matrix-E<gt>spectral_radius();> | |
3737 | ||
3738 | Returns the maximum value of the absolute value of all eigenvalues. | |
3739 | Currently this computes B<all> eigenvalues, then sifts through them | |
3740 | to find the largest in absolute value. Needless to say, this is very | |
3741 | inefficient, and in the future an algorithm that computes only the | |
3742 | largest eigenvalue may be implemented. | |
3743 | ||
3744 | =item * | |
3745 | ||
3746 | C<$matrix1-E<gt>transpose($matrix2);> | |
3747 | ||
3748 | Calculates the transposed matrix of matrix $matrix2 and stores | |
3749 | the result in matrix "C<$matrix1>" (which must already exist and have | |
3750 | the same size as matrix "C<$matrix2>"!). | |
3751 | ||
3752 | This operation can also be carried out "in-place", i.e., input and | |
3753 | output matrix may be identical. | |
3754 | ||
3755 | Transposition is a symmetry operation: imagine you rotate the matrix | |
3756 | along the axis of its main diagonal (going through elements (1,1), | |
3757 | (2,2), (3,3) and so on) by 180 degrees. | |
3758 | ||
3759 | Another way of looking at it is to say that rows and columns are | |
3760 | swapped. In fact the contents of element C<(i,j)> are swapped | |
3761 | with those of element C<(j,i)>. | |
3762 | ||
3763 | Note that (especially for vectors) it makes a big difference if you | |
3764 | have a row vector, like this: | |
3765 | ||
3766 | [ -1 0 1 ] | |
3767 | ||
3768 | or a column vector, like this: | |
3769 | ||
3770 | [ -1 ] | |
3771 | [ 0 ] | |
3772 | [ 1 ] | |
3773 | ||
3774 | the one vector being the transposed of the other! | |
3775 | ||
3776 | This is especially true for the matrix product of two vectors: | |
3777 | ||
3778 | [ -1 ] | |
3779 | [ -1 0 1 ] * [ 0 ] = [ 2 ] , whereas | |
3780 | [ 1 ] | |
3781 | ||
3782 | * [ -1 0 1 ] | |
3783 | [ -1 ] [ 1 0 -1 ] | |
3784 | [ 0 ] * [ -1 0 1 ] = [ -1 ] [ 1 0 -1 ] = [ 0 0 0 ] | |
3785 | [ 1 ] [ 0 ] [ 0 0 0 ] [ -1 0 1 ] | |
3786 | [ 1 ] [ -1 0 1 ] | |
3787 | ||
3788 | So be careful about what you really mean! | |
3789 | ||
3790 | Hint: throughout this module, whenever a vector is explicitly required | |
3791 | for input, a B<COLUMN> vector is expected! | |
3792 | ||
3793 | =item * | |
3794 | ||
3795 | C<$trace = $matrix-E<gt>trace();> | |
3796 | ||
3797 | This returns the trace of the matrix, which is defined as | |
3798 | the sum of the diagonal elements. The matrix must be | |
3799 | quadratic. | |
3800 | ||
3801 | =item * | |
3802 | ||
3803 | C<$minor = $matrix-E<gt>minor($row,$col);> | |
3804 | ||
3805 | Returns the minor matrix corresponding to $row and $col. $matrix must be quadratic. | |
3806 | If $matrix is n rows by n cols, the minor of $row and $col will be an (n-1) by (n-1) | |
3807 | matrix. The minor is defined as crossing out the row and the col specified and returning | |
3808 | the remaining rows and columns as a matrix. This method is used by C<cofactor()>. | |
3809 | ||
3810 | =item * | |
3811 | ||
3812 | C<$cofactor = $matrix-E<gt>cofactor();> | |
3813 | ||
3814 | The cofactor matrix is constructed as follows: | |
3815 | ||
3816 | For each element, cross out the row and column that it sits in. | |
3817 | Now, take the determinant of the matrix that is left in the other | |
3818 | rows and columns. | |
3819 | Multiply the determinant by (-1)^(i+j), where i is the row index, | |
3820 | and j is the column index. | |
3821 | Replace the given element with this value. | |
3822 | ||
3823 | The cofactor matrix can be used to find the inverse of the matrix. One formula for the | |
3824 | inverse of a matrix is the cofactor matrix transposed divided by the original | |
3825 | determinant of the matrix. | |
3826 | ||
3827 | The following two inverses should be exactly the same: | |
3828 | ||
3829 | my $inverse1 = $matrix->inverse; | |
3830 | my $inverse2 = ~($matrix->cofactor)->each( sub { (shift)/$matrix->det() } ); | |
3831 | ||
3832 | Caveat: Although the cofactor matrix is simple algorithm to compute the inverse of a matrix, and | |
3833 | can be used with pencil and paper for small matrices, it is comically slower than | |
3834 | the native C<inverse()> function. Here is a small benchmark: | |
3835 | ||
3836 | # $matrix1 is 15x15 | |
3837 | $det = $matrix1->det; | |
3838 | timethese( 10, | |
3839 | {'inverse' => sub { $matrix1->inverse(); }, | |
3840 | 'cofactor' => sub { (~$matrix1->cofactor)->each ( sub { (shift)/$det; } ) } | |
3841 | } ); | |
3842 | ||
3843 | ||
3844 | Benchmark: timing 10 iterations of LR, cofactor, inverse... | |
3845 | inverse: 1 wallclock secs ( 0.56 usr + 0.00 sys = 0.56 CPU) @ 17.86/s (n=10) | |
3846 | cofactor: 36 wallclock secs (36.62 usr + 0.01 sys = 36.63 CPU) @ 0.27/s (n=10) | |
3847 | ||
3848 | =item * | |
3849 | ||
3850 | C<$adjoint = $matrix-E<gt>adjoint();> | |
3851 | ||
3852 | The adjoint is just the transpose of the cofactor matrix. This method is | |
3853 | just an alias for C< ~($matrix-E<gt>cofactor)>. | |
3854 | ||
3855 | ||
3856 | =head2 Arithmetic Operations | |
3857 | ||
3858 | =item * | |
3859 | ||
3860 | C<$matrix1-E<gt>add($matrix2,$matrix3);> | |
3861 | ||
3862 | Calculates the sum of matrix "C<$matrix2>" and matrix "C<$matrix3>" | |
3863 | and stores the result in matrix "C<$matrix1>" (which must already exist | |
3864 | and have the same size as matrix "C<$matrix2>" and matrix "C<$matrix3>"!). | |
3865 | ||
3866 | This operation can also be carried out "in-place", i.e., the output and | |
3867 | one (or both) of the input matrices may be identical. | |
3868 | ||
3869 | =item * | |
3870 | ||
3871 | C<$matrix1-E<gt>subtract($matrix2,$matrix3);> | |
3872 | ||
3873 | Calculates the difference of matrix "C<$matrix2>" minus matrix "C<$matrix3>" | |
3874 | and stores the result in matrix "C<$matrix1>" (which must already exist | |
3875 | and have the same size as matrix "C<$matrix2>" and matrix "C<$matrix3>"!). | |
3876 | ||
3877 | This operation can also be carried out "in-place", i.e., the output and | |
3878 | one (or both) of the input matrices may be identical. | |
3879 | ||
3880 | Note that this operation is the same as | |
3881 | C<$matrix1-E<gt>add($matrix2,-$matrix3);>, although the latter is | |
3882 | a little less efficient. | |
3883 | ||
3884 | =item * | |
3885 | ||
3886 | C<$matrix1-E<gt>multiply_scalar($matrix2,$scalar);> | |
3887 | ||
3888 | Calculates the product of matrix "C<$matrix2>" and the number "C<$scalar>" | |
3889 | (i.e., multiplies each element of matrix "C<$matrix2>" with the factor | |
3890 | "C<$scalar>") and stores the result in matrix "C<$matrix1>" (which must | |
3891 | already exist and have the same size as matrix "C<$matrix2>"!). | |
3892 | ||
3893 | This operation can also be carried out "in-place", i.e., input and | |
3894 | output matrix may be identical. | |
3895 | ||
3896 | =item * | |
3897 | ||
3898 | C<$product_matrix = $matrix1-E<gt>multiply($matrix2);> | |
3899 | ||
3900 | Calculates the product of matrix "C<$matrix1>" and matrix "C<$matrix2>" | |
3901 | and returns an object reference to a new matrix "C<$product_matrix>" in | |
3902 | which the result of this operation has been stored. | |
3903 | ||
3904 | Note that the dimensions of the two matrices "C<$matrix1>" and "C<$matrix2>" | |
3905 | (i.e., their numbers of rows and columns) must harmonize in the following | |
3906 | way (example): | |
3907 | ||
3908 | [ 2 2 ] | |
3909 | [ 2 2 ] | |
3910 | [ 2 2 ] | |
3911 | ||
3912 | [ 1 1 1 ] [ * * ] | |
3913 | [ 1 1 1 ] [ * * ] | |
3914 | [ 1 1 1 ] [ * * ] | |
3915 | [ 1 1 1 ] [ * * ] | |
3916 | ||
3917 | I.e., the number of columns of matrix "C<$matrix1>" has to be the same | |
3918 | as the number of rows of matrix "C<$matrix2>". | |
3919 | ||
3920 | The number of rows and columns of the resulting matrix "C<$product_matrix>" | |
3921 | is determined by the number of rows of matrix "C<$matrix1>" and the number | |
3922 | of columns of matrix "C<$matrix2>", respectively. | |
3923 | ||
3924 | =item * | |
3925 | ||
3926 | C<$matrix1-E<gt>negate($matrix2);> | |
3927 | ||
3928 | Calculates the negative of matrix "C<$matrix2>" (i.e., multiplies | |
3929 | all elements with "-1") and stores the result in matrix "C<$matrix1>" | |
3930 | (which must already exist and have the same size as matrix "C<$matrix2>"!). | |
3931 | ||
3932 | This operation can also be carried out "in-place", i.e., input and | |
3933 | output matrix may be identical. | |
3934 | ||
3935 | ||
3936 | =item * | |
3937 | ||
3938 | C<$matrix_to_power = $matrix1-E<gt>exponent($integer);> | |
3939 | ||
3940 | Raises the matrix to the C<$integer> power. Obviously, C<$integer> must | |
3941 | be an integer. If it is zero, the identity matrix is returned. If a negative | |
3942 | integer is given, the inverse will be computed (if it exists) and then raised | |
3943 | the the absolute value of C<$integer>. The matrix must be quadratic. | |
3944 | ||
3945 | ||
3946 | =head2 Boolean Matrix Operations | |
3947 | ||
3948 | =item * | |
3949 | ||
3950 | C<$matrix-E<gt>is_quadratic();> | |
3951 | ||
3952 | Returns a boolean value indicating if the given matrix is | |
3953 | quadratic (also know as "square" or "n by n"). A matrix is | |
3954 | quadratic if it has the same number of rows as it does columns. | |
3955 | ||
3956 | =item * | |
3957 | ||
3958 | C<$matrix-E<gt>is_square();> | |
3959 | ||
3960 | This is an alias for C<is_quadratic()>. | |
3961 | ||
3962 | ||
3963 | =item * | |
3964 | ||
3965 | C<$matrix-E<gt>is_symmetric();> | |
3966 | ||
3967 | Returns a boolean value indicating if the given matrix is | |
3968 | symmetric. By definition, a matrix is symmetric if and only | |
3969 | if (B<M>[I<i>,I<j>]=B<M>[I<j>,I<i>]). This is equivalent to | |
3970 | C<($matrix == ~$matrix)> but without memory allocation. | |
3971 | Only quadratic matrices can be symmetric. | |
3972 | ||
3973 | Notes: A symmetric matrix always has real eigenvalues/eigenvectors. | |
3974 | A matrix plus its transpose is always symmetric. | |
3975 | ||
3976 | =item * | |
3977 | ||
3978 | C<$matrix-E<gt>is_skew_symmetric();> | |
3979 | ||
3980 | Returns a boolean value indicating if the given matrix is | |
3981 | skew symmetric. By definition, a matrix is symmetric if and only | |
3982 | if (B<M>[I<i>,I<j>]=B<-M>[I<j>,I<i>]). This is equivalent to | |
3983 | C<($matrix == -(~$matrix))> but without memory allocation. | |
3984 | Only quadratic matrices can be skew symmetric. | |
3985 | ||
3986 | ||
3987 | =item * | |
3988 | ||
3989 | C<$matrix-E<gt>is_diagonal();> | |
3990 | ||
3991 | Returns a boolean value indicating if the given matrix is | |
3992 | diagonal, i.e. all of the nonzero elements are on the main diagonal. | |
3993 | Only quadratic matrices can be diagonal. | |
3994 | ||
3995 | =item * | |
3996 | ||
3997 | C<$matrix-E<gt>is_tridiagonal();> | |
3998 | ||
3999 | Returns a boolean value indicating if the given matrix is | |
4000 | tridiagonal, i.e. all of the nonzero elements are on the main diagonal | |
4001 | or the diagonals above and below the main diagonal. | |
4002 | Only quadratic matrices can be tridiagonal. | |
4003 | ||
4004 | ||
4005 | =item * | |
4006 | ||
4007 | C<$matrix-E<gt>is_upper_triangular();> | |
4008 | ||
4009 | Returns a boolean value indicating if the given matrix is upper triangular, | |
4010 | i.e. all of the nonzero elements not on the main diagonal are above it. | |
4011 | Only quadratic matrices can be upper triangular. | |
4012 | Note: diagonal matrices are both upper and lower triangular. | |
4013 | ||
4014 | ||
4015 | =item * | |
4016 | ||
4017 | C<$matrix-E<gt>is_lower_triangular();> | |
4018 | ||
4019 | Returns a boolean value indicating if the given matrix is lower triangular, | |
4020 | i.e. all of the nonzero elements not on the main diagonal are below it. | |
4021 | Only quadratic matrices can be lower triangular. | |
4022 | Note: diagonal matrices are both upper and lower triangular. | |
4023 | ||
4024 | =item * | |
4025 | ||
4026 | C<$matrix-E<gt>is_orthogonal();> | |
4027 | ||
4028 | Returns a boolean value indicating if the given matrix is orthogonal. | |
4029 | An orthogonal matrix is has the property that the transpose equals the | |
4030 | inverse of the matrix. Instead of computing each and comparing them, this | |
4031 | method multiplies the matrix by it's transpose, and returns true if this | |
4032 | turns out to be the identity matrix, false otherwise. | |
4033 | Only quadratic matrices can orthogonal. | |
4034 | ||
4035 | =item * | |
4036 | ||
4037 | C<$matrix-E<gt>is_binary();> | |
4038 | ||
4039 | Returns a boolean value indicating if the given matrix is binary. | |
4040 | A matrix is binary if it contains only zeroes or ones. | |
4041 | ||
4042 | =item * | |
4043 | ||
4044 | C<$matrix-E<gt>is_gramian();> | |
4045 | ||
4046 | Returns a boolean value indicating if the give matrix is Gramian. | |
4047 | A matrix C<$A> is Gramian if and only if there exists a | |
4048 | square matrix C<$B> such that C<$A = ~$B*$B>. This is equivalent to | |
4049 | checking if C<$A> is symmetric and has all nonnegative eigenvalues, which | |
4050 | is what Math::MatrixReal uses to check for this property. | |
4051 | ||
4052 | =item * | |
4053 | ||
4054 | C<$matrix-E<gt>is_LR();> | |
4055 | ||
4056 | Returns a boolean value indicating if the matrix is an LR decomposition | |
4057 | matrix. | |
4058 | ||
4059 | =item * | |
4060 | ||
4061 | C<$matrix-E<gt>is_positive();> | |
4062 | ||
4063 | Returns a boolean value indicating if the matrix contains only | |
4064 | positive entries. Note that a zero entry is not positive and | |
4065 | will cause C<is_positive()> to return false. | |
4066 | ||
4067 | =item * | |
4068 | ||
4069 | C<$matrix-E<gt>is_negative();> | |
4070 | ||
4071 | Returns a boolean value indicating if the matrix contains only | |
4072 | negative entries. Note that a zero entry is not negative and | |
4073 | will cause C<is_negative()> to return false. | |
4074 | ||
4075 | =item * | |
4076 | ||
4077 | C<$matrix-E<gt>is_periodic($k);> | |
4078 | ||
4079 | Returns a boolean value indicating if the matrix is periodic | |
4080 | with period $k. This is true if C<$matrix ** ($k+1) == $matrix>. | |
4081 | When C<$k == 1>, this reduces down to the C<is_idempotent()> | |
4082 | function. | |
4083 | ||
4084 | =item * | |
4085 | ||
4086 | C<$matrix-E<gt>is_idempotent();> | |
4087 | ||
4088 | Returns a boolean value indicating if the matrix is idempotent, | |
4089 | which is defined as the square of the matrix being equal to | |
4090 | the original matrix, i.e C<$matrix ** 2 == $matrix>. | |
4091 | ||
4092 | =item * | |
4093 | ||
4094 | C<$matrix-E<gt>is_row_vector();> | |
4095 | ||
4096 | Returns a boolean value indicating if the matrix is a row vector. | |
4097 | A row vector is a matrix which is 1xn. Note that the 1x1 matrix is | |
4098 | both a row and column vector. | |
4099 | ||
4100 | =item * | |
4101 | ||
4102 | C<$matrix-E<gt>is_col_vector();> | |
4103 | ||
4104 | Returns a boolean value indicating if the matrix is a col vector. | |
4105 | A col vector is a matrix which is nx1. Note that the 1x1 matrix is | |
4106 | both a row and column vector. | |
4107 | ||
4108 | =head2 Eigensystems | |
4109 | ||
4110 | =over 2 | |
4111 | ||
4112 | =item * | |
4113 | ||
4114 | C<($l, $V) = $matrix-E<gt>sym_diagonalize();> | |
4115 | ||
4116 | This method performs the diagonalization of the quadratic | |
4117 | I<symmetric> matrix B<M> stored in $matrix. | |
4118 | On output, B<l> is a column vector containing all the eigenvalues | |
4119 | of B<M> and B<V> is an orthogonal matrix which columns are the | |
4120 | corresponding normalized eigenvectors. | |
4121 | The primary property of an eigenvalue I<l> and an eigenvector | |
4122 | B<x> is of course that: B<M> * B<x> = I<l> * B<x>. | |
4123 | ||
4124 | The method uses a Householder reduction to tridiagonal form | |
4125 | followed by a QL algoritm with implicit shifts on this | |
4126 | tridiagonal. (The tridiagonal matrix is kept internally | |
4127 | in a compact form in this routine to save memory.) | |
4128 | In fact, this routine wraps the householder() and | |
4129 | tri_diagonalize() methods described below when their | |
4130 | intermediate results are not desired. | |
4131 | The overall algorithmic complexity of this technique | |
4132 | is O(N^3). According to several books, the coefficient | |
4133 | hidden by the 'O' is one of the best possible for general | |
4134 | (symmetric) matrixes. | |
4135 | ||
4136 | =item * | |
4137 | ||
4138 | C<($T, $Q) = $matrix-E<gt>householder();> | |
4139 | ||
4140 | This method performs the Householder algorithm which reduces | |
4141 | the I<n> by I<n> real I<symmetric> matrix B<M> contained | |
4142 | in $matrix to tridiagonal form. | |
4143 | On output, B<T> is a symmetric tridiagonal matrix (only | |
4144 | diagonal and off-diagonal elements are non-zero) and B<Q> | |
4145 | is an I<orthogonal> matrix performing the tranformation | |
4146 | between B<M> and B<T> (C<$M == $Q * $T * ~$Q>). | |
4147 | ||
4148 | =item * | |
4149 | ||
4150 | C<($l, $V) = $T-E<gt>tri_diagonalize([$Q]);> | |
4151 | ||
4152 | This method diagonalizes the symmetric tridiagonal | |
4153 | matrix B<T>. On output, $l and $V are similar to the | |
4154 | output values described for sym_diagonalize(). | |
4155 | ||
4156 | The optional argument $Q corresponds to an orthogonal | |
4157 | transformation matrix B<Q> that should be used additionally | |
4158 | during B<V> (eigenvectors) computation. It should be supplied | |
4159 | if the desired eigenvectors correspond to a more general | |
4160 | symmetric matrix B<M> previously reduced by the | |
4161 | householder() method, not a mere tridiagonal. If B<T> is | |
4162 | really a tridiagonal matrix, B<Q> can be omitted (it | |
4163 | will be internally created in fact as an identity matrix). | |
4164 | The method uses a QL algorithm (with implicit shifts). | |
4165 | ||
4166 | =item * | |
4167 | ||
4168 | C<$l = $matrix-E<gt>sym_eigenvalues();> | |
4169 | ||
4170 | This method computes the eigenvalues of the quadratic | |
4171 | I<symmetric> matrix B<M> stored in $matrix. | |
4172 | On output, B<l> is a column vector containing all the eigenvalues | |
4173 | of B<M>. Eigenvectors are not computed (on the contrary of | |
4174 | C<sym_diagonalize()>) and this method is more efficient | |
4175 | (even though it uses a similar algorithm with two phases). | |
4176 | However, understand that the algorithmic complexity of this | |
4177 | technique is still also O(N^3). But the coefficient hidden | |
4178 | by the 'O' is better by a factor of..., well, see your | |
4179 | benchmark, it's wiser. | |
4180 | ||
4181 | This routine wraps the householder_tridiagonal() and | |
4182 | tri_eigenvalues() methods described below when the | |
4183 | intermediate tridiagonal matrix is not needed. | |
4184 | ||
4185 | =item * | |
4186 | ||
4187 | C<$T = $matrix-E<gt>householder_tridiagonal();> | |
4188 | ||
4189 | This method performs the Householder algorithm which reduces | |
4190 | the I<n> by I<n> real I<symmetric> matrix B<M> contained | |
4191 | in $matrix to tridiagonal form. | |
4192 | On output, B<T> is the obtained symmetric tridiagonal matrix | |
4193 | (only diagonal and off-diagonal elements are non-zero). The | |
4194 | operation is similar to the householder() method, but potentially | |
4195 | a little more efficient as the transformation matrix is not | |
4196 | computed. | |
4197 | ||
4198 | =item * | |
4199 | ||
4200 | C<$l = $T-E<gt>tri_eigenvalues();> | |
4201 | ||
4202 | This method computesthe eigenvalues of the symmetric | |
4203 | tridiagonal matrix B<T>. On output, $l is a vector | |
4204 | containing the eigenvalues (similar to C<sym_eigenvalues()>). | |
4205 | This method is much more efficient than tri_diagonalize() | |
4206 | when eigenvectors are not needed. | |
4207 | ||
4208 | =back | |
4209 | ||
4210 | =head2 Miscellaneous | |
4211 | ||
4212 | =item * | |
4213 | ||
4214 | C<$matrix-E<gt>zero();> | |
4215 | ||
4216 | Assigns a zero to every element of the matrix "C<$matrix>", i.e., | |
4217 | erases all values previously stored there, thereby effectively | |
4218 | transforming the matrix into a "zero"-matrix or "null"-matrix, | |
4219 | the neutral element of the addition operation in a Ring. | |
4220 | ||
4221 | (For instance the (quadratic) matrices with "n" rows and columns | |
4222 | and matrix addition and multiplication form a Ring. Most prominent | |
4223 | characteristic of a Ring is that multiplication is not commutative, | |
4224 | i.e., in general, "C<matrix1 * matrix2>" is not the same as | |
4225 | "C<matrix2 * matrix1>"!) | |
4226 | ||
4227 | =item * | |
4228 | ||
4229 | C<$matrix-E<gt>one();> | |
4230 | ||
4231 | Assigns one's to the elements on the main diagonal (elements (1,1), | |
4232 | (2,2), (3,3) and so on) of matrix "C<$matrix>" and zero's to all others, | |
4233 | thereby erasing all values previously stored there and transforming the | |
4234 | matrix into a "one"-matrix, the neutral element of the multiplication | |
4235 | operation in a Ring. | |
4236 | ||
4237 | (If the matrix is quadratic (which this method doesn't require, though), | |
4238 | then multiplying this matrix with itself yields this same matrix again, | |
4239 | and multiplying it with some other matrix leaves that other matrix | |
4240 | unchanged!) | |
4241 | ||
4242 | =item * | |
4243 | ||
4244 | C<$latex_string = $matrix-E<gt>as_latex( align=E<gt> "c", format =E<gt> "%s", name =E<gt> "" );> | |
4245 | ||
4246 | This function returns the matrix as a LaTeX string. It takes a hash as an | |
4247 | argument which is used to control the style of the output. The hash element C<align> | |
4248 | may be "c","l" or "r", corresponding to center, left and right, respectively. The | |
4249 | C<format> element is a format string that is given to C<sprintf> to control the | |
4250 | style of number format, such a floating point or scientific notation. The C<name> | |
4251 | element can be used so that a LaTeX string of "$name = " is prepended to the string. | |
4252 | ||
4253 | Example: | |
4254 | ||
4255 | my $a = Math::MatrixReal->new_from_cols([[ 1.234, 5.678, 9.1011],[1,2,3]] ); | |
4256 | print $a->as_latex( ( format => "%.2f", align => "l",name => "A" ) ); | |
4257 | ||
4258 | Output: | |
4259 | $A = $ $ | |
4260 | \left( \begin{array}{ll} | |
4261 | 1.23&1.00 \\ | |
4262 | 5.68&2.00 \\ | |
4263 | 9.10&3.00 | |
4264 | \end{array} \right) | |
4265 | $ | |
4266 | ||
4267 | =item * | |
4268 | ||
4269 | C<$yacas_string = $matrix-E<gt>as_yacas( format =E<gt> "%s", name =E<gt> "", semi =E<gt> 0 );> | |
4270 | ||
4271 | This function returns the matrix as a string that can be read by Yacas. | |
4272 | It takes a hash as | |
4273 | an an argument which controls the style of the output. The | |
4274 | C<format> element is a format string that is given to C<sprintf> to control the | |
4275 | style of number format, such a floating point or scientific notation. The C<name> | |
4276 | element can be used so that "$name = " is prepended to the string. The <semi> element can | |
4277 | be set to 1 to that a semicolon is appended (so Matlab does not print out the matrix.) | |
4278 | ||
4279 | Example: | |
4280 | ||
4281 | $a = Math::MatrixReal->new_from_cols([[ 1.234, 5.678, 9.1011],[1,2,3]] ); | |
4282 | print $a->as_yacas( ( format => "%.2f", align => "l",name => "A" ) ); | |
4283 | ||
4284 | Output: | |
4285 | ||
4286 | A := {{1.23,1.00},{5.68,2.00},{9.10,3.00}} | |
4287 | ||
4288 | =item * | |
4289 | ||
4290 | C<$matlab_string = $matrix-E<gt>as_matlab( format =E<gt> "%s", name =E<gt> "", semi =E<gt> 0 );> | |
4291 | ||
4292 | This function returns the matrix as a string that can be read by Matlab. It takes a hash as | |
4293 | an an argument which controls the style of the output. The | |
4294 | C<format> element is a format string that is given to C<sprintf> to control the | |
4295 | style of number format, such a floating point or scientific notation. The C<name> | |
4296 | element can be used so that "$name = " is prepended to the string. The <semi> element can | |
4297 | be set to 1 to that a semicolon is appended (so Matlab does not print out the matrix.) | |
4298 | ||
4299 | Example: | |
4300 | ||
4301 | my $a = Math::MatrixReal->new_from_rows([[ 1.234, 5.678, 9.1011],[1,2,3]] ); | |
4302 | print $a->as_matlab( ( format => "%.3f", name => "A",semi => 1 ) ); | |
4303 | ||
4304 | Output: | |
4305 | A = [ 1.234 5.678 9.101; | |
4306 | 1.000 2.000 3.000]; | |
4307 | ||
4308 | ||
4309 | =item * | |
4310 | ||
4311 | C<$scilab_string = $matrix-E<gt>as_scilab( format =E<gt> "%s", name =E<gt> "", semi =E<gt> 0 );> | |
4312 | ||
4313 | This function is just an alias for C<as_matlab()>, since both Scilab and Matlab have the | |
4314 | same matrix format. | |
4315 | ||
4316 | =item * | |
4317 | ||
4318 | C<$minimum = Math::MatrixReal::min($number1,$number2);> | |
4319 | ||
4320 | Returns the minimum of the two numbers "C<number1>" and "C<number2>". | |
4321 | ||
4322 | =item * | |
4323 | ||
4324 | C<$maximum = Math::MatrixReal::max($number1,$number2);> | |
4325 | ||
4326 | Returns the maximum of the two numbers "C<number1>" and "C<number2>". | |
4327 | ||
4328 | =item * | |
4329 | ||
4330 | C<$minimal_cost_matrix = $cost_matrix-E<gt>kleene();> | |
4331 | ||
4332 | Copies the matrix "C<$cost_matrix>" (which has to be quadratic!) to | |
4333 | a new matrix of the same size (i.e., "clones" the input matrix) and | |
4334 | applies Kleene's algorithm to it. | |
4335 | ||
4336 | See L<Math::Kleene(3)> for more details about this algorithm! | |
4337 | ||
4338 | The method returns an object reference to the new matrix. | |
4339 | ||
4340 | Matrix "C<$cost_matrix>" is not changed by this method in any way. | |
4341 | ||
4342 | =item * | |
4343 | ||
4344 | C<($norm_matrix,$norm_vector) = $matrix-E<gt>normalize($vector);> | |
4345 | ||
4346 | This method is used to improve the numerical stability when solving | |
4347 | linear equation systems. | |
4348 | ||
4349 | Suppose you have a matrix "A" and a vector "b" and you want to find | |
4350 | out a vector "x" so that C<A * x = b>, i.e., the vector "x" which | |
4351 | solves the equation system represented by the matrix "A" and the | |
4352 | vector "b". | |
4353 | ||
4354 | Applying this method to the pair (A,b) yields a pair (A',b') where | |
4355 | each row has been divided by (the absolute value of) the greatest | |
4356 | coefficient appearing in that row. So this coefficient becomes equal | |
4357 | to "1" (or "-1") in the new pair (A',b') (all others become smaller | |
4358 | than one and greater than minus one). | |
4359 | ||
4360 | Note that this operation does not change the equation system itself | |
4361 | because the same division is carried out on either side of the equation | |
4362 | sign! | |
4363 | ||
4364 | The method requires a quadratic (!) matrix "C<$matrix>" and a vector | |
4365 | "C<$vector>" for input (the vector must be a column vector with the same | |
4366 | number of rows as the input matrix) and returns a list of two items | |
4367 | which are object references to a new matrix and a new vector, in this | |
4368 | order. | |
4369 | ||
4370 | The output matrix and vector are clones of the input matrix and vector | |
4371 | to which the operation explained above has been applied. | |
4372 | ||
4373 | The input matrix and vector are not changed by this in any way. | |
4374 | ||
4375 | Example of how this method can affect the result of the methods to solve | |
4376 | equation systems (explained immediately below following this method): | |
4377 | ||
4378 | Consider the following little program: | |
4379 | ||
4380 | #!perl -w | |
4381 | ||
4382 | use Math::MatrixReal qw(new_from_string); | |
4383 | ||
4384 | $A = Math::MatrixReal->new_from_string(<<"MATRIX"); | |
4385 | [ 1 2 3 ] | |
4386 | [ 5 7 11 ] | |
4387 | [ 23 19 13 ] | |
4388 | MATRIX | |
4389 | ||
4390 | $b = Math::MatrixReal->new_from_string(<<"MATRIX"); | |
4391 | [ 0 ] | |
4392 | [ 1 ] | |
4393 | [ 29 ] | |
4394 | MATRIX | |
4395 | ||
4396 | $LR = $A->decompose_LR(); | |
4397 | if (($dim,$x,$B) = $LR->solve_LR($b)) | |
4398 | { | |
4399 | $test = $A * $x; | |
4400 | print "x = \n$x"; | |
4401 | print "A * x = \n$test"; | |
4402 | } | |
4403 | ||
4404 | ($A_,$b_) = $A->normalize($b); | |
4405 | ||
4406 | $LR = $A_->decompose_LR(); | |
4407 | if (($dim,$x,$B) = $LR->solve_LR($b_)) | |
4408 | { | |
4409 | $test = $A * $x; | |
4410 | print "x = \n$x"; | |
4411 | print "A * x = \n$test"; | |
4412 | } | |
4413 | ||
4414 | This will print: | |
4415 | ||
4416 | x = | |
4417 | [ 1.000000000000E+00 ] | |
4418 | [ 1.000000000000E+00 ] | |
4419 | [ -1.000000000000E+00 ] | |
4420 | A * x = | |
4421 | [ 4.440892098501E-16 ] | |
4422 | [ 1.000000000000E+00 ] | |
4423 | [ 2.900000000000E+01 ] | |
4424 | x = | |
4425 | [ 1.000000000000E+00 ] | |
4426 | [ 1.000000000000E+00 ] | |
4427 | [ -1.000000000000E+00 ] | |
4428 | A * x = | |
4429 | [ 0.000000000000E+00 ] | |
4430 | [ 1.000000000000E+00 ] | |
4431 | [ 2.900000000000E+01 ] | |
4432 | ||
4433 | You can see that in the second example (where "normalize()" has been used), | |
4434 | the result is "better", i.e., more accurate! | |
4435 | ||
4436 | =item * | |
4437 | ||
4438 | C<$LR_matrix = $matrix-E<gt>decompose_LR();> | |
4439 | ||
4440 | This method is needed to solve linear equation systems. | |
4441 | ||
4442 | Suppose you have a matrix "A" and a vector "b" and you want to find | |
4443 | out a vector "x" so that C<A * x = b>, i.e., the vector "x" which | |
4444 | solves the equation system represented by the matrix "A" and the | |
4445 | vector "b". | |
4446 | ||
4447 | You might also have a matrix "A" and a whole bunch of different | |
4448 | vectors "b1".."bk" for which you need to find vectors "x1".."xk" | |
4449 | so that C<A * xi = bi>, for C<i=1..k>. | |
4450 | ||
4451 | Using Gaussian transformations (multiplying a row or column with | |
4452 | a factor, swapping two rows or two columns and adding a multiple | |
4453 | of one row or column to another), it is possible to decompose any | |
4454 | matrix "A" into two triangular matrices, called "L" and "R" (for | |
4455 | "Left" and "Right"). | |
4456 | ||
4457 | "L" has one's on the main diagonal (the elements (1,1), (2,2), (3,3) | |
4458 | and so so), non-zero values to the left and below of the main diagonal | |
4459 | and all zero's in the upper right half of the matrix. | |
4460 | ||
4461 | "R" has non-zero values on the main diagonal as well as to the right | |
4462 | and above of the main diagonal and all zero's in the lower left half | |
4463 | of the matrix, as follows: | |
4464 | ||
4465 | [ 1 0 0 0 0 ] [ x x x x x ] | |
4466 | [ x 1 0 0 0 ] [ 0 x x x x ] | |
4467 | L = [ x x 1 0 0 ] R = [ 0 0 x x x ] | |
4468 | [ x x x 1 0 ] [ 0 0 0 x x ] | |
4469 | [ x x x x 1 ] [ 0 0 0 0 x ] | |
4470 | ||
4471 | Note that "C<L * R>" is equivalent to matrix "A" in the sense that | |
4472 | C<L * R * x = b E<lt>==E<gt> A * x = b> for all vectors "x", leaving | |
4473 | out of account permutations of the rows and columns (these are taken | |
4474 | care of "magically" by this module!) and numerical errors. | |
4475 | ||
4476 | Trick: | |
4477 | ||
4478 | Because we know that "L" has one's on its main diagonal, we can | |
4479 | store both matrices together in the same array without information | |
4480 | loss! I.e., | |
4481 | ||
4482 | [ R R R R R ] | |
4483 | [ L R R R R ] | |
4484 | LR = [ L L R R R ] | |
4485 | [ L L L R R ] | |
4486 | [ L L L L R ] | |
4487 | ||
4488 | Beware, though, that "LR" and "C<L * R>" are not the same!!! | |
4489 | ||
4490 | Note also that for the same reason, you cannot apply the method "normalize()" | |
4491 | to an "LR" decomposition matrix. Trying to do so will yield meaningless | |
4492 | rubbish! | |
4493 | ||
4494 | (You need to apply "normalize()" to each pair (Ai,bi) B<BEFORE> decomposing | |
4495 | the matrix "Ai'"!) | |
4496 | ||
4497 | Now what does all this help us in solving linear equation systems? | |
4498 | ||
4499 | It helps us because a triangular matrix is the next best thing | |
4500 | that can happen to us besides a diagonal matrix (a matrix that | |
4501 | has non-zero values only on its main diagonal - in which case | |
4502 | the solution is trivial, simply divide "C<b[i]>" by "C<A[i,i]>" | |
4503 | to get "C<x[i]>"!). | |
4504 | ||
4505 | To find the solution to our problem "C<A * x = b>", we divide this | |
4506 | problem in parts: instead of solving C<A * x = b> directly, we first | |
4507 | decompose "A" into "L" and "R" and then solve "C<L * y = b>" and | |
4508 | finally "C<R * x = y>" (motto: divide and rule!). | |
4509 | ||
4510 | From the illustration above it is clear that solving "C<L * y = b>" | |
4511 | and "C<R * x = y>" is straightforward: we immediately know that | |
4512 | C<y[1] = b[1]>. We then deduce swiftly that | |
4513 | ||
4514 | y[2] = b[2] - L[2,1] * y[1] | |
4515 | ||
4516 | (and we know "C<y[1]>" by now!), that | |
4517 | ||
4518 | y[3] = b[3] - L[3,1] * y[1] - L[3,2] * y[2] | |
4519 | ||
4520 | and so on. | |
4521 | ||
4522 | Having effortlessly calculated the vector "y", we now proceed to | |
4523 | calculate the vector "x" in a similar fashion: we see immediately | |
4524 | that C<x[n] = y[n] / R[n,n]>. It follows that | |
4525 | ||
4526 | x[n-1] = ( y[n-1] - R[n-1,n] * x[n] ) / R[n-1,n-1] | |
4527 | ||
4528 | and | |
4529 | ||
4530 | x[n-2] = ( y[n-2] - R[n-2,n-1] * x[n-1] - R[n-2,n] * x[n] ) | |
4531 | / R[n-2,n-2] | |
4532 | ||
4533 | and so on. | |
4534 | ||
4535 | You can see that - especially when you have many vectors "b1".."bk" | |
4536 | for which you are searching solutions to C<A * xi = bi> - this scheme | |
4537 | is much more efficient than a straightforward, "brute force" approach. | |
4538 | ||
4539 | This method requires a quadratic matrix as its input matrix. | |
4540 | ||
4541 | If you don't have that many equations, fill up with zero's (i.e., do | |
4542 | nothing to fill the superfluous rows if it's a "fresh" matrix, i.e., | |
4543 | a matrix that has been created with "new()" or "shadow()"). | |
4544 | ||
4545 | The method returns an object reference to a new matrix containing the | |
4546 | matrices "L" and "R". | |
4547 | ||
4548 | The input matrix is not changed by this method in any way. | |
4549 | ||
4550 | Note that you can "copy()" or "clone()" the result of this method without | |
4551 | losing its "magical" properties (for instance concerning the hidden | |
4552 | permutations of its rows and columns). | |
4553 | ||
4554 | However, as soon as you are applying any method that alters the contents | |
4555 | of the matrix, its "magical" properties are stripped off, and the matrix | |
4556 | immediately reverts to an "ordinary" matrix (with the values it just happens | |
4557 | to contain at that moment, be they meaningful as an ordinary matrix or not!). | |
4558 | ||
4559 | =item * | |
4560 | ||
4561 | C<($dimension,$x_vector,$base_matrix) = $LR_matrix>C<-E<gt>>C<solve_LR($b_vector);> | |
4562 | ||
4563 | Use this method to actually solve an equation system. | |
4564 | ||
4565 | Matrix "C<$LR_matrix>" must be a (quadratic) matrix returned by the | |
4566 | method "decompose_LR()", the LR decomposition matrix of the matrix | |
4567 | "A" of your equation system C<A * x = b>. | |
4568 | ||
4569 | The input vector "C<$b_vector>" is the vector "b" in your equation system | |
4570 | C<A * x = b>, which must be a column vector and have the same number of | |
4571 | rows as the input matrix "C<$LR_matrix>". | |
4572 | ||
4573 | The method returns a list of three items if a solution exists or an | |
4574 | empty list otherwise (!). | |
4575 | ||
4576 | Therefore, you should always use this method like this: | |
4577 | ||
4578 | if ( ($dim,$x_vec,$base) = $LR->solve_LR($b_vec) ) | |
4579 | { | |
4580 | # do something with the solution... | |
4581 | } | |
4582 | else | |
4583 | { | |
4584 | # do something with the fact that there is no solution... | |
4585 | } | |
4586 | ||
4587 | The three items returned are: the dimension "C<$dimension>" of the solution | |
4588 | space (which is zero if only one solution exists, one if the solution is | |
4589 | a straight line, two if the solution is a plane, and so on), the solution | |
4590 | vector "C<$x_vector>" (which is the vector "x" of your equation system | |
4591 | C<A * x = b>) and a matrix "C<$base_matrix>" representing a base of the | |
4592 | solution space (a set of vectors which put up the solution space like | |
4593 | the spokes of an umbrella). | |
4594 | ||
4595 | Only the first "C<$dimension>" columns of this base matrix actually | |
4596 | contain entries, the remaining columns are all zero. | |
4597 | ||
4598 | Now what is all this stuff with that "base" good for? | |
4599 | ||
4600 | The output vector "x" is B<ALWAYS> a solution of your equation system | |
4601 | C<A * x = b>. | |
4602 | ||
4603 | But also any vector "C<$vector>" | |
4604 | ||
4605 | $vector = $x_vector->clone(); | |
4606 | ||
4607 | $machine_infinity = 1E+99; # or something like that | |
4608 | ||
4609 | for ( $i = 1; $i <= $dimension; $i++ ) | |
4610 | { | |
4611 | $vector += rand($machine_infinity) * $base_matrix->column($i); | |
4612 | } | |
4613 | ||
4614 | is a solution to your problem C<A * x = b>, i.e., if "C<$A_matrix>" contains | |
4615 | your matrix "A", then | |
4616 | ||
4617 | print abs( $A_matrix * $vector - $b_vector ), "\n"; | |
4618 | ||
4619 | should print a number around 1E-16 or so! | |
4620 | ||
4621 | By the way, note that you can actually calculate those vectors "C<$vector>" | |
4622 | a little more efficient as follows: | |
4623 | ||
4624 | $rand_vector = $x_vector->shadow(); | |
4625 | ||
4626 | $machine_infinity = 1E+99; # or something like that | |
4627 | ||
4628 | for ( $i = 1; $i <= $dimension; $i++ ) | |
4629 | { | |
4630 | $rand_vector->assign($i,1, rand($machine_infinity) ); | |
4631 | } | |
4632 | ||
4633 | $vector = $x_vector + ( $base_matrix * $rand_vector ); | |
4634 | ||
4635 | Note that the input matrix and vector are not changed by this method | |
4636 | in any way. | |
4637 | ||
4638 | =item * | |
4639 | ||
4640 | C<$inverse_matrix = $LR_matrix-E<gt>invert_LR();> | |
4641 | ||
4642 | Use this method to calculate the inverse of a given matrix "C<$LR_matrix>", | |
4643 | which must be a (quadratic) matrix returned by the method "decompose_LR()". | |
4644 | ||
4645 | The method returns an object reference to a new matrix of the same size as | |
4646 | the input matrix containing the inverse of the matrix that you initially | |
4647 | fed into "decompose_LR()" B<IF THE INVERSE EXISTS>, or an empty list | |
4648 | otherwise. | |
4649 | ||
4650 | Therefore, you should always use this method in the following way: | |
4651 | ||
4652 | if ( $inverse_matrix = $LR->invert_LR() ) | |
4653 | { | |
4654 | # do something with the inverse matrix... | |
4655 | } | |
4656 | else | |
4657 | { | |
4658 | # do something with the fact that there is no inverse matrix... | |
4659 | } | |
4660 | ||
4661 | Note that by definition (disregarding numerical errors), the product | |
4662 | of the initial matrix and its inverse (or vice-versa) is always a matrix | |
4663 | containing one's on the main diagonal (elements (1,1), (2,2), (3,3) and | |
4664 | so on) and zero's elsewhere. | |
4665 | ||
4666 | The input matrix is not changed by this method in any way. | |
4667 | ||
4668 | =item * | |
4669 | ||
4670 | C<$condition = $matrix-E<gt>condition($inverse_matrix);> | |
4671 | ||
4672 | In fact this method is just a shortcut for | |
4673 | ||
4674 | abs($matrix) * abs($inverse_matrix) | |
4675 | ||
4676 | Both input matrices must be quadratic and have the same size, and the result | |
4677 | is meaningful only if one of them is the inverse of the other (for instance, | |
4678 | as returned by the method "invert_LR()"). | |
4679 | ||
4680 | The number returned is a measure of the "condition" of the given matrix | |
4681 | "C<$matrix>", i.e., a measure of the numerical stability of the matrix. | |
4682 | ||
4683 | This number is always positive, and the smaller its value, the better the | |
4684 | condition of the matrix (the better the stability of all subsequent | |
4685 | computations carried out using this matrix). | |
4686 | ||
4687 | Numerical stability means for example that if | |
4688 | ||
4689 | abs( $vec_correct - $vec_with_error ) < $epsilon | |
4690 | ||
4691 | holds, there must be a "C<$delta>" which doesn't depend on the vector | |
4692 | "C<$vec_correct>" (nor "C<$vec_with_error>", by the way) so that | |
4693 | ||
4694 | abs( $matrix * $vec_correct - $matrix * $vec_with_error ) < $delta | |
4695 | ||
4696 | also holds. | |
4697 | ||
4698 | =item * | |
4699 | ||
4700 | C<$determinant = $LR_matrix-E<gt>det_LR();> | |
4701 | ||
4702 | Calculates the determinant of a matrix, whose LR decomposition matrix | |
4703 | "C<$LR_matrix>" must be given (which must be a (quadratic) matrix | |
4704 | returned by the method "decompose_LR()"). | |
4705 | ||
4706 | In fact the determinant is a by-product of the LR decomposition: It is | |
4707 | (in principle, that is, except for the sign) simply the product of the | |
4708 | elements on the main diagonal (elements (1,1), (2,2), (3,3) and so on) | |
4709 | of the LR decomposition matrix. | |
4710 | ||
4711 | (The sign is taken care of "magically" by this module) | |
4712 | ||
4713 | =item * | |
4714 | ||
4715 | C<$order = $LR_matrix-E<gt>order_LR();> | |
4716 | ||
4717 | Calculates the order (called "Rang" in German) of a matrix, whose | |
4718 | LR decomposition matrix "C<$LR_matrix>" must be given (which must | |
4719 | be a (quadratic) matrix returned by the method "decompose_LR()"). | |
4720 | ||
4721 | This number is a measure of the number of linear independent row | |
4722 | and column vectors (= number of linear independent equations in | |
4723 | the case of a matrix representing an equation system) of the | |
4724 | matrix that was initially fed into "decompose_LR()". | |
4725 | ||
4726 | If "n" is the number of rows and columns of the (quadratic!) matrix, | |
4727 | then "n - order" is the dimension of the solution space of the | |
4728 | associated equation system. | |
4729 | ||
4730 | =item * | |
4731 | ||
4732 | C<$rank = $LR_matrix-E<gt>rank_LR();> | |
4733 | ||
4734 | This is an alias for the C<order_LR()> function. The "order" | |
4735 | is usually called the "rank" in the United States. | |
4736 | ||
4737 | =item * | |
4738 | ||
4739 | C<$scalar_product = $vector1-E<gt>scalar_product($vector2);> | |
4740 | ||
4741 | Returns the scalar product of vector "C<$vector1>" and vector "C<$vector2>". | |
4742 | ||
4743 | Both vectors must be column vectors (i.e., a matrix having | |
4744 | several rows but only one column). | |
4745 | ||
4746 | This is a (more efficient!) shortcut for | |
4747 | ||
4748 | $temp = ~$vector1 * $vector2; | |
4749 | $scalar_product = $temp->element(1,1); | |
4750 | ||
4751 | or the sum C<i=1..n> of the products C<vector1[i] * vector2[i]>. | |
4752 | ||
4753 | Provided none of the two input vectors is the null vector, then | |
4754 | the two vectors are orthogonal, i.e., have an angle of 90 degrees | |
4755 | between them, exactly when their scalar product is zero, and | |
4756 | vice-versa. | |
4757 | ||
4758 | =item * | |
4759 | ||
4760 | C<$vector_product = $vector1-E<gt>vector_product($vector2);> | |
4761 | ||
4762 | Returns the vector product of vector "C<$vector1>" and vector "C<$vector2>". | |
4763 | ||
4764 | Both vectors must be column vectors (i.e., a matrix having several rows | |
4765 | but only one column). | |
4766 | ||
4767 | Currently, the vector product is only defined for 3 dimensions (i.e., | |
4768 | vectors with 3 rows); all other vectors trigger an error message. | |
4769 | ||
4770 | In 3 dimensions, the vector product of two vectors "x" and "y" | |
4771 | is defined as | |
4772 | ||
4773 | | x[1] y[1] e[1] | | |
4774 | determinant | x[2] y[2] e[2] | | |
4775 | | x[3] y[3] e[3] | | |
4776 | ||
4777 | where the "C<x[i]>" and "C<y[i]>" are the components of the two vectors | |
4778 | "x" and "y", respectively, and the "C<e[i]>" are unity vectors (i.e., | |
4779 | vectors with a length equal to one) with a one in row "i" and zero's | |
4780 | elsewhere (this means that you have numbers and vectors as elements | |
4781 | in this matrix!). | |
4782 | ||
4783 | This determinant evaluates to the rather simple formula | |
4784 | ||
4785 | z[1] = x[2] * y[3] - x[3] * y[2] | |
4786 | z[2] = x[3] * y[1] - x[1] * y[3] | |
4787 | z[3] = x[1] * y[2] - x[2] * y[1] | |
4788 | ||
4789 | A characteristic property of the vector product is that the resulting | |
4790 | vector is orthogonal to both of the input vectors (if neither of both | |
4791 | is the null vector, otherwise this is trivial), i.e., the scalar product | |
4792 | of each of the input vectors with the resulting vector is always zero. | |
4793 | ||
4794 | =item * | |
4795 | ||
4796 | C<$length = $vector-E<gt>length();> | |
4797 | ||
4798 | This is actually a shortcut for | |
4799 | ||
4800 | $length = sqrt( $vector->scalar_product($vector) ); | |
4801 | ||
4802 | and returns the length of a given (column!) vector "C<$vector>". | |
4803 | ||
4804 | Note that the "length" calculated by this method is in fact the | |
4805 | "two"-norm of a vector "C<$vector>"! | |
4806 | ||
4807 | The general definition for norms of vectors is the following: | |
4808 | ||
4809 | sub vector_norm | |
4810 | { | |
4811 | croak "Usage: \$norm = \$vector->vector_norm(\$n);" | |
4812 | if (@_ != 2); | |
4813 | ||
4814 | my($vector,$n) = @_; | |
4815 | my($rows,$cols) = ($vector->[1],$vector->[2]); | |
4816 | my($k,$comp,$sum); | |
4817 | ||
4818 | croak "Math::MatrixReal::vector_norm(): vector is not a column vector" | |
4819 | unless ($cols == 1); | |
4820 | ||
4821 | croak "Math::MatrixReal::vector_norm(): norm index must be > 0" | |
4822 | unless ($n > 0); | |
4823 | ||
4824 | croak "Math::MatrixReal::vector_norm(): norm index must be integer" | |
4825 | unless ($n == int($n)); | |
4826 | ||
4827 | $sum = 0; | |
4828 | for ( $k = 0; $k < $rows; $k++ ) | |
4829 | { | |
4830 | $comp = abs( $vector->[0][$k][0] ); | |
4831 | $sum += $comp ** $n; | |
4832 | } | |
4833 | return( $sum ** (1 / $n) ); | |
4834 | } | |
4835 | ||
4836 | Note that the case "n = 1" is the "one"-norm for matrices applied to a | |
4837 | vector, the case "n = 2" is the euclidian norm or length of a vector, | |
4838 | and if "n" goes to infinity, you have the "infinity"- or "maximum"-norm | |
4839 | for matrices applied to a vector! | |
4840 | ||
4841 | =item * | |
4842 | ||
4843 | C<$xn_vector = $matrix-E<gt>>C<solve_GSM($x0_vector,$b_vector,$epsilon);> | |
4844 | ||
4845 | =item * | |
4846 | ||
4847 | C<$xn_vector = $matrix-E<gt>>C<solve_SSM($x0_vector,$b_vector,$epsilon);> | |
4848 | ||
4849 | =item * | |
4850 | ||
4851 | C<$xn_vector = $matrix-E<gt>>C<solve_RM($x0_vector,$b_vector,$weight,$epsilon);> | |
4852 | ||
4853 | In some cases it might not be practical or desirable to solve an | |
4854 | equation system "C<A * x = b>" using an analytical algorithm like | |
4855 | the "decompose_LR()" and "solve_LR()" method pair. | |
4856 | ||
4857 | In fact in some cases, due to the numerical properties (the "condition") | |
4858 | of the matrix "A", the numerical error of the obtained result can be | |
4859 | greater than by using an approximative (iterative) algorithm like one | |
4860 | of the three implemented here. | |
4861 | ||
4862 | All three methods, GSM ("Global Step Method" or "Gesamtschrittverfahren"), | |
4863 | SSM ("Single Step Method" or "Einzelschrittverfahren") and RM ("Relaxation | |
4864 | Method" or "Relaxationsverfahren"), are fix-point iterations, that is, can | |
4865 | be described by an iteration function "C<x(t+1) = Phi( x(t) )>" which has | |
4866 | the property: | |
4867 | ||
4868 | Phi(x) = x <==> A * x = b | |
4869 | ||
4870 | We can define "C<Phi(x)>" as follows: | |
4871 | ||
4872 | Phi(x) := ( En - A ) * x + b | |
4873 | ||
4874 | where "En" is a matrix of the same size as "A" ("n" rows and columns) | |
4875 | with one's on its main diagonal and zero's elsewhere. | |
4876 | ||
4877 | This function has the required property. | |
4878 | ||
4879 | Proof: | |
4880 | ||
4881 | A * x = b | |
4882 | ||
4883 | <==> -( A * x ) = -b | |
4884 | ||
4885 | <==> -( A * x ) + x = -b + x | |
4886 | ||
4887 | <==> -( A * x ) + x + b = x | |
4888 | ||
4889 | <==> x - ( A * x ) + b = x | |
4890 | ||
4891 | <==> ( En - A ) * x + b = x | |
4892 | ||
4893 | This last step is true because | |
4894 | ||
4895 | x[i] - ( a[i,1] x[1] + ... + a[i,i] x[i] + ... + a[i,n] x[n] ) + b[i] | |
4896 | ||
4897 | is the same as | |
4898 | ||
4899 | ( -a[i,1] x[1] + ... + (1 - a[i,i]) x[i] + ... + -a[i,n] x[n] ) + b[i] | |
4900 | ||
4901 | qed | |
4902 | ||
4903 | Note that actually solving the equation system "C<A * x = b>" means | |
4904 | to calculate | |
4905 | ||
4906 | a[i,1] x[1] + ... + a[i,i] x[i] + ... + a[i,n] x[n] = b[i] | |
4907 | ||
4908 | <==> a[i,i] x[i] = | |
4909 | b[i] | |
4910 | - ( a[i,1] x[1] + ... + a[i,i] x[i] + ... + a[i,n] x[n] ) | |
4911 | + a[i,i] x[i] | |
4912 | ||
4913 | <==> x[i] = | |
4914 | ( b[i] | |
4915 | - ( a[i,1] x[1] + ... + a[i,i] x[i] + ... + a[i,n] x[n] ) | |
4916 | + a[i,i] x[i] | |
4917 | ) / a[i,i] | |
4918 | ||
4919 | <==> x[i] = | |
4920 | ( b[i] - | |
4921 | ( a[i,1] x[1] + ... + a[i,i-1] x[i-1] + | |
4922 | a[i,i+1] x[i+1] + ... + a[i,n] x[n] ) | |
4923 | ) / a[i,i] | |
4924 | ||
4925 | There is one major restriction, though: a fix-point iteration is | |
4926 | guaranteed to converge only if the first derivative of the iteration | |
4927 | function has an absolute value less than one in an area around the | |
4928 | point "C<x(*)>" for which "C<Phi( x(*) ) = x(*)>" is to be true, and | |
4929 | if the start vector "C<x(0)>" lies within that area! | |
4930 | ||
4931 | This is best verified grafically, which unfortunately is impossible | |
4932 | to do in this textual documentation! | |
4933 | ||
4934 | See literature on Numerical Analysis for details! | |
4935 | ||
4936 | In our case, this restriction translates to the following three conditions: | |
4937 | ||
4938 | There must exist a norm so that the norm of the matrix of the iteration | |
4939 | function, C<( En - A )>, has a value less than one, the matrix "A" may | |
4940 | not have any zero value on its main diagonal and the initial vector | |
4941 | "C<x(0)>" must be "good enough", i.e., "close enough" to the solution | |
4942 | "C<x(*)>". | |
4943 | ||
4944 | (Remember school math: the first derivative of a straight line given by | |
4945 | "C<y = a * x + b>" is "a"!) | |
4946 | ||
4947 | The three methods expect a (quadratic!) matrix "C<$matrix>" as their | |
4948 | first argument, a start vector "C<$x0_vector>", a vector "C<$b_vector>" | |
4949 | (which is the vector "b" in your equation system "C<A * x = b>"), in the | |
4950 | case of the "Relaxation Method" ("RM"), a real number "C<$weight>" best | |
4951 | between zero and two, and finally an error limit (real number) "C<$epsilon>". | |
4952 | ||
4953 | (Note that the weight "C<$weight>" used by the "Relaxation Method" ("RM") | |
4954 | is B<NOT> checked to lie within any reasonable range!) | |
4955 | ||
4956 | The three methods first test the first two conditions of the three | |
4957 | conditions listed above and return an empty list if these conditions | |
4958 | are not fulfilled. | |
4959 | ||
4960 | Therefore, you should always test their return value using some | |
4961 | code like: | |
4962 | ||
4963 | if ( $xn_vector = $A_matrix->solve_GSM($x0_vector,$b_vector,1E-12) ) | |
4964 | { | |
4965 | # do something with the solution... | |
4966 | } | |
4967 | else | |
4968 | { | |
4969 | # do something with the fact that there is no solution... | |
4970 | } | |
4971 | ||
4972 | Otherwise, they iterate until C<abs( Phi(x) - x ) E<lt> epsilon>. | |
4973 | ||
4974 | (Beware that theoretically, infinite loops might result if the starting | |
4975 | vector is too far "off" the solution! In practice, this shouldn't be | |
4976 | a problem. Anyway, you can always press <ctrl-C> if you think that the | |
4977 | iteration takes too long!) | |
4978 | ||
4979 | The difference between the three methods is the following: | |
4980 | ||
4981 | In the "Global Step Method" ("GSM"), the new vector "C<x(t+1)>" | |
4982 | (called "y" here) is calculated from the vector "C<x(t)>" | |
4983 | (called "x" here) according to the formula: | |
4984 | ||
4985 | y[i] = | |
4986 | ( b[i] | |
4987 | - ( a[i,1] x[1] + ... + a[i,i-1] x[i-1] + | |
4988 | a[i,i+1] x[i+1] + ... + a[i,n] x[n] ) | |
4989 | ) / a[i,i] | |
4990 | ||
4991 | In the "Single Step Method" ("SSM"), the components of the vector | |
4992 | "C<x(t+1)>" which have already been calculated are used to calculate | |
4993 | the remaining components, i.e. | |
4994 | ||
4995 | y[i] = | |
4996 | ( b[i] | |
4997 | - ( a[i,1] y[1] + ... + a[i,i-1] y[i-1] + # note the "y[]"! | |
4998 | a[i,i+1] x[i+1] + ... + a[i,n] x[n] ) # note the "x[]"! | |
4999 | ) / a[i,i] | |
5000 | ||
5001 | In the "Relaxation method" ("RM"), the components of the vector | |
5002 | "C<x(t+1)>" are calculated by "mixing" old and new value (like | |
5003 | cold and hot water), and the weight "C<$weight>" determines the | |
5004 | "aperture" of both the "hot water tap" as well as of the "cold | |
5005 | water tap", according to the formula: | |
5006 | ||
5007 | y[i] = | |
5008 | ( b[i] | |
5009 | - ( a[i,1] y[1] + ... + a[i,i-1] y[i-1] + # note the "y[]"! | |
5010 | a[i,i+1] x[i+1] + ... + a[i,n] x[n] ) # note the "x[]"! | |
5011 | ) / a[i,i] | |
5012 | y[i] = weight * y[i] + (1 - weight) * x[i] | |
5013 | ||
5014 | Note that the weight "C<$weight>" should be greater than zero and | |
5015 | less than two (!). | |
5016 | ||
5017 | The three methods are supposed to be of different efficiency. | |
5018 | Experiment! | |
5019 | ||
5020 | Remember that in most cases, it is probably advantageous to first | |
5021 | "normalize()" your equation system prior to solving it! | |
5022 | ||
5023 | =head1 OVERLOADED OPERATORS | |
5024 | ||
5025 | =head2 SYNOPSIS | |
5026 | ||
5027 | =over 2 | |
5028 | ||
5029 | =item * | |
5030 | ||
5031 | Unary operators: | |
5032 | ||
5033 | "C<->", "C<~>", "C<abs>", C<test>, "C<!>", 'C<"">' | |
5034 | ||
5035 | =item * | |
5036 | ||
5037 | Binary (arithmetic) operators: | |
5038 | ||
5039 | "C<+>", "C<->", "C<*>", "C<**>", | |
5040 | "C<+=>", "C<-=>", "C<*=>", "C<**=>" | |
5041 | ||
5042 | =item * | |
5043 | ||
5044 | Binary (relational) operators: | |
5045 | ||
5046 | "C<==>", "C<!=>", "C<E<lt>>", "C<E<lt>=>", "C<E<gt>>", "C<E<gt>=>" | |
5047 | ||
5048 | "C<eq>", "C<ne>", "C<lt>", "C<le>", "C<gt>", "C<ge>" | |
5049 | ||
5050 | Note that the latter ("C<eq>", "C<ne>", ... ) are just synonyms | |
5051 | of the former ("C<==>", "C<!=>", ... ), defined for convenience | |
5052 | only. | |
5053 | ||
5054 | =back | |
5055 | ||
5056 | =head2 DESCRIPTION | |
5057 | ||
5058 | =over 5 | |
5059 | ||
5060 | =item '-' | |
5061 | ||
5062 | Unary minus | |
5063 | ||
5064 | Returns the negative of the given matrix, i.e., the matrix with | |
5065 | all elements multiplied with the factor "-1". | |
5066 | ||
5067 | Example: | |
5068 | ||
5069 | $matrix = -$matrix; | |
5070 | ||
5071 | =item '~' | |
5072 | ||
5073 | Transposition | |
5074 | ||
5075 | Returns the transposed of the given matrix. | |
5076 | ||
5077 | Examples: | |
5078 | ||
5079 | $temp = ~$vector * $vector; | |
5080 | $length = sqrt( $temp->element(1,1) ); | |
5081 | ||
5082 | if (~$matrix == $matrix) { # matrix is symmetric ... } | |
5083 | ||
5084 | =item abs | |
5085 | ||
5086 | Norm | |
5087 | ||
5088 | Returns the "one"-Norm of the given matrix. | |
5089 | ||
5090 | Example: | |
5091 | ||
5092 | $error = abs( $A * $x - $b ); | |
5093 | ||
5094 | =item test | |
5095 | ||
5096 | Boolean test | |
5097 | ||
5098 | Tests wether there is at least one non-zero element in the matrix. | |
5099 | ||
5100 | Example: | |
5101 | ||
5102 | if ($xn_vector) { # result of iteration is not zero ... } | |
5103 | ||
5104 | =item '!' | |
5105 | ||
5106 | Negated boolean test | |
5107 | ||
5108 | Tests wether the matrix contains only zero's. | |
5109 | ||
5110 | Examples: | |
5111 | ||
5112 | if (! $b_vector) { # heterogenous equation system ... } | |
5113 | else { # homogenous equation system ... } | |
5114 | ||
5115 | unless ($x_vector) { # not the null-vector! } | |
5116 | ||
5117 | =item '""""' | |
5118 | ||
5119 | "Stringify" operator | |
5120 | ||
5121 | Converts the given matrix into a string. | |
5122 | ||
5123 | Uses scientific representation to keep precision loss to a minimum in case | |
5124 | you want to read this string back in again later with "new_from_string()". | |
5125 | ||
5126 | Uses a 13-digit mantissa and a 20-character field for each element so that | |
5127 | lines will wrap nicely on an 80-column screen. | |
5128 | ||
5129 | Examples: | |
5130 | ||
5131 | $matrix = Math::MatrixReal->new_from_string(<<"MATRIX"); | |
5132 | [ 1 0 ] | |
5133 | [ 0 -1 ] | |
5134 | MATRIX | |
5135 | print "$matrix"; | |
5136 | ||
5137 | [ 1.000000000000E+00 0.000000000000E+00 ] | |
5138 | [ 0.000000000000E+00 -1.000000000000E+00 ] | |
5139 | ||
5140 | $string = "$matrix"; | |
5141 | $test = Math::MatrixReal->new_from_string($string); | |
5142 | if ($test == $matrix) { print ":-)\n"; } else { print ":-(\n"; } | |
5143 | ||
5144 | =item '+' | |
5145 | ||
5146 | Addition | |
5147 | ||
5148 | Returns the sum of the two given matrices. | |
5149 | ||
5150 | Examples: | |
5151 | ||
5152 | $matrix_S = $matrix_A + $matrix_B; | |
5153 | ||
5154 | $matrix_A += $matrix_B; | |
5155 | ||
5156 | =item '-' | |
5157 | ||
5158 | Subtraction | |
5159 | ||
5160 | Returns the difference of the two given matrices. | |
5161 | ||
5162 | Examples: | |
5163 | ||
5164 | $matrix_D = $matrix_A - $matrix_B; | |
5165 | ||
5166 | $matrix_A -= $matrix_B; | |
5167 | ||
5168 | Note that this is the same as: | |
5169 | ||
5170 | $matrix_S = $matrix_A + -$matrix_B; | |
5171 | ||
5172 | $matrix_A += -$matrix_B; | |
5173 | ||
5174 | (The latter are less efficient, though) | |
5175 | ||
5176 | =item '*' | |
5177 | ||
5178 | Multiplication | |
5179 | ||
5180 | Returns the matrix product of the two given matrices or | |
5181 | the product of the given matrix and scalar factor. | |
5182 | ||
5183 | Examples: | |
5184 | ||
5185 | $matrix_P = $matrix_A * $matrix_B; | |
5186 | ||
5187 | $matrix_A *= $matrix_B; | |
5188 | ||
5189 | $vector_b = $matrix_A * $vector_x; | |
5190 | ||
5191 | $matrix_B = -1 * $matrix_A; | |
5192 | ||
5193 | $matrix_B = $matrix_A * -1; | |
5194 | ||
5195 | $matrix_A *= -1; | |
5196 | ||
5197 | =item '**' | |
5198 | ||
5199 | Exponentiation | |
5200 | ||
5201 | Returns the matrix raised to an integer power. If 0 is passed, | |
5202 | the identity matrix is returned. If a negative integer is passed, | |
5203 | it computes the inverse (if it exists) and then raised the inverse | |
5204 | to the absolute value of the integer. The matrix must be quadratic. | |
5205 | ||
5206 | Examples: | |
5207 | ||
5208 | $matrix2 = $matrix ** 2; | |
5209 | ||
5210 | $matrix **= 2; | |
5211 | ||
5212 | $inv2 = $matrix ** -2; | |
5213 | ||
5214 | $ident = $matrix ** 0; | |
5215 | ||
5216 | ||
5217 | ||
5218 | =item '==' | |
5219 | ||
5220 | Equality | |
5221 | ||
5222 | Tests two matrices for equality. | |
5223 | ||
5224 | Example: | |
5225 | ||
5226 | if ( $A * $x == $b ) { print "EUREKA!\n"; } | |
5227 | ||
5228 | Note that in most cases, due to numerical errors (due to the finite | |
5229 | precision of computer arithmetics), it is a bad idea to compare two | |
5230 | matrices or vectors this way. | |
5231 | ||
5232 | Better use the norm of the difference of the two matrices you want | |
5233 | to compare and compare that norm with a small number, like this: | |
5234 | ||
5235 | if ( abs( $A * $x - $b ) < 1E-12 ) { print "BINGO!\n"; } | |
5236 | ||
5237 | =item '!=' | |
5238 | ||
5239 | Inequality | |
5240 | ||
5241 | Tests two matrices for inequality. | |
5242 | ||
5243 | Example: | |
5244 | ||
5245 | while ($x0_vector != $xn_vector) { # proceed with iteration ... } | |
5246 | ||
5247 | (Stops when the iteration becomes stationary) | |
5248 | ||
5249 | Note that (just like with the '==' operator), it is usually a bad idea | |
5250 | to compare matrices or vectors this way. Compare the norm of the difference | |
5251 | of the two matrices with a small number instead. | |
5252 | ||
5253 | =item 'E<lt>' | |
5254 | ||
5255 | Less than | |
5256 | ||
5257 | Examples: | |
5258 | ||
5259 | if ( $matrix1 < $matrix2 ) { # ... } | |
5260 | ||
5261 | if ( $vector < $epsilon ) { # ... } | |
5262 | ||
5263 | if ( 1E-12 < $vector ) { # ... } | |
5264 | ||
5265 | if ( $A * $x - $b < 1E-12 ) { # ... } | |
5266 | ||
5267 | These are just shortcuts for saying: | |
5268 | ||
5269 | if ( abs($matrix1) < abs($matrix2) ) { # ... } | |
5270 | ||
5271 | if ( abs($vector) < abs($epsilon) ) { # ... } | |
5272 | ||
5273 | if ( abs(1E-12) < abs($vector) ) { # ... } | |
5274 | ||
5275 | if ( abs( $A * $x - $b ) < abs(1E-12) ) { # ... } | |
5276 | ||
5277 | Uses the "one"-norm for matrices and Perl's built-in "abs()" for scalars. | |
5278 | ||
5279 | =item 'E<lt>=' | |
5280 | ||
5281 | Less than or equal | |
5282 | ||
5283 | As with the '<' operator, this is just a shortcut for the same expression | |
5284 | with "abs()" around all arguments. | |
5285 | ||
5286 | Example: | |
5287 | ||
5288 | if ( $A * $x - $b <= 1E-12 ) { # ... } | |
5289 | ||
5290 | which in fact is the same as: | |
5291 | ||
5292 | if ( abs( $A * $x - $b ) <= abs(1E-12) ) { # ... } | |
5293 | ||
5294 | Uses the "one"-norm for matrices and Perl's built-in "abs()" for scalars. | |
5295 | ||
5296 | =item 'E<gt>' | |
5297 | ||
5298 | Greater than | |
5299 | ||
5300 | As with the '<' and '<=' operator, this | |
5301 | ||
5302 | if ( $xn - $x0 > 1E-12 ) { # ... } | |
5303 | ||
5304 | is just a shortcut for: | |
5305 | ||
5306 | if ( abs( $xn - $x0 ) > abs(1E-12) ) { # ... } | |
5307 | ||
5308 | Uses the "one"-norm for matrices and Perl's built-in "abs()" for scalars. | |
5309 | ||
5310 | =item 'E<gt>=' | |
5311 | ||
5312 | Greater than or equal | |
5313 | ||
5314 | As with the '<', '<=' and '>' operator, the following | |
5315 | ||
5316 | if ( $LR >= $A ) { # ... } | |
5317 | ||
5318 | is simply a shortcut for: | |
5319 | ||
5320 | if ( abs($LR) >= abs($A) ) { # ... } | |
5321 | ||
5322 | Uses the "one"-norm for matrices and Perl's built-in "abs()" for scalars. | |
5323 | ||
5324 | =back | |
5325 | ||
5326 | =head1 SEE ALSO | |
5327 | ||
5328 | Math::VectorReal, Math::PARI, Math::MatrixBool, | |
5329 | DFA::Kleene, Math::Kleene, | |
5330 | Set::IntegerRange, Set::IntegerFast . | |
5331 | ||
5332 | =head1 VERSION | |
5333 | ||
5334 | This man page documents Math::MatrixReal version 1.9. | |
5335 | ||
5336 | The latest version can always be found at | |
5337 | http://leto.net/code/Math-MatrixReal/ | |
5338 | ||
5339 | =head1 AUTHORS | |
5340 | ||
5341 | Steffen Beyer <sb@engelschall.com>, Rodolphe Ortalo <ortalo@laas.fr>, | |
5342 | Jonathan Leto <jonathan@leto.net>. | |
5343 | ||
5344 | Currently maintained by Jonathan Leto, send all bugs/patches to me. | |
5345 | ||
5346 | =head1 CREDITS | |
5347 | ||
5348 | Many thanks to Prof. Pahlings for stoking the fire of my enthusiasm for | |
5349 | Algebra and Linear Algebra at the university (RWTH Aachen, Germany), and | |
5350 | to Prof. Esser and his assistant, Mr. Jarausch, for their fascinating | |
5351 | lectures in Numerical Analysis! | |
5352 | ||
5353 | =head1 COPYRIGHT | |
5354 | ||
5355 | Copyright (c) 1996-2002 by Steffen Beyer, Rodolphe Ortalo, Jonathan Leto. | |
5356 | All rights reserved. | |
5357 | ||
5358 | =head1 LICENSE AGREEMENT | |
5359 | ||
5360 | This package is free software; you can redistribute it and/or | |
5361 | modify it under the same terms as Perl itself. | |
5362 |