File Coverage

blib/lib/Math/MatrixReal.pm
Criterion Covered Total %
statement 1326 1640 80.8
branch 437 724 60.3
condition 158 341 46.3
subroutine 151 166 90.9
pod 27 99 27.2
total 2099 2970 70.6


line stmt bran cond sub pod time code
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-2011 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 57     57   2255860 use strict;
  57         160  
  57         3580  
10 57     57   418 use warnings;
  57         129  
  57         2070  
11 57     57   486 use Carp;
  57         200  
  57         6302  
12 57     57   87454 use Data::Dumper;
  57         806637  
  57         5561  
13 57     57   808 use Scalar::Util qw/reftype/;
  57         125  
  57         6949  
14 57     57   524 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION);
  57         116  
  57         14634  
15             require Exporter;
16              
17             @ISA = qw(Exporter);
18             @EXPORT = qw();
19             @EXPORT_OK = qw(min max);
20             %EXPORT_TAGS = (all => [@EXPORT_OK]);
21             $VERSION = '2.11';
22              
23             use overload
24 57         542 '.' => '_concat',
25             'neg' => '_negate',
26             '~' => '_transpose',
27             'bool' => '_boolean',
28             '!' => '_not_boolean',
29             'abs' => '_norm',
30             '+' => '_add',
31             '-' => '_subtract',
32             '*' => '_multiply',
33             '/' => '_divide',
34             '**' => '_exponent',
35             '+=' => '_assign_add',
36             '-=' => '_assign_subtract',
37             '*=' => '_assign_multiply',
38             '**=' => '_assign_exponent',
39             '==' => '_equal',
40             '!=' => '_not_equal',
41             '<' => '_less_than',
42             '<=' => '_less_than_or_equal',
43             '>' => '_greater_than',
44             '>=' => '_greater_than_or_equal',
45             'eq' => '_equal',
46             'ne' => '_not_equal',
47             'lt' => '_less_than',
48             'le' => '_less_than_or_equal',
49             'gt' => '_greater_than',
50             'ge' => '_greater_than_or_equal',
51             '=' => '_clone',
52             '""' => '_stringify',
53 57     57   329 'fallback' => undef;
  57         112  
54              
55             =head1 NAME
56              
57             Math::MatrixReal - Matrix of Reals
58              
59             Implements the data type "matrix of real numbers" (and consequently also
60             "vector of real numbers").
61              
62             =head1 SYNOPSIS
63              
64             my $a = Math::MatrixReal->new_random(5, 5);
65              
66             my $b = $a->new_random(10, 30, { symmetric=>1, bounded_by=>[-1,1] });
67              
68             my $c = $b * $a ** 3;
69              
70             my $d = $b->new_from_rows( [ [ 5, 3 ,4], [3, 4, 5], [ 2, 4, 1 ] ] );
71              
72             print $a;
73              
74             my $row = ($a * $b)->row(3);
75              
76             my $col = (5*$c)->col(2);
77              
78             my $transpose = ~$c;
79              
80             my $transpose = $c->transpose;
81              
82             my $inverse = $a->inverse;
83              
84             my $inverse = 1/$a;
85              
86             my $inverse = $a ** -1;
87              
88             my $determinant= $a->det;
89              
90             =cut
91              
92             sub new
93             {
94 1772 50   1772 1 5142 croak "Usage: \$new_matrix = Math::MatrixReal->new(\$rows,\$columns);" if (@_ != 3);
95              
96 1772         7276 my ($self,$rows,$cols) = @_;
97 1772   50     6589 my $class = ref($self) || $self || 'Math::MatrixReal';
98              
99 1772 50 33     10024 croak "Math::MatrixReal::new(): number of rows must be integer > 0"
100             unless ($rows > 0 and $rows == int($rows) );
101              
102 1772 50 33     8257 croak "Math::MatrixReal::new(): number of columns must be integer > 0"
103             unless ($cols > 0 and $cols == int($cols) );
104              
105 1772         5416 my $this = [ [ ], $rows, $cols ];
106              
107             # Create the first empty row and pre-lengthen
108 1772         2845 my $empty = [ ];
109 1772         5865 $#$empty = $cols - 1;
110              
111 1772         4590 map { $empty->[$_] = 0.0 } ( 0 .. $cols-1 );
  11120         18245  
112              
113             # Create a row at a time
114 1772         3914 map { $this->[0][$_] = [ @$empty ] } ( 0 .. $rows-1);
  14481         63457  
115              
116 1772         9590 bless $this, $class;
117             }
118              
119             sub new_diag {
120 28 50   28 1 332 croak "Usage: \$new_matrix = Math::MatrixReal->new_diag( [ 1, 2, 3] );" unless (@_ == 2 );
121 28         55 my ($self,$diag) = @_;
122 28         67 my $n = scalar @$diag;
123              
124 28 50       132 croak "Math::MatrixReal::new_diag(): Third argument must be an arrayref" unless (ref($diag) eq "ARRAY");
125              
126 28         106 my $matrix = Math::MatrixReal->new($n,$n);
127              
128 28         77 map { $matrix->[0][$_][$_] = shift @$diag } ( 0 .. $n-1);
  108         1383  
129 28         115 return $matrix;
130             }
131             sub new_tridiag {
132 2 50   2 1 509 croak "Usage: \$new_matrix = Math::MatrixReal->new_tridiag( [ 1, 2, 3], [ 4, 5, 6, 7], [-1,-2,-3] );" unless (@_ == 4 );
133 2         5 my ($self,$lower,$diag,$upper) = @_;
134 2         3 my $matrix;
135 2         5 my ($l,$n,$m) = (scalar(@$lower),scalar(@$diag),scalar(@$upper));
136 2         3 my ($k,$p)=(-1,-1);
137              
138 2 50 33     24 croak "Math::MatrixReal::new_tridiag(): Arguments must be arrayrefs" unless
      33        
139             ref $diag eq 'ARRAY' && ref $lower eq 'ARRAY' && ref $upper eq 'ARRAY';
140 2 50 33     12 croak "Math::MatrixReal::new_tridiag(): new_tridiag(\$lower,\$diag,\$upper) diagonal dimensions incompatible" unless
141             ($l == $m && $n == ($l+1));
142              
143 2         8 $matrix = Math::MatrixReal->new_diag($diag);
144             $matrix = $matrix->each(
145             sub {
146 32     32   125 my ($e,$i,$j) = @_;
147 32 100       145 if (($i-$j) == -1) { $k++; return $upper->[$k];}
  6 100       9  
  6 100       28  
148 8         36 elsif ( $i == $j) { return $e; }
149 6         6 elsif (($i-$j) == 1) { $p++; return $lower->[$p];}
  6         24  
150             }
151 2         23 );
152 2         26 return $matrix;
153             }
154              
155             sub new_random {
156 38 100   38 1 10335 croak "Usage: \$new_matrix = Math::MatrixReal->new_random(\$n,\$m, { symmetric => 1, bounded_by => [-5,5], integer => 1 } );"
157             if (@_ < 2);
158 37         101 my ($self, $rows, $cols, $options ) = @_;
159 37 100 33     155 (($options = $cols) and ($cols = $rows)) if ref $cols eq 'HASH';
160 37 100       149 my ($min,$max) = defined $options->{bounded_by} ? @{ $options->{bounded_by} } : ( 0, 10);
  10         24  
161 37         73 my $integer = $options->{integer};
162 37   50     221 $self = ref($self) || $self || 'Math::MatrixReal';
163            
164 37   66     146 $cols ||= $rows;
165 37 100 100     277 croak "Math::MatrixReal::new_random(): number of rows must = number of cols for symmetric option"
166             if ($rows != $cols and $options->{symmetric} );
167              
168 36 50 66     497 croak "Math::MatrixReal::new_random(): number of rows must be integer > 0"
      33        
      66        
169             unless ($rows > 0 and $rows == int($rows) ) && ($cols > 0 and $cols == int($cols) ) ;
170              
171 35 100 66     577 croak "Math::MatrixReal::new_random(): bounded_by interval length must be > 0"
      100        
172             unless (defined $min && defined $max && $min < $max );
173              
174 33 100 66     388 croak "Math::MatrixReal::new_random(): tridiag option only for square matrices"
      100        
175             if (($options->{tridiag} || $options->{tridiagonal}) && $rows != $cols);
176              
177 32 100 66     534 croak "Math::MatrixReal::new_random(): diagonal option only for square matrices "
      100        
178             if (($options->{diag} || $options->{diagonal}) && ($rows != $cols));
179              
180 31         149 my $matrix = Math::MatrixReal->new($rows,$cols);
181 31 100   10982   177 my $random_code = sub { $integer ? int($min + rand($max-$min)) : $min + rand($max-$min) } ;
  10982         40618  
182              
183 31 100 66     289 $matrix = $options->{diag} || $options->{diagonal} ? $matrix->each_diag($random_code) : $matrix->each($random_code);
184 31 100 66 50   691 $matrix = $matrix->each( sub {my($e,$i,$j)=@_; ( abs($i-$j)>1 ) ? 0 : $e } ) if ($options->{tridiag} || $options->{tridiagonal} );
  50 100       60  
  50         225  
185 31 100       386 $options->{symmetric} ? 0.5*($matrix + ~$matrix) : $matrix;
186             }
187            
188             sub new_from_string#{{{
189             {#{{{
190 175 50   175 1 4208 croak "Usage: \$new_matrix = Math::MatrixReal->new_from_string(\$string);"
191             if (@_ != 2);
192              
193 175         354 my ($self,$string) = @_;
194 175   50     1103 my $class = ref($self) || $self || 'Math::MatrixReal';
195 175         300 my ($line,$values);
196 0         0 my ($rows,$cols);
197 0         0 my ($row,$col);
198 0         0 my ($warn,$this);
199              
200 175         282 $warn = $rows = $cols = 0;
201              
202 175         604 $values = [ ];
203 175         2317 while ($string =~ m!^\s* \[ \s+ ( (?: [+-]? \d+ (?: \. \d*)? (?: E [+-]? \d+ )? \s+ )+ ) \] \s*? \n !ix) {
204 515         1089 $line = $1; $string = $';
  515         913  
205 515         1219 $values->[$rows] = [ ]; @{$values->[$rows]} = split(' ', $line);
  515         2480  
  515         1767  
206 515         831 $col = @{$values->[$rows]};
  515         1062  
207 515 100       1521 if ($col != $cols) {
208 174 50       425 unless ($cols == 0) { $warn = 1; }
  0         0  
209 174 50       398 if ($col > $cols) { $cols = $col; }
  174         761  
210             }
211 515         3191 $rows++;
212             }
213 175 50       825 if ($string !~ m/^\s*$/) {
214 0         0 chomp $string;
215 0         0 my $error_msg = "Math::MatrixReal::new_from_string(): syntax error in input string: $string";
216 0         0 croak $error_msg;
217             }
218 175 100       1982 if ($rows == 0) { croak "Math::MatrixReal::new_from_string(): empty input string"; }
  1         154  
219 174 50       624 if ($warn) { warn "Math::MatrixReal::new_from_string(): missing elements will be set to zero!\n"; }
  0         0  
220 174         722 $this = Math::MatrixReal::new($class,$rows,$cols);
221 174         645 for ( $row = 0; $row < $rows; $row++ ) {
222 515         806 for ( $col = 0; $col < @{$values->[$row]}; $col++ ) {
  2326         6167  
223 1811         8340 $this->[0][$row][$col] = $values->[$row][$col];
224             }
225             }
226 174         1077 return $this;
227             }#}}}#}}}
228              
229             # from Math::MatrixReal::Ext1 (msouth@fulcrum.org)
230             sub new_from_cols {
231 25     25 1 1842 my $self = shift;
232 25 50 33     146 my $extra_args = ( @_ > 1 && ref($_[-1]) eq 'HASH' ) ? pop : {};
233 25         197 $extra_args->{_type} = 'column';
234 25         323 $self->_new_from_rows_or_cols(@_, $extra_args );
235             }
236             # from Math::MatrixReal::Ext1 (msouth@fulcrum.org)
237             sub new_from_columns {
238 4     4 0 455 my $self = shift;
239 4         10 $self->new_from_cols(@_);
240             }
241             # from Math::MatrixReal::Ext1 (msouth@fulcrum.org)
242             sub new_from_rows {
243 28     28 1 1159 my $self = shift;
244 28 50 33     302 my $extra_args = ( @_ > 1 && ref($_[-1]) eq 'HASH' ) ? pop : {};
245 28         78 $extra_args->{_type} = 'row';
246              
247 28         246 $self->_new_from_rows_or_cols(@_, $extra_args );
248             }
249              
250             sub reshape {
251 1     1 1 427 my ($self, $rows, $cols, $values) = @_;
252              
253 1         3 my @cols = ();
254 1         3 my $p = 0;
255 1         4 for my $c (1..$cols) {
256 3         6 push @cols, [@{$values}[$p .. $p + $rows - 1]];
  3         8  
257 3         6 $p += $rows;
258             }
259              
260 1         5 return $self->new_from_cols( \@cols );
261             }
262              
263             # from Math::MatrixReal::Ext1 (msouth@fulcrum.org)
264             sub _new_from_rows_or_cols {
265 53     53   94 my $proto = shift;
266 53   66     323 my $class = ref($proto) || $proto;
267 53         81 my $ref_to_vectors = shift;
268              
269             # these additional args are internal at the moment, but in the future the user could pass e.g. {pad=>1} to
270             # request padding
271 53         171 my $args = pop;
272 53         166 my $vector_type = $args->{_type};
273 53 50       446 die "Internal ".__PACKAGE__." error" unless $vector_type =~ /^(row|column)$/;
274              
275             # step back one frame because this private method is not how the user called it
276 53         557 my $caller_subname = (caller(1))[3];
277              
278 53 100       778 croak "$caller_subname: need a reference to an array of ${vector_type}s" unless reftype($ref_to_vectors) eq 'ARRAY';
279              
280 51         187 my @vectors = @{$ref_to_vectors};
  51         149  
281 51         84 my $matrix;
282 51         226 my $other_type = {row=>'column', column=>'row'}->{$vector_type};
283 51         191 my %matrix_dim = (
284             $vector_type => scalar( @vectors ),
285             $other_type => 0, # we will correct this in a bit
286             );
287              
288             # row and column indices are one based
289 51         252 my $current_vector_count = 1;
290 51         151 foreach my $current_vector (@vectors) {
291             # dimension is one-based, so we're
292             # starting with one here and incrementing
293             # as we go. The other dimension is fixed (for now, until
294             # we add the 'pad' option), and gets set later
295 99         269 my $ref = ref( $current_vector ) ;
296              
297 99 100 33     510 if ( $ref eq '' ) {
    100 66        
    100          
298             # we hope this is a properly formatted Math::MatrixReal string,
299             # but if not we just let the Math::MatrixReal die() do it's
300             # thing
301 9         26 $current_vector = $class->new_from_string( $current_vector );
302             } elsif ( $ref eq 'ARRAY' ) {
303 72         165 my @array = @$current_vector;
304 72 100       655 croak "$caller_subname: one $vector_type you gave me was a ref to an array with no elements" unless @array;
305             # we need to create the right kind of string based on whether
306             # they said they were sending us rows or columns:
307 71 100       451 if ($vector_type eq 'row') {
308 39         381 $current_vector = $class->new_from_string( '[ '. join( " ", @array) ." ]\n" );
309             } else {
310 32         311 $current_vector = $class->new_from_string( '[ '. join( " ]\n[ ", @array) ." ]\n" );
311             }
312             } elsif ( $ref ne 'HASH' and
313             ( $current_vector->isa('Math::MatrixReal') ||
314             $current_vector->isa('Math::MatrixComplex')
315             ) ) {
316             # it's already a Math::MatrixReal something.
317             # we don't need to do anything, it will all
318             # work out
319             } else {
320             # we have no idea, error time!
321 1         444 croak "$caller_subname: I only know how to deal with array refs, strings, and things that inherit from Math::MatrixReal\n";
322             }
323              
324             # starting now we know $current_vector isa Math::MatrixReal thingy
325 96         432 my @vector_dims = $current_vector->dim;
326              
327             #die unless the appropriate dimension is 1
328 96 100       1132 croak "$caller_subname: I don't accept $other_type vectors" unless ($vector_dims[ $vector_type eq 'row' ? 0 : 1 ] == 1) ;
    100          
329              
330             # the other dimension is the length of our vector
331 94 100       5423 my $length = $vector_dims[ $vector_type eq 'row' ? 1 : 0 ];
332              
333             # set the "other" dimension to the length of this
334             # vector the first time through
335 94   66     600 $matrix_dim{$other_type} ||= $length;
336              
337             # die unless length of this vector matches the first length
338 94 100       1236 croak "$caller_subname: one $vector_type has [$length] elements and another one had [$matrix_dim{$other_type}]--all of the ${vector_type}s passed in must have the same dimension"
339             unless ($length == $matrix_dim{$other_type}) ;
340              
341             # create the matrix the first time through
342 92   66     397 $matrix ||= $class->new($matrix_dim{row}, $matrix_dim{column});
343              
344             # step along the vector assigning the value of each element
345             # to the correct place in the matrix we're building
346 92         220 foreach my $element_index ( 1..$length ){
347             # args for vector assignment:
348             # initialize both to one and reset the correct
349             # one below
350 243         432 my ($v_r, $v_c) = (1,1);
351              
352             # args for matrix assignment
353 243         383 my ($row_index, $column_index, $value);
354              
355 243 100       674 if ($vector_type eq 'row') {
356 130         248 $row_index = $current_vector_count;
357 130         457 $v_c = $column_index = $element_index;
358             } else {
359 113         380 $v_r = $row_index = $element_index;
360 113         179 $column_index = $current_vector_count;
361             }
362 243         933 $value = $current_vector->element($v_r, $v_c);
363 243         819 $matrix->assign($row_index, $column_index, $value);
364             }
365 92         254 $current_vector_count ++ ;
366             }
367 44         590 return $matrix;
368             }
369              
370             sub shadow
371             {
372 52 50   52 1 181 croak "Usage: \$new_matrix = \$some_matrix->shadow();" if (@_ != 1);
373              
374 52         116 my ($matrix) = @_;
375              
376 52         313 return $matrix->new($matrix->[1],$matrix->[2]);
377             }
378              
379             =over 4
380              
381             =item * $matrix->display_precision($integer)
382              
383             Sets the default precision when matrices are printed or stringified.
384             $matrix->display_precision(0) will only show the integer part of all the
385             entries of $matrix and $matrix->display_precision() will return to the default
386             scientific display notation. This method does not effect the precision of the
387             calculations.
388              
389             =back
390              
391             =cut
392              
393             sub display_precision
394             {
395 5     5 1 32 my ($self,$n) = @_;
396 5 50       13 if (defined $n) {
397 5 100       219 croak "Usage: \$matrix->display_precision(\$nonnegative_integer);" if ($n < 0);
398 4         15 $self->[4] = int $n;
399             } else {
400 0         0 $self->[4] = undef;
401             }
402             }
403              
404             sub copy
405             {
406 471 50   471 1 1345 croak "Usage: \$matrix1->copy(\$matrix2);"
407             if (@_ != 2);
408              
409 471         1218 my ($matrix1,$matrix2) = @_;
410 471         1022 my ($rows1,$cols1) = ($matrix1->[1],$matrix1->[2]);
411 471         886 my ($rows2,$cols2) = ($matrix2->[1],$matrix2->[2]);
412 471         1712 my ($i,$j);
413              
414 471 50 33     2508 croak "Math::MatrixReal::copy(): matrix size mismatch" unless $rows1 == $rows2 && $cols1 == $cols2;
415              
416 471         1310 for ( $i = 0; $i < $rows1; $i++ )
417             {
418 4620         6151 my $r1 = [];
419 4620         8664 my $r2 = $matrix2->[0][$i];
420 4620         17769 @$r1 = @$r2; # Copy whole array directly
421 4620         19554 $matrix1->[0][$i] = $r1;
422             }
423 471 100       1860 if (defined $matrix2->[3]) # is an LR decomposition matrix!
424             {
425 5         13 $matrix1->[3] = $matrix2->[3]; # $sign
426 5         8 $matrix1->[4] = $matrix2->[4]; # $perm_row
427 5         15 $matrix1->[5] = $matrix2->[5]; # $perm_col
428             }
429             }
430              
431             sub clone
432             {
433 290 50   290 1 862 croak "Usage: \$twin_matrix = \$some_matrix->clone();" if (@_ != 1);
434              
435 290         536 my($matrix) = @_;
436 290         370 my($temp);
437              
438 290         1068 $temp = $matrix->new($matrix->[1],$matrix->[2]);
439 290         13538 $temp->copy($matrix);
440 290         586 return $temp;
441             }
442              
443             ## trace() : return the sum of the diagonal elements
444             sub trace {
445 13 50   13 0 50 croak "Usage: \$trace = \$matrix->trace();" if (@_ != 1);
446              
447 13         20 my $matrix = shift;
448 13         27 my($rows,$cols) = ($matrix->[1],$matrix->[2]);
449 13         17 my $trace = 0;
450              
451 13 50       25 croak "Math::MatrixReal::trace(): matrix is not quadratic" unless ($rows == $cols);
452              
453 13         47 map { $trace += $matrix->[0][$_][$_] } (0 .. $cols-1);
  188         373  
454              
455 13         86 return $trace;
456             }
457             sub submatrix {
458 4     4 0 20 my $self = shift;
459 4         6 my ($x1, $y1, $x2, $y2) = @_;
460 4 100 66     347 croak "Math::MatrixReal::submatrix(): indices must be positive integers"
      66        
      66        
461             unless ($x1 >= 1 && $x2 >= 1 && $y1 >=1 && $y2 >=1 );
462 2         6 my($rows,$cols) = ($self->[1],$self->[2]);
463 2         6 my($sr,$sc) = ( 1+abs($x1-$x2), 1+abs($y1-$y2) );
464 2         6 my $submatrix = $self->new( $sr, $sc );
465              
466 2         8 for (my $i = $x1-1; $i < $x2; $i++ ) {
467 3         11 for (my $j = $y1-1; $j < $y2; $j++ ) {
468 5         26 $submatrix->[0][$i-($x1-1)][$j-($y1-1)] = $self->[0][$i][$j];
469             }
470             }
471 2         9 return $submatrix;
472             }
473             ## return the minor corresponding to $r and $c
474             ## eliminate row $r and col $c , and return the $r-1 by $c-1 matrix
475             sub minor {
476 137 50   137 0 385 croak "Usage: \$minor = \$matrix->minor(\$r,\$c);" unless (@_ == 3);
477 137         261 my ($matrix,$r,$c) = @_;
478 137         304 my ($rows,$cols) = $matrix->dim();
479              
480 137 50 33     621 croak "Math::MatrixReal::minor(): \$matrix must be at least 2x2"
481             unless ($rows > 1 and $cols > 1);
482 137 50 33     559 croak "Math::MatrixReal::minor(): $r and $c must be positive"
483             unless ($r > 0 and $c > 0 );
484 137 50 33     559 croak "Math::MatrixReal::minor(): matrix has no $r,$c element"
485             unless ($r <= $rows and $c <= $cols );
486              
487 137         424 my $minor = new Math::MatrixReal($rows-1,$cols-1);
488 137         227 my ($i,$j) = (0,0);
489              
490             ## assign() might have been easier, but this should be faster
491             ## the element can be in any of 4 regions compared to the eliminated
492             ## row and col:
493             ## above and to the left, above and to the right
494             ## below and to the left, below and to the right
495              
496 137         301 for(; $i < $rows; $i++){
497 677         1486 for(;$j < $rows; $j++ ){
498 3577 100 100     37934 if( $i >= $r && $j >= $c ){
    100 66        
    100 66        
    50 33        
499 595         1826 $minor->[0][$i-1][$j-1] = $matrix->[0][$i][$j];
500             } elsif ( $i >= $r && $j < $c ){
501 864         2459 $minor->[0][$i-1][$j] = $matrix->[0][$i][$j];
502             } elsif ( $i < $r && $j < $c ){
503 1260         4321 $minor->[0][$i][$j] = $matrix->[0][$i][$j];
504             } elsif ( $i < $r && $j >= $c ){
505 858         3107 $minor->[0][$i][$j-1] = $matrix->[0][$i][$j];
506             } else {
507 0         0 croak "Very bad things";
508             }
509             }
510 677         1334 $j = 0;
511             }
512 137         487 return ($minor);
513             }
514             sub swap_col {
515 1 50   1 1 10 croak "Usage: \$matrix->swap_col(\$col1,\$col2); " unless (@_ == 3);
516 1         2 my ($matrix,$col1,$col2) = @_;
517 1         6 my ($rows,$cols) = $matrix->dim();
518 1         2 my (@temp);
519              
520 1 50 33     12 croak "Math::MatrixReal::swap_col(): col index is not valid"
      33        
      33        
521             unless ( $col1 <= $cols && $col2 <= $cols &&
522             $col1 == int($col1) &&
523             $col2 == int($col2) );
524 1         2 $col1--;$col2--;
  1         1  
525 1         3 for(my $i=0;$i < $rows;$i++){
526 4         7 $temp[$i] = $matrix->[0][$i][$col1];
527 4         6 $matrix->[0][$i][$col1] = $matrix->[0][$i][$col2];
528 4         11 $matrix->[0][$i][$col2] = $temp[$i];
529             }
530             }
531             sub swap_row {
532 1 50   1 1 10 croak "Usage: \$matrix->swap_row(\$row1,\$row2); " unless (@_ == 3);
533 1         3 my ($matrix,$row1,$row2) = @_;
534 1         5 my ($rows,$cols) = $matrix->dim();
535 1         2 my (@temp);
536              
537 1 50 33     16 croak "Math::MatrixReal::swap_row(): row index is not valid"
      33        
      33        
538             unless ( $row1 <= $rows && $row2 <= $rows &&
539             $row1 == int($row1) &&
540             $row2 == int($row2) );
541 1         2 $row1--;$row2--;
  1         2  
542 1         4 for(my $j=0;$j < $cols;$j++){
543 4         9 $temp[$j] = $matrix->[0][$row1][$j];
544 4         8 $matrix->[0][$row1][$j] = $matrix->[0][$row2][$j];
545 4         12 $matrix->[0][$row2][$j] = $temp[$j];
546             }
547             }
548              
549             sub assign_row {
550 4 100   4 1 164 croak "Usage: \$matrix->assign_row(\$row,\$row_vec);" unless (@_ == 3);
551 3         21 my ($matrix,$row,$row_vec) = @_;
552 3         10 my ($rows1,$cols1) = $matrix->dim();
553 3         25 my ($rows2,$cols2) = $row_vec->dim();
554            
555 2 100       207 croak "Math::MatrixReal::assign_row(): number of columns mismatch" if ($cols1 != $cols2);
556 1 50       4 croak "Math::MatrixReal::assign_row(): not a row vector" unless( $rows2 == 1);
557              
558 1         2 @{$matrix->[0][--$row]} = @{$row_vec->[0][0]};
  1         4  
  1         3  
559 1         3 return $matrix;
560             }
561             # returns the number of zeroes in a row
562             sub _count_zeroes_row {
563 0     0   0 my ($matrix) = @_;
564 0         0 my ($rows,$cols) = $matrix->dim();
565 0         0 my $count = 0;
566 0 0       0 croak "_count_zeroes_row(): only 1 row, buddy" unless ($rows == 1);
567              
568 0 0       0 map { $count++ unless $matrix->[0][0][$_] } (0 .. $cols-1);
  0         0  
569 0         0 return $count;
570             }
571             ## divide a row by it's largest abs() element
572             sub _normalize_row {
573 0     0   0 my ($matrix) = @_;
574 0         0 my ($rows,$cols) = $matrix->dim();
575 0         0 my $new_row = Math::MatrixReal->new(1,$cols);
576              
577 0         0 my $big = abs($matrix->[0][0][0]);
578 0         0 for(my $j=0;$j < $cols; $j++ ){
579 0 0       0 $big = $big < abs($matrix->[0][0][$j])
580             ? abs($matrix->[0][0][$j]) : $big;
581             }
582 0 0       0 next unless $big;
583             # now $big is biggest element in row
584 0         0 for(my $j = 0;$j < $cols; $j++ ){
585 0         0 $new_row->[0][0][$j] = $matrix->[0][0][$j] / $big;
586             }
587 0         0 return $new_row;
588              
589             }
590              
591             sub cofactor {
592 7     7 0 84 my ($matrix) = @_;
593 7         40 my ($rows,$cols) = $matrix->dim();
594              
595 7 50       25 croak "Math::MatrixReal::cofactor(): Matrix is not quadratic"
596             unless ($rows == $cols);
597              
598             # black magic ahead
599             my $cofactor = $matrix->each(
600             sub {
601 133     133   213 my($v,$i,$j) = @_;
602 133 100       1092 ($i+$j) % 2 == 0 ? $matrix->minor($i,$j)->det() : -1*$matrix->minor($i,$j)->det();
603 7         72 });
604 7         91 return ($cofactor);
605             }
606              
607             sub adjoint {
608 4     4 0 22 my ($matrix) = @_;
609 4         16 return ~($matrix->cofactor);
610             }
611              
612             sub row
613             {
614 15 50   15 1 218 croak "Usage: \$row_vector = \$matrix->row(\$row);"
615             if (@_ != 2);
616              
617 15         166 my($matrix,$row) = @_;
618 15         31 my($rows,$cols) = ($matrix->[1],$matrix->[2]);
619 15         20 my($temp);
620              
621 15 50 33     193 croak "Math::MatrixReal::row(): row index out of range" if ($row < 1 || $row > $rows);
622              
623 15         21 $row--;
624 15         49 $temp = $matrix->new(1,$cols);
625 15         52 for ( my $j = 0; $j < $cols; $j++ )
626             {
627 91         273 $temp->[0][0][$j] = $matrix->[0][$row][$j];
628             }
629 15         61 return($temp);
630             }
631 4     4 0 14 sub col{ return (shift)->column(shift) }
632             sub column
633             {
634 11 50   11 1 380 croak "Usage: \$column_vector = \$matrix->column(\$column);" if (@_ != 2);
635              
636 11         20 my($matrix,$col) = @_;
637 11         25 my($rows,$cols) = ($matrix->[1],$matrix->[2]);
638             #my($temp);
639             #my($i);
640 11         12 my $col_vector;
641              
642 11 50 33     57 croak "Math::MatrixReal::column(): column index out of range" if ($col < 1 || $col > $cols);
643              
644 11         15 $col--;
645 11         29 $col_vector = $matrix->new($rows,1);
646              
647 11         26 map { $col_vector->[0][$_][0] = $matrix->[0][$_][$col] } (0 .. $rows-1);
  50         132  
648              
649 11         45 return $col_vector;
650             }
651              
652             sub _undo_LR
653             {
654 3283 50   3283   6570 croak "Usage: \$matrix->_undo_LR();"
655             if (@_ != 1);
656              
657 3283         3730 my($self) = @_;
658              
659 3283         4090 undef $self->[3];
660 3283         3644 undef $self->[4];
661 3283         4876 undef $self->[5];
662             }
663             # brrr
664             sub zero
665             {
666 66 50   66 1 280 croak "Usage: \$matrix->zero();" if (@_ != 1);
667              
668 66         133 my ($self) = @_;
669 66         167 my ($rows,$cols) = ($self->[1],$self->[2]);
670              
671 66         213 $self->_undo_LR();
672              
673             # zero out first row
674 66         252 map { $self->[0][0][$_] = 0.0 } (0 .. $cols-1);
  1058         1637  
675            
676             # copy that to the other rows
677 66         215 map { @{$self->[0][$_]} = @{$self->[0][0]} } (0 .. $rows-1);
  1058         1090  
  1058         9037  
  1058         1664  
678              
679 66         155 return $self;
680             }
681              
682             sub one
683             {
684 24 50   24 1 143 croak "Usage: \$matrix->one();" if (@_ != 1);
685              
686 24         62 my ($self) = @_;
687 24         248 my ($rows,$cols) = ($self->[1],$self->[2]);
688              
689 24         101 $self->zero(); # We rely on zero() efficiency
690              
691 24         61 map { $self->[0][$_][$_] = 1.0 } (0 .. $rows-1);
  225         344  
692              
693 24         91 return $self;
694             }
695              
696             sub assign
697             {
698 2784 50   2784 1 5419 croak "Usage: \$matrix->assign(\$row,\$column,\$value);" if (@_ != 4);
699              
700 2784         3661 my($self,$row,$col,$value) = @_;
701 2784         3717 my($rows,$cols) = ($self->[1],$self->[2]);
702              
703 2784 50 33     10757 croak "Math::MatrixReal::assign(): row index out of range" if (($row < 1) || ($row > $rows));
704 2784 50 33     10357 croak "Math::MatrixReal::assign(): column index out of range" if (($col < 1) || ($col > $cols));
705              
706 2784         4766 $self->_undo_LR();
707 2784         9885 $self->[0][--$row][--$col] = $value;
708             }
709              
710             sub element
711             {
712 3091 50   3091 1 7737 croak "Usage: \$value = \$matrix->element(\$row,\$column);" if (@_ != 3);
713              
714 3091         4352 my($self,$row,$col) = @_;
715 3091         15238 my($rows,$cols) = ($self->[1],$self->[2]);
716              
717 3091 50 33     12760 croak "Math::MatrixReal::element(): row index out of range" if (($row < 1) || ($row > $rows));
718 3091 50 33     10891 croak "Math::MatrixReal::element(): column index out of range" if (($col < 1) || ($col > $cols));
719              
720 3091         10182 return( $self->[0][--$row][--$col] );
721             }
722              
723             sub dim # returns dimensions of a matrix
724             {
725 850 50   850 0 11003 croak "Usage: (\$rows,\$columns) = \$matrix->dim();" if (@_ != 1);
726              
727 850         1107 my($matrix) = @_;
728              
729 850         2460 return( $matrix->[1], $matrix->[2] );
730             }
731              
732             sub norm_one # maximum of sums of each column
733             {
734 191 50   191 0 598 croak "Usage: \$norm_one = \$matrix->norm_one();" if (@_ != 1);
735              
736 191         296 my($self) = @_;
737 191         367 my($rows,$cols) = ($self->[1],$self->[2]);
738              
739 191         322 my $max = 0.0;
740 191         522 for (my $j = 0; $j < $cols; $j++)
741             {
742 1343         1361 my $sum = 0.0;
743 1343         2521 for (my $i = 0; $i < $rows; $i++)
744             {
745 21545         43712 $sum += abs( $self->[0][$i][$j] );
746             }
747 1343 100       3898 $max = $sum if ($sum > $max);
748             }
749 191         1798 return($max);
750             }
751             ## sum of absolute value of every element
752             sub norm_sum {
753 1 50   1 0 4 croak "Usage: \$norm_sum = \$matrix->norm_sum();" unless (@_ == 1);
754 1         3 my ($matrix) = @_;
755 1         1 my $norm = 0;
756 1     25   8 $matrix->each( sub { $norm+=abs(shift); } );
  25         75  
757 1         6 return $norm;
758             }
759             sub norm_frobenius {
760 1     1 0 11 my ($m) = @_;
761 1         4 my ($r,$c) = $m->dim;
762 1         2 my $s=0;
763              
764 1     4   7 $m->each( sub { $s+=abs(shift)**2 } );
  4         21  
765 1         9 return sqrt($s);
766             }
767              
768             # Vector Norm
769             sub norm_p {
770 5     5 0 29 my ($v,$p) = @_;
771             # sanity check on $p
772 5 50 33     14 croak "Math::MatrixReal:norm_p: argument must be a row or column vector"
773             unless ( $v->is_row_vector || $v->is_col_vector );
774 5 50 66     32 croak "Math::MatrixReal::norm_p: $p must be >= 1"
775             unless ($p =~ m/Inf(inity)?/i || $p >= 1);
776              
777 5 100       24 if( $p =~ m/^(Inf|Infinity)$/i ){
778 1         4 my $max = $v->element(1,1);
779 1 100   3   7 $v->each ( sub { my $x=abs(shift); $max = $x if( $x > $max ); } );
  3         6  
  3         27  
780 1         9 return $max;
781             }
782              
783 4         7 my $s=0;
784 4     12   39 $v->each( sub { $s+= (abs(shift))**$p; } );
  12         70  
785 4         37 return $s ** (1/$p);
786              
787             }
788             sub norm_max # maximum of sums of each row
789             {
790 1 50   1 0 20 croak "Usage: \$norm_max = \$matrix->norm_max();" if (@_ != 1);
791              
792 1         3 my($self) = @_;
793 1         2 my($rows,$cols) = ($self->[1],$self->[2]);
794              
795 1         2 my $max = 0.0;
796 1         5 for (my $i = 0; $i < $rows; $i++)
797             {
798 5         6 my $sum = 0.0;
799 5         11 for (my $j = 0; $j < $cols; $j++)
800             {
801 25         51 $sum += abs( $self->[0][$i][$j] );
802             }
803 5 100       17 $max = $sum if ($sum > $max);
804             }
805 1         5 return($max);
806             }
807              
808             sub negate
809             {
810 1 50   1 0 4 croak "Usage: \$matrix1->negate(\$matrix2);"
811             if (@_ != 2);
812              
813 1         3 my($matrix1,$matrix2) = @_;
814 1         3 my($rows1,$cols1) = ($matrix1->[1],$matrix1->[2]);
815 1         2 my($rows2,$cols2) = ($matrix2->[1],$matrix2->[2]);
816              
817 1 50 33     6 croak "Math::MatrixReal::negate(): matrix size mismatch"
818             unless (($rows1 == $rows2) && ($cols1 == $cols2));
819              
820 1         7 $matrix1->_undo_LR();
821              
822 1         5 for (my $i = 0; $i < $rows1; $i++ )
823             {
824 10         15 for (my $j = 0; $j < $cols1; $j++ )
825             {
826 100         182 $matrix1->[0][$i][$j] = -($matrix2->[0][$i][$j]);
827             }
828             }
829             }
830             ## each(): evaluate a coderef on each element and return a new matrix
831             ## of said
832             sub each {
833 92 50   92 1 550 croak "Usage: \$new_matrix = \$matrix->each( \&sub );" unless (@_ == 2 );
834 92         171 my($matrix,$function) = @_;
835 92         599 my($rows,$cols) = ($matrix->[1],$matrix->[2]);
836 92         312 my($new_matrix) = $matrix->clone();
837              
838 92 50       412 croak "Math::MatrixReal::each(): argument is not a sub reference" unless ref($function);
839 92         458 $new_matrix->_undo_LR();
840              
841 92         295 for (my $i = 0; $i < $rows; $i++ ) {
842 685         1560 for (my $j = 0; $j < $cols; $j++ ) {
843 57     57   472992 no strict 'refs';
  57         218  
  57         15608  
844             # $i,$j are 1-based as of 1.7
845 12663         22960 $new_matrix->[0][$i][$j] = &{ $function }($matrix->[0][$i][$j],$i+1,$j+1) ;
  12663         21893  
846             }
847             }
848 92         456 return ($new_matrix);
849             }
850             ## each_diag(): same as each() but only diag elements
851             sub each_diag {
852 39 50   39 1 185 croak "Usage: \$new_matrix = \$matrix->each_diag( \&sub );" unless (@_ == 2 );
853 39         57 my($matrix,$function) = @_;
854 39         122 my($rows,$cols) = ($matrix->[1],$matrix->[2]);
855 39         160 my($new_matrix) = $matrix->clone();
856              
857 39 50       107 croak "Math::MatrixReal::each(): argument is not a sub reference" unless ref($function);
858 39 50       82 croak "Matrix is not quadratic" unless ($rows == $cols);
859              
860 39         95 $new_matrix->_undo_LR();
861              
862 39         103 for (my $i = 0; $i < $rows; $i++ ) {
863 166         461 for (my $j = 0; $j < $cols; $j++ ) {
864 972 100       2340 next unless ($i == $j);
865 57     57   359 no strict 'refs';
  57         222  
  57         1129108  
866             # $i,$j are 1-based as of 1.7
867 166         278 $new_matrix->[0][$i][$j] = &{ $function }($matrix->[0][$i][$j],$i+1,$j+1) ;
  166         363  
868             }
869             }
870 39         269 return ($new_matrix);
871             }
872              
873             ## Make computing the inverse more user friendly
874             sub inverse {
875 19 50   19 0 681 croak "Usage: \$inverse = \$matrix->inverse();" unless (@_ == 1);
876 19         30 my ($matrix) = @_;
877 19         65 return $matrix->decompose_LR->invert_LR;
878             }
879              
880             sub transpose {
881              
882 57 50   57 0 208 croak "Usage: \$matrix1->transpose(\$matrix2);" if (@_ != 2);
883              
884 57         93 my($matrix1,$matrix2) = @_;
885 57         129 my($rows1,$cols1) = ($matrix1->[1],$matrix1->[2]);
886 57         130 my($rows2,$cols2) = ($matrix2->[1],$matrix2->[2]);
887              
888 57 50 33     5136 croak "Math::MatrixReal::transpose(): matrix size mismatch"
889             unless (($rows1 == $cols2) && ($cols1 == $rows2));
890              
891 57         166 $matrix1->_undo_LR();
892              
893 57 100       154 if ($rows1 == $cols1)
894             {
895             # more complicated to make in-place possible!
896              
897 55         187 for (my $i = 0; $i < $rows1; $i++)
898             {
899 585         1254 for (my $j = ($i + 1); $j < $cols1; $j++)
900             {
901 6975         17258 my $swap = $matrix2->[0][$i][$j];
902 6975         10603 $matrix1->[0][$i][$j] = $matrix2->[0][$j][$i];
903 6975         20067 $matrix1->[0][$j][$i] = $swap;
904             }
905 585         1693 $matrix1->[0][$i][$i] = $matrix2->[0][$i][$i];
906             }
907             } else { # ($rows1 != $cols1)
908 2         9 for (my $i = 0; $i < $rows1; $i++)
909             {
910 6         22 for (my $j = 0; $j < $cols1; $j++)
911             {
912 6         32 $matrix1->[0][$i][$j] = $matrix2->[0][$j][$i];
913             }
914             }
915             }
916             }
917              
918             sub add
919             {
920 25 50   25 0 79 croak "Usage: \$matrix1->add(\$matrix2,\$matrix3);" if (@_ != 3);
921              
922 25         59 my($matrix1,$matrix2,$matrix3) = @_;
923 25         121 my($rows1,$cols1) = ($matrix1->[1],$matrix1->[2]);
924 25         49 my($rows2,$cols2) = ($matrix2->[1],$matrix2->[2]);
925 25         94 my($rows3,$cols3) = ($matrix3->[1],$matrix3->[2]);
926              
927 25 50 33     321 croak "Math::MatrixReal::add(): matrix size mismatch"
      33        
      33        
928             unless (($rows1 == $rows2) && ($rows1 == $rows3) &&
929             ($cols1 == $cols2) && ($cols1 == $cols3));
930              
931 25         76 $matrix1->_undo_LR();
932              
933 25         118 for ( my $i = 0; $i < $rows1; $i++ )
934             {
935 378         707 for ( my $j = 0; $j < $cols1; $j++ )
936             {
937 11142         30582 $matrix1->[0][$i][$j] = $matrix2->[0][$i][$j] + $matrix3->[0][$i][$j];
938             }
939             }
940             }
941              
942             sub subtract
943             {
944 171 50   171 0 1014 croak "Usage: \$matrix1->subtract(\$matrix2,\$matrix3);" if (@_ != 3);
945              
946 171         266 my($matrix1,$matrix2,$matrix3) = @_;
947 171         338 my($rows1,$cols1) = ($matrix1->[1],$matrix1->[2]);
948 171         365 my($rows2,$cols2) = ($matrix2->[1],$matrix2->[2]);
949 171         343 my($rows3,$cols3) = ($matrix3->[1],$matrix3->[2]);
950              
951 171 50 33     7382 croak "Math::MatrixReal::subtract(): matrix size mismatch"
      33        
      33        
952             unless (($rows1 == $rows2) && ($rows1 == $rows3) &&
953             ($cols1 == $cols2) && ($cols1 == $cols3));
954              
955 171         462 $matrix1->_undo_LR();
956              
957 171         529 for ( my $i = 0; $i < $rows1; $i++ )
958             {
959 1537         3038 for ( my $j = 0; $j < $cols1; $j++ )
960             {
961 21703         66599 $matrix1->[0][$i][$j] = $matrix2->[0][$i][$j] - $matrix3->[0][$i][$j];
962             }
963             }
964             }
965              
966             sub multiply_scalar
967             {
968 24 50   24 0 88 croak "Usage: \$matrix1->multiply_scalar(\$matrix2,\$scalar);"
969             if (@_ != 3);
970              
971 24         40 my($matrix1,$matrix2,$scalar) = @_;
972 24         48 my($rows1,$cols1) = ($matrix1->[1],$matrix1->[2]);
973 24         50 my($rows2,$cols2) = ($matrix2->[1],$matrix2->[2]);
974              
975 24 50 33     129 croak "Math::MatrixReal::multiply_scalar(): matrix size mismatch"
976             unless (($rows1 == $rows2) && ($cols1 == $cols2));
977              
978 24         71 $matrix1->_undo_LR();
979              
980 24         79 for ( my $i = 0; $i < $rows1; $i++ )
981             {
982 249         541 map { $matrix1->[0][$i][$_] = $matrix2->[0][$i][$_] * $scalar } (0 .. $cols1-1);
  4111         7328  
983             }
984             }
985              
986             sub multiply
987             {
988 45 50   45 0 174 croak "Usage: \$product_matrix = \$matrix1->multiply(\$matrix2);"
989             if (@_ != 2);
990              
991 45         98 my($matrix1,$matrix2) = @_;
992 45         105 my($rows1,$cols1) = ($matrix1->[1],$matrix1->[2]);
993 45         88 my($rows2,$cols2) = ($matrix2->[1],$matrix2->[2]);
994              
995 45 50       141 croak "Math::MatrixReal::multiply(): matrix size mismatch" unless ($cols1 == $rows2);
996              
997 45         132 my $temp = $matrix1->new($rows1,$cols2);
998 45         208 for (my $i = 0; $i < $rows1; $i++ )
999             {
1000 417         859 for (my $j = 0; $j < $cols2; $j++ )
1001             {
1002 8427         8551 my $sum = 0.0;
1003 8427         15904 for (my $k = 0; $k < $cols1; $k++ )
1004             {
1005 224889         567815 $sum += ( $matrix1->[0][$i][$k] * $matrix2->[0][$k][$j] );
1006             }
1007 8427         23438 $temp->[0][$i][$j] = $sum;
1008             }
1009             }
1010 45         405 return($temp);
1011             }
1012              
1013             sub exponent {
1014 21 50   21 0 102 croak "Usage: \$matrix_exp = \$matrix1->exponent(\$integer);" if(@_ != 2 );
1015 21         33 my($matrix,$argument) = @_;
1016 21         47 my($rows,$cols) = ($matrix->[1],$matrix->[2]);
1017 21         34 my($name) = "'**'";
1018 21         62 my($temp) = $matrix->clone();
1019              
1020 21 50       54 croak "Matrix is not quadratic" unless ($rows == $cols);
1021 21 50       122 croak "Exponent must be integer" unless ($argument =~ m/^[+-]?\d+$/ );
1022              
1023 21 100       60 return($matrix) if ($argument == 1);
1024              
1025 20         67 $temp->_undo_LR();
1026              
1027             # negative exponent is (A^-1)^n
1028 20 100       84 if( $argument < 0 ){
    100          
1029 5         21 my $LR = $matrix->decompose_LR();
1030 5         19 my $inverse = $LR->invert_LR();
1031 5 50       15 unless (defined $inverse){
1032 0         0 carp "Matrix has no inverse";
1033 0         0 return undef;
1034             }
1035 5         23 $temp = $inverse->clone();
1036 5 50       34 if( $inverse ){
1037 5 100       54 return($inverse) if ($argument == -1);
1038 1         3 for( 2 .. abs($argument) ){
1039 1         4 $temp = multiply($inverse,$temp);
1040             }
1041 1         16 return($temp);
1042             } else {
1043             # TODO: is this the right behaviour?
1044 0         0 carp "Cannot compute negative exponent, inverse does not exist";
1045 0         0 return undef;
1046             }
1047             # matrix to zero power is identity matrix
1048             } elsif( $argument == 0 ){
1049 3         7 $temp->one();
1050 3         14 return ($temp);
1051             }
1052              
1053             # if it is diagonal, just raise diagonal entries to power
1054 12 100       37 if( $matrix->is_diagonal() ){
1055 10     75   73 $temp = $temp->each_diag( sub { (shift)**$argument } );
  75         317  
1056 10         492 return ($temp);
1057            
1058             } else {
1059 2         7 for( 2 .. $argument ){
1060 2         8 $temp = multiply($matrix,$temp);
1061             }
1062 2         11 return ($temp);
1063             }
1064             }
1065              
1066             sub min
1067             {
1068              
1069 3 100   3 0 33 if ( @_ == 1 ) {
1070 2         5 my $matrix = shift;
1071              
1072 2 50       8 croak "Usage: \$minimum = Math::MatrixReal::min(\$number1,\$number2) or $matrix->min" if (@_ > 2);
1073 2 50       8 croak "invalid" unless ref $matrix eq 'Math::MatrixReal';
1074              
1075 2         10 my $min = $matrix->element(1,1);
1076 2 100   500   26 $matrix->each( sub { my ($e,$i,$j) = @_; $min = $e if $e < $min; } );
  500         577  
  500         2298  
1077 2         16 return $min;
1078             }
1079 1 50       11 $_[0] < $_[1] ? $_[0] : $_[1];
1080             }
1081              
1082             sub max
1083             {
1084 3 100   3 0 59 if ( @_ == 1 ) {
1085 2         4 my $matrix = shift;
1086              
1087 2 50       7 croak "Usage: \$maximum = Math::MatrixReal::max(\$number1,\$number2) or $matrix->max" if (@_ > 2);
1088 2 50       8 croak "Math::MatrixReal::max(\$matrix) \$matrix is not a Math::MatrixReal matrix" unless ref $matrix eq 'Math::MatrixReal';
1089            
1090 2         9 my $max = $matrix->element(1,1);
1091 2 100   500   18 $matrix->each( sub { my ($e,$i,$j) = @_; $max = $e if $e > $max; } );
  500         585  
  500         2435  
1092 2         15 return $max;
1093             }
1094              
1095 1 50       9 $_[0] > $_[1] ? $_[0] : $_[1];
1096             }
1097              
1098             sub kleene
1099             {
1100 0 0   0 0 0 croak "Usage: \$minimal_cost_matrix = \$cost_matrix->kleene();" if (@_ != 1);
1101              
1102 0         0 my($matrix) = @_;
1103 0         0 my($rows,$cols) = ($matrix->[1],$matrix->[2]);
1104              
1105 0 0       0 croak "Math::MatrixReal::kleene(): matrix is not quadratic" unless ($rows == $cols);
1106              
1107 0         0 my $temp = $matrix->new($rows,$cols);
1108 0         0 $temp->copy($matrix);
1109 0         0 $temp->_undo_LR();
1110 0         0 my $n = $rows;
1111 0         0 for ( my $i = 0; $i < $n; $i++ )
1112             {
1113 0         0 $temp->[0][$i][$i] = min( $temp->[0][$i][$i] , 0 );
1114             }
1115 0         0 for ( my $k = 0; $k < $n; $k++ )
1116             {
1117 0         0 for ( my $i = 0; $i < $n; $i++ )
1118             {
1119 0         0 for ( my $j = 0; $j < $n; $j++ )
1120             {
1121 0         0 $temp->[0][$i][$j] = min( $temp->[0][$i][$j] ,
1122             ( $temp->[0][$i][$k] +
1123             $temp->[0][$k][$j] ) );
1124             }
1125             }
1126             }
1127 0         0 return($temp);
1128             }
1129              
1130             sub normalize
1131             {
1132 0 0   0 0 0 croak "Usage: (\$norm_matrix,\$norm_vector) = \$matrix->normalize(\$vector);"
1133             if (@_ != 2);
1134              
1135 0         0 my($matrix,$vector) = @_;
1136 0         0 my($rows,$cols) = ($matrix->[1],$matrix->[2]);
1137 0         0 my($norm_matrix,$norm_vector);
1138 0         0 my($max,$val);
1139 0         0 my($i,$j,$n);
1140              
1141 0 0       0 croak "Math::MatrixReal::normalize(): matrix is not quadratic"
1142             unless ($rows == $cols);
1143              
1144 0         0 $n = $rows;
1145              
1146 0 0       0 croak "Math::MatrixReal::normalize(): vector is not a column vector"
1147             unless ($vector->[2] == 1);
1148              
1149 0 0       0 croak "Math::MatrixReal::normalize(): matrix and vector size mismatch"
1150             unless ($vector->[1] == $n);
1151              
1152 0         0 $norm_matrix = $matrix->new($n,$n);
1153 0         0 $norm_vector = $vector->new($n,1);
1154              
1155 0         0 $norm_matrix->copy($matrix);
1156 0         0 $norm_vector->copy($vector);
1157              
1158 0         0 $norm_matrix->_undo_LR();
1159              
1160 0         0 for ( $i = 0; $i < $n; $i++ )
1161             {
1162 0         0 $max = abs($norm_vector->[0][$i][0]);
1163 0         0 for ( $j = 0; $j < $n; $j++ )
1164             {
1165 0         0 $val = abs($norm_matrix->[0][$i][$j]);
1166 0 0       0 if ($val > $max) { $max = $val; }
  0         0  
1167             }
1168 0 0       0 if ($max != 0)
1169             {
1170 0         0 $norm_vector->[0][$i][0] /= $max;
1171 0         0 for ( $j = 0; $j < $n; $j++ )
1172             {
1173 0         0 $norm_matrix->[0][$i][$j] /= $max;
1174             }
1175             }
1176             }
1177 0         0 return($norm_matrix,$norm_vector);
1178             }
1179              
1180             sub decompose_LR
1181             {
1182 177 50   177 0 457 croak "Usage: \$LR_matrix = \$matrix->decompose_LR();"
1183             if (@_ != 1);
1184              
1185 177         246 my($matrix) = @_;
1186 177         369 my($rows,$cols) = ($matrix->[1],$matrix->[2]);
1187 177         238 my($perm_row,$perm_col);
1188 0         0 my($row,$col,$max);
1189 0         0 my($i,$j,$k,$n);
1190 177         212 my($sign) = 1;
1191 177         196 my($swap);
1192             my($temp);
1193              
1194 177 50       340 croak "Math::MatrixReal::decompose_LR(): matrix is not quadratic"
1195             unless ($rows == $cols);
1196              
1197 177         514 $temp = $matrix->new($rows,$cols);
1198 177         496 $temp->copy($matrix);
1199 177         316 $n = $rows;
1200 177         244 $perm_row = [ ];
1201 177         545 $perm_col = [ ];
1202 177         486 for ( $i = 0; $i < $n; $i++ )
1203             {
1204 779         1523 $perm_row->[$i] = $i;
1205 779         1905 $perm_col->[$i] = $i;
1206             }
1207             NONZERO:
1208 177         1016 for ( $k = 0; $k < $n; $k++ ) # use Gauss's algorithm:
1209             {
1210             # complete pivot-search:
1211              
1212 776         1825 $max = 0;
1213 776         2710 for ( $i = $k; $i < $n; $i++ )
1214             {
1215 2265         5976 for ( $j = $k; $j < $n; $j++ )
1216             {
1217 8483 100       46215 if (($swap = abs($temp->[0][$i][$j])) > $max)
1218             {
1219 1786         2280 $max = $swap;
1220 1786         2649 $row = $i;
1221 1786         4334 $col = $j;
1222             }
1223             }
1224             }
1225 776 100       2100 last NONZERO if ($max == 0); # (all remaining elements are zero)
1226 773 100       1755 if ($k != $row) # swap row $k and row $row:
1227             {
1228 346         509 $sign = -$sign;
1229 346         2140 $swap = $perm_row->[$k];
1230 346         535 $perm_row->[$k] = $perm_row->[$row];
1231 346         819 $perm_row->[$row] = $swap;
1232 346         692 for ( $j = 0; $j < $n; $j++ )
1233             {
1234             # (must run from 0 since L has to be swapped too!)
1235              
1236 1751         2830 $swap = $temp->[0][$k][$j];
1237 1751         2570 $temp->[0][$k][$j] = $temp->[0][$row][$j];
1238 1751         7979 $temp->[0][$row][$j] = $swap;
1239             }
1240             }
1241 773 100       1615 if ($k != $col) # swap column $k and column $col:
1242             {
1243 393         521 $sign = -$sign;
1244 393         474 $swap = $perm_col->[$k];
1245 393         877 $perm_col->[$k] = $perm_col->[$col];
1246 393         573 $perm_col->[$col] = $swap;
1247 393         945 for ( $i = 0; $i < $n; $i++ )
1248             {
1249 1947         4027 $swap = $temp->[0][$i][$k];
1250 1947         2878 $temp->[0][$i][$k] = $temp->[0][$i][$col];
1251 1947         5833 $temp->[0][$i][$col] = $swap;
1252             }
1253             }
1254 773         2686 for ( $i = ($k + 1); $i < $n; $i++ )
1255             {
1256             # scan the remaining rows, add multiples of row $k to row $i:
1257              
1258 1486         3022 $swap = $temp->[0][$i][$k] / $temp->[0][$k][$k];
1259 1486 100       3693 if ($swap != 0)
1260             {
1261             # calculate a row of matrix R:
1262              
1263 1357         2699 for ( $j = ($k + 1); $j < $n; $j++ )
1264             {
1265 4114         16385 $temp->[0][$i][$j] -= $temp->[0][$k][$j] * $swap;
1266             }
1267              
1268             # store matrix L in same matrix as R:
1269              
1270 1357         9539 $temp->[0][$i][$k] = $swap;
1271             }
1272             }
1273             }
1274 177         3005 $temp->[3] = $sign;
1275 177         299 $temp->[4] = $perm_row;
1276 177         284 $temp->[5] = $perm_col;
1277 177         900 return($temp);
1278             }
1279              
1280             sub solve_LR
1281             {
1282 113 50   113 0 257 croak "Usage: (\$dimension,\$x_vector,\$base_matrix) = \$LR_matrix->solve_LR(\$b_vector);"
1283             if (@_ != 2);
1284              
1285 113         201 my($LR_matrix,$b_vector) = @_;
1286 113         180 my($rows,$cols) = ($LR_matrix->[1],$LR_matrix->[2]);
1287 113         124 my($dimension,$x_vector,$base_matrix);
1288 0         0 my($perm_row,$perm_col);
1289 0         0 my($y_vector,$sum);
1290 0         0 my($i,$j,$k,$n);
1291              
1292 113 50 33     498 croak "Math::MatrixReal::solve_LR(): not an LR decomposition matrix"
1293             unless ((defined $LR_matrix->[3]) && ($rows == $cols));
1294              
1295 113         137 $n = $rows;
1296              
1297 113 50       239 croak "Math::MatrixReal::solve_LR(): vector is not a column vector"
1298             unless ($b_vector->[2] == 1);
1299              
1300 113 50       255 croak "Math::MatrixReal::solve_LR(): matrix and vector size mismatch"
1301             unless ($b_vector->[1] == $n);
1302              
1303 113         143 $perm_row = $LR_matrix->[4];
1304 113         163 $perm_col = $LR_matrix->[5];
1305              
1306 113         259 $x_vector = $b_vector->new($n,1);
1307 113         270 $y_vector = $b_vector->new($n,1);
1308 113         301 $base_matrix = $LR_matrix->new($n,$n);
1309              
1310             # calculate "x" so that LRx = b ==> calculate Ly = b, Rx = y:
1311              
1312 113         308 for ( $i = 0; $i < $n; $i++ ) # calculate $y_vector:
1313             {
1314 665         1051 $sum = $b_vector->[0][($perm_row->[$i])][0];
1315 665         1330 for ( $j = 0; $j < $i; $j++ )
1316             {
1317 1987         5060 $sum -= $LR_matrix->[0][$i][$j] * $y_vector->[0][$j][0];
1318             }
1319 665         1721 $y_vector->[0][$i][0] = $sum;
1320             }
1321              
1322 113         139 $dimension = 0;
1323 113         257 for ( $i = ($n - 1); $i >= 0; $i-- ) # calculate $x_vector:
1324             {
1325 665 50       1224 if ($LR_matrix->[0][$i][$i] == 0)
1326             {
1327 0 0       0 if ($y_vector->[0][$i][0] != 0)
1328             {
1329 0         0 return(); # a solution does not exist!
1330             }
1331             else
1332             {
1333 0         0 $dimension++;
1334 0         0 $x_vector->[0][($perm_col->[$i])][0] = 0;
1335             }
1336             } else {
1337 665         1536 $sum = $y_vector->[0][$i][0];
1338 665         1271 for ( $j = ($i + 1); $j < $n; $j++ )
1339             {
1340 1987         5171 $sum -= $LR_matrix->[0][$i][$j] *
1341             $x_vector->[0][($perm_col->[$j])][0];
1342             }
1343 665         3571 $x_vector->[0][($perm_col->[$i])][0] =
1344             $sum / $LR_matrix->[0][$i][$i];
1345             }
1346             }
1347 113 50       228 if ($dimension)
1348             {
1349 0 0       0 if ($dimension == $n)
1350             {
1351 0         0 $base_matrix->one();
1352             } else {
1353 0         0 for ( $k = 0; $k < $dimension; $k++ )
1354             {
1355 0         0 $base_matrix->[0][($perm_col->[($n-$k-1)])][$k] = 1;
1356 0         0 for ( $i = ($n-$dimension-1); $i >= 0; $i-- )
1357             {
1358 0         0 $sum = 0;
1359 0         0 for ( $j = ($i + 1); $j < $n; $j++ )
1360             {
1361 0         0 $sum -= $LR_matrix->[0][$i][$j] *
1362             $base_matrix->[0][($perm_col->[$j])][$k];
1363             }
1364 0         0 $base_matrix->[0][($perm_col->[$i])][$k] =
1365             $sum / $LR_matrix->[0][$i][$i];
1366             }
1367             }
1368             }
1369             }
1370 113         655 return( $dimension, $x_vector, $base_matrix );
1371             }
1372              
1373             sub invert_LR
1374             {
1375 25 50   25 0 85 croak "Usage: \$inverse_matrix = \$LR_matrix->invert_LR();"
1376             if (@_ != 1);
1377              
1378 25         46 my($matrix) = @_;
1379 25         64 my($rows,$cols) = ($matrix->[1],$matrix->[2]);
1380 25         42 my($inv_matrix,$x_vector,$y_vector);
1381 0         0 my($i,$j,$n);
1382              
1383 25 50 33     201 croak "Math::MatrixReal::invert_LR(): not an LR decomposition matrix"
1384             unless ((defined $matrix->[3]) && ($rows == $cols));
1385              
1386 25         52 $n = $rows;
1387             #print Dumper [ $matrix ];
1388 25 50       679 if ($matrix->[0][$n-1][$n-1] != 0)
1389             {
1390 25         102 $inv_matrix = $matrix->new($n,$n);
1391 25         83 $y_vector = $matrix->new($n,1);
1392 25         109 for ( $j = 0; $j < $n; $j++ )
1393             {
1394 112 100       241 if ($j > 0)
1395             {
1396 87         169 $y_vector->[0][$j-1][0] = 0;
1397             }
1398 112         195 $y_vector->[0][$j][0] = 1;
1399 112 50       343 if (($rows,$x_vector,$cols) = $matrix->solve_LR($y_vector))
1400             {
1401 112         291 for ( $i = 0; $i < $n; $i++ )
1402             {
1403 662         2142 $inv_matrix->[0][$i][$j] = $x_vector->[0][$i][0];
1404             }
1405             } else {
1406 0         0 die "Math::MatrixReal::invert_LR(): unexpected error - please inform author!\n";
1407             }
1408             }
1409 25         236 return($inv_matrix);
1410             } else {
1411 0         0 warn __PACKAGE__ . qq{: matrix not invertible\n};
1412 0         0 return;
1413             }
1414             }
1415              
1416             sub condition
1417             {
1418             # 1st matrix MUST be the inverse of 2nd matrix (or vice-versa)
1419             # for a meaningful result!
1420              
1421             # make this work when given no args
1422              
1423 1 50   1 0 5 croak "Usage: \$condition = \$matrix->condition(\$inverse_matrix);" if (@_ != 2);
1424              
1425 1         3 my($matrix1,$matrix2) = @_;
1426 1         3 my($rows1,$cols1) = ($matrix1->[1],$matrix1->[2]);
1427 1         2 my($rows2,$cols2) = ($matrix2->[1],$matrix2->[2]);
1428              
1429 1 50       4 croak "Math::MatrixReal::condition(): 1st matrix is not quadratic"
1430             unless ($rows1 == $cols1);
1431              
1432 1 50       3 croak "Math::MatrixReal::condition(): 2nd matrix is not quadratic"
1433             unless ($rows2 == $cols2);
1434              
1435 1 50 33     7 croak "Math::MatrixReal::condition(): matrix size mismatch"
1436             unless (($rows1 == $rows2) && ($cols1 == $cols2));
1437              
1438 1         5 return( $matrix1->norm_one() * $matrix2->norm_one() );
1439             }
1440              
1441             ## easy to use determinant
1442             ## very fast if matrix is diagonal or triangular
1443              
1444             sub det {
1445 172 50   172 1 1414 croak "Usage: \$determinant = \$matrix->det_LR();" unless (@_ == 1);
1446 172         1213 my ($matrix) = @_;
1447 172         425 my ($rows,$cols) = $matrix->dim();
1448 172         309 my $det = 1;
1449              
1450 172 50       476 croak "Math::MatrixReal::det(): Matrix is not quadratic"
1451             unless ($rows == $cols);
1452            
1453             # diagonal will match too
1454 172 100       379 if( $matrix->is_upper_triangular() ){
    100          
1455 24     72   686 $matrix->each_diag( sub { $det*=shift; } );
  72         241  
1456             } elsif ( $matrix->is_lower_triangular() ){
1457 3     9   17 $matrix->each_diag( sub { $det*=shift; } );
  9         34  
1458             } else {
1459 145         743 return $matrix->decompose_LR->det_LR();
1460             }
1461 27         167 return $det;
1462             }
1463              
1464             sub det_LR # determinant of LR decomposition matrix
1465             {
1466 145 50   145 0 412 croak "Usage: \$determinant = \$LR_matrix->det_LR();"
1467             if (@_ != 1);
1468              
1469 145         334 my($matrix) = @_;
1470 145         257 my($rows,$cols) = ($matrix->[1],$matrix->[2]);
1471 145         154 my($k,$det);
1472              
1473 145 50 33     675 croak "Math::MatrixReal::det_LR(): not an LR decomposition matrix"
1474             unless ((defined $matrix->[3]) && ($rows == $cols));
1475              
1476 145         198 $det = $matrix->[3];
1477 145         333 for ( $k = 0; $k < $rows; $k++ )
1478             {
1479 644         3123 $det *= $matrix->[0][$k][$k];
1480             }
1481 145         2884 return($det);
1482             }
1483              
1484             sub rank_LR {
1485 5     5 0 12 return (shift)->order_LR;
1486             }
1487              
1488             sub order_LR # order of LR decomposition matrix (number of non-zero equations)
1489             {
1490 5 50   5 0 11 croak "Usage: \$order = \$LR_matrix->order_LR();"
1491             if (@_ != 1);
1492              
1493 5         6 my($matrix) = @_;
1494 5         7 my($rows,$cols) = ($matrix->[1],$matrix->[2]);
1495 5         4 my($order);
1496              
1497 5 50 33     28 croak "Math::MatrixReal::order_LR(): not an LR decomposition matrix"
1498             unless ((defined $matrix->[3]) && ($rows == $cols));
1499              
1500             ZERO:
1501 5         20 for ( $order = ($rows - 1); $order >= 0; $order-- )
1502             {
1503 10 100       31 last ZERO if ($matrix->[0][$order][$order] != 0);
1504             }
1505 5         22 return(++$order);
1506             }
1507              
1508             sub scalar_product
1509             {
1510 2 50   2 0 12 croak "Usage: \$scalar_product = \$vector1->scalar_product(\$vector2);"
1511             if (@_ != 2);
1512              
1513 2         3 my($vector1,$vector2) = @_;
1514 2         5 my($rows1,$cols1) = ($vector1->[1],$vector1->[2]);
1515 2         3 my($rows2,$cols2) = ($vector2->[1],$vector2->[2]);
1516              
1517 2 50       6 croak "Math::MatrixReal::scalar_product(): 1st vector is not a column vector"
1518             unless ($cols1 == 1);
1519              
1520 2 50       5 croak "Math::MatrixReal::scalar_product(): 2nd vector is not a column vector"
1521             unless ($cols2 == 1);
1522              
1523 2 50       5 croak "Math::MatrixReal::scalar_product(): vector size mismatch"
1524             unless ($rows1 == $rows2);
1525              
1526 2         3 my $sum = 0;
1527 2         5 map { $sum += $vector1->[0][$_][0] * $vector2->[0][$_][0] } ( 0 .. $rows1-1);
  6         16  
1528 2         12 return $sum;
1529             }
1530              
1531             sub vector_product
1532             {
1533 1 50   1 0 9 croak "Usage: \$vector_product = \$vector1->vector_product(\$vector2);" if (@_ != 2);
1534              
1535 1         2 my($vector1,$vector2) = @_;
1536 1         4 my($rows1,$cols1) = ($vector1->[1],$vector1->[2]);
1537 1         2 my($rows2,$cols2) = ($vector2->[1],$vector2->[2]);
1538 1         3 my($temp);
1539             my($n);
1540              
1541 1 50       3 croak "Math::MatrixReal::vector_product(): 1st vector is not a column vector"
1542             unless ($cols1 == 1);
1543              
1544 1 50       4 croak "Math::MatrixReal::vector_product(): 2nd vector is not a column vector"
1545             unless ($cols2 == 1);
1546              
1547 1 50       4 croak "Math::MatrixReal::vector_product(): vector size mismatch"
1548             unless ($rows1 == $rows2);
1549              
1550 1         1 $n = $rows1;
1551              
1552 1 50       28 croak "Math::MatrixReal::vector_product(): only defined for 3 dimensions"
1553             unless ($n == 3);
1554              
1555 1         4 $temp = $vector1->new($n,1);
1556 1         7 $temp->[0][0][0] = $vector1->[0][1][0] * $vector2->[0][2][0] -
1557             $vector1->[0][2][0] * $vector2->[0][1][0];
1558 1         6 $temp->[0][1][0] = $vector1->[0][2][0] * $vector2->[0][0][0] -
1559             $vector1->[0][0][0] * $vector2->[0][2][0];
1560 1         5 $temp->[0][2][0] = $vector1->[0][0][0] * $vector2->[0][1][0] -
1561             $vector1->[0][1][0] * $vector2->[0][0][0];
1562 1         3 return($temp);
1563             }
1564              
1565             sub length
1566             {
1567 2 50   2 0 26 croak "Usage: \$length = \$vector->length();" if (@_ != 1);
1568              
1569 2         3 my($vector) = @_;
1570 2         5 my($rows,$cols) = ($vector->[1],$vector->[2]);
1571 2         3 my($k,$comp,$sum);
1572              
1573 2 50 33     11 croak "Math::MatrixReal::length(): vector is not a row or column vector"
1574             unless ($cols == 1 || $rows ==1 );
1575              
1576 2 50       6 $vector = ~$vector if ($rows == 1 );
1577              
1578 2         3 $sum = 0;
1579 2         7 for ( $k = 0; $k < $rows; $k++ )
1580             {
1581 6         12 $comp = $vector->[0][$k][0];
1582 6         15 $sum += $comp * $comp;
1583             }
1584 2         6 return sqrt $sum;
1585             }
1586              
1587             sub _init_iteration
1588             {
1589 0 0   0   0 croak "Usage: \$which_norm = \$matrix->_init_iteration();"
1590             if (@_ != 1);
1591              
1592 0         0 my($matrix) = @_;
1593 0         0 my($rows,$cols) = ($matrix->[1],$matrix->[2]);
1594 0         0 my($ok,$max,$sum,$norm);
1595 0         0 my($i,$j,$n);
1596              
1597 0 0       0 croak "Math::MatrixReal::_init_iteration(): matrix is not quadratic"
1598             unless ($rows == $cols);
1599              
1600 0         0 $ok = 1;
1601 0         0 $n = $rows;
1602 0         0 for ( $i = 0; $i < $n; $i++ )
1603             {
1604 0 0       0 if ($matrix->[0][$i][$i] == 0) { $ok = 0; }
  0         0  
1605             }
1606 0 0       0 if ($ok)
1607             {
1608 0         0 $norm = 1; # norm_one
1609 0         0 $max = 0;
1610 0         0 for ( $j = 0; $j < $n; $j++ )
1611             {
1612 0         0 $sum = 0;
1613 0         0 for ( $i = 0; $i < $j; $i++ )
1614             {
1615 0         0 $sum += abs($matrix->[0][$i][$j]);
1616             }
1617 0         0 for ( $i = ($j + 1); $i < $n; $i++ )
1618             {
1619 0         0 $sum += abs($matrix->[0][$i][$j]);
1620             }
1621 0         0 $sum /= abs($matrix->[0][$j][$j]);
1622 0 0       0 if ($sum > $max) { $max = $sum; }
  0         0  
1623             }
1624 0         0 $ok = ($max < 1);
1625 0 0       0 unless ($ok)
1626             {
1627 0         0 $norm = -1; # norm_max
1628 0         0 $max = 0;
1629 0         0 for ( $i = 0; $i < $n; $i++ )
1630             {
1631 0         0 $sum = 0;
1632 0         0 for ( $j = 0; $j < $i; $j++ )
1633             {
1634 0         0 $sum += abs($matrix->[0][$i][$j]);
1635             }
1636 0         0 for ( $j = ($i + 1); $j < $n; $j++ )
1637             {
1638 0         0 $sum += abs($matrix->[0][$i][$j]);
1639             }
1640 0         0 $sum /= abs($matrix->[0][$i][$i]);
1641 0 0       0 if ($sum > $max) { $max = $sum; }
  0         0  
1642             }
1643 0         0 $ok = ($max < 1)
1644             }
1645             }
1646 0 0       0 if ($ok) { return($norm); }
  0         0  
1647 0         0 else { return(0); }
1648             }
1649              
1650             sub solve_GSM # Global Step Method
1651             {
1652 0 0   0 0 0 croak "Usage: \$xn_vector = \$matrix->solve_GSM(\$x0_vector,\$b_vector,\$epsilon);"
1653             if (@_ != 4);
1654              
1655 0         0 my($matrix,$x0_vector,$b_vector,$epsilon) = @_;
1656 0         0 my($rows1,$cols1) = ( $matrix->[1], $matrix->[2]);
1657 0         0 my($rows2,$cols2) = ($x0_vector->[1],$x0_vector->[2]);
1658 0         0 my($rows3,$cols3) = ( $b_vector->[1], $b_vector->[2]);
1659 0         0 my($norm,$sum,$diff);
1660 0         0 my($xn_vector);
1661 0         0 my($i,$j,$n);
1662              
1663 0 0       0 croak "Math::MatrixReal::solve_GSM(): matrix is not quadratic"
1664             unless ($rows1 == $cols1);
1665              
1666 0         0 $n = $rows1;
1667              
1668 0 0       0 croak "Math::MatrixReal::solve_GSM(): 1st vector is not a column vector"
1669             unless ($cols2 == 1);
1670              
1671 0 0       0 croak "Math::MatrixReal::solve_GSM(): 2nd vector is not a column vector"
1672             unless ($cols3 == 1);
1673              
1674 0 0 0     0 croak "Math::MatrixReal::solve_GSM(): matrix and vector size mismatch"
1675             unless (($rows2 == $n) && ($rows3 == $n));
1676              
1677 0 0       0 return() unless ($norm = $matrix->_init_iteration());
1678              
1679 0         0 $xn_vector = $x0_vector->new($n,1);
1680              
1681 0         0 $diff = $epsilon + 1;
1682 0         0 while ($diff >= $epsilon)
1683             {
1684 0         0 for ( $i = 0; $i < $n; $i++ )
1685             {
1686 0         0 $sum = $b_vector->[0][$i][0];
1687 0         0 for ( $j = 0; $j < $i; $j++ )
1688             {
1689 0         0 $sum -= $matrix->[0][$i][$j] * $x0_vector->[0][$j][0];
1690             }
1691 0         0 for ( $j = ($i + 1); $j < $n; $j++ )
1692             {
1693 0         0 $sum -= $matrix->[0][$i][$j] * $x0_vector->[0][$j][0];
1694             }
1695 0         0 $xn_vector->[0][$i][0] = $sum / $matrix->[0][$i][$i];
1696             }
1697 0         0 $x0_vector->subtract($x0_vector,$xn_vector);
1698 0 0       0 if ($norm > 0) { $diff = $x0_vector->norm_one(); }
  0         0  
1699 0         0 else { $diff = $x0_vector->norm_max(); }
1700 0         0 for ( $i = 0; $i < $n; $i++ )
1701             {
1702 0         0 $x0_vector->[0][$i][0] = $xn_vector->[0][$i][0];
1703             }
1704             }
1705 0         0 return($xn_vector);
1706             }
1707              
1708             sub solve_SSM # Single Step Method
1709             {
1710 0 0   0 0 0 croak "Usage: \$xn_vector = \$matrix->solve_SSM(\$x0_vector,\$b_vector,\$epsilon);"
1711             if (@_ != 4);
1712              
1713 0         0 my($matrix,$x0_vector,$b_vector,$epsilon) = @_;
1714 0         0 my($rows1,$cols1) = ( $matrix->[1], $matrix->[2]);
1715 0         0 my($rows2,$cols2) = ($x0_vector->[1],$x0_vector->[2]);
1716 0         0 my($rows3,$cols3) = ( $b_vector->[1], $b_vector->[2]);
1717 0         0 my($norm,$sum,$diff);
1718 0         0 my($xn_vector);
1719 0         0 my($i,$j,$n);
1720              
1721 0 0       0 croak "Math::MatrixReal::solve_SSM(): matrix is not quadratic"
1722             unless ($rows1 == $cols1);
1723              
1724 0         0 $n = $rows1;
1725              
1726 0 0       0 croak "Math::MatrixReal::solve_SSM(): 1st vector is not a column vector"
1727             unless ($cols2 == 1);
1728              
1729 0 0       0 croak "Math::MatrixReal::solve_SSM(): 2nd vector is not a column vector"
1730             unless ($cols3 == 1);
1731              
1732 0 0 0     0 croak "Math::MatrixReal::solve_SSM(): matrix and vector size mismatch"
1733             unless (($rows2 == $n) && ($rows3 == $n));
1734              
1735 0 0       0 return() unless ($norm = $matrix->_init_iteration());
1736              
1737 0         0 $xn_vector = $x0_vector->new($n,1);
1738 0         0 $xn_vector->copy($x0_vector);
1739              
1740 0         0 $diff = $epsilon + 1;
1741 0         0 while ($diff >= $epsilon)
1742             {
1743 0         0 for ( $i = 0; $i < $n; $i++ )
1744             {
1745 0         0 $sum = $b_vector->[0][$i][0];
1746 0         0 for ( $j = 0; $j < $i; $j++ )
1747             {
1748 0         0 $sum -= $matrix->[0][$i][$j] * $xn_vector->[0][$j][0];
1749             }
1750 0         0 for ( $j = ($i + 1); $j < $n; $j++ )
1751             {
1752 0         0 $sum -= $matrix->[0][$i][$j] * $xn_vector->[0][$j][0];
1753             }
1754 0         0 $xn_vector->[0][$i][0] = $sum / $matrix->[0][$i][$i];
1755             }
1756 0         0 $x0_vector->subtract($x0_vector,$xn_vector);
1757 0 0       0 if ($norm > 0) { $diff = $x0_vector->norm_one(); }
  0         0  
1758 0         0 else { $diff = $x0_vector->norm_max(); }
1759 0         0 for ( $i = 0; $i < $n; $i++ )
1760             {
1761 0         0 $x0_vector->[0][$i][0] = $xn_vector->[0][$i][0];
1762             }
1763             }
1764 0         0 return($xn_vector);
1765             }
1766              
1767             sub solve_RM # Relaxation Method
1768             {
1769 0 0   0 0 0 croak "Usage: \$xn_vector = \$matrix->solve_RM(\$x0_vector,\$b_vector,\$weight,\$epsilon);"
1770             if (@_ != 5);
1771              
1772 0         0 my($matrix,$x0_vector,$b_vector,$weight,$epsilon) = @_;
1773 0         0 my($rows1,$cols1) = ( $matrix->[1], $matrix->[2]);
1774 0         0 my($rows2,$cols2) = ($x0_vector->[1],$x0_vector->[2]);
1775 0         0 my($rows3,$cols3) = ( $b_vector->[1], $b_vector->[2]);
1776 0         0 my($norm,$sum,$diff);
1777 0         0 my($xn_vector);
1778 0         0 my($i,$j,$n);
1779              
1780 0 0       0 croak "Math::MatrixReal::solve_RM(): matrix is not quadratic"
1781             unless ($rows1 == $cols1);
1782              
1783 0         0 $n = $rows1;
1784              
1785 0 0       0 croak "Math::MatrixReal::solve_RM(): 1st vector is not a column vector"
1786             unless ($cols2 == 1);
1787              
1788 0 0       0 croak "Math::MatrixReal::solve_RM(): 2nd vector is not a column vector"
1789             unless ($cols3 == 1);
1790              
1791 0 0 0     0 croak "Math::MatrixReal::solve_RM(): matrix and vector size mismatch"
1792             unless (($rows2 == $n) && ($rows3 == $n));
1793              
1794 0 0       0 return() unless ($norm = $matrix->_init_iteration());
1795              
1796 0         0 $xn_vector = $x0_vector->new($n,1);
1797 0         0 $xn_vector->copy($x0_vector);
1798              
1799 0         0 $diff = $epsilon + 1;
1800 0         0 while ($diff >= $epsilon)
1801             {
1802 0         0 for ( $i = 0; $i < $n; $i++ )
1803             {
1804 0         0 $sum = $b_vector->[0][$i][0];
1805 0         0 for ( $j = 0; $j < $i; $j++ )
1806             {
1807 0         0 $sum -= $matrix->[0][$i][$j] * $xn_vector->[0][$j][0];
1808             }
1809 0         0 for ( $j = ($i + 1); $j < $n; $j++ )
1810             {
1811 0         0 $sum -= $matrix->[0][$i][$j] * $xn_vector->[0][$j][0];
1812             }
1813 0         0 $xn_vector->[0][$i][0] = $weight * ( $sum / $matrix->[0][$i][$i] )
1814             + (1 - $weight) * $xn_vector->[0][$i][0];
1815             }
1816 0         0 $x0_vector->subtract($x0_vector,$xn_vector);
1817 0 0       0 if ($norm > 0) { $diff = $x0_vector->norm_one(); }
  0         0  
1818 0         0 else { $diff = $x0_vector->norm_max(); }
1819 0         0 for ( $i = 0; $i < $n; $i++ )
1820             {
1821 0         0 $x0_vector->[0][$i][0] = $xn_vector->[0][$i][0];
1822             }
1823             }
1824 0         0 return($xn_vector);
1825             }
1826              
1827             # Core householder reduction routine (when eigenvector
1828             # are wanted).
1829             # Adapted from: Numerical Recipes, 2nd edition.
1830             sub _householder_vectors ($)
1831             {
1832 38     38   64 my ($Q) = @_;
1833 38         121 my ($rows, $cols) = ($Q->[1], $Q->[2]);
1834            
1835             # Creates tridiagonal
1836             # Set up tridiagonal needed elements
1837 38         79 my $d = []; # N Diagonal elements 0...n-1
1838 38         56 my $e = []; # N-1 Off-Diagonal elements 0...n-2
1839            
1840 38         86 my @p = ();
1841 38         144 for (my $i = ($rows-1); $i > 1; $i--)
1842             {
1843 814         942 my $scale = 0.0;
1844             # Computes norm of one column (below diagonal)
1845 814         1740 for (my $k = 0; $k < $i; $k++)
1846             {
1847 11150         26611 $scale += abs($Q->[0][$i][$k]);
1848             }
1849 814 50       1498 if ($scale == 0.0)
1850             { # skip the transformation
1851 0         0 $e->[$i-1] = $Q->[0][$i][$i-1];
1852             }
1853             else
1854             {
1855 814         1138 my $h = 0.0;
1856 814         1807 for (my $k = 0; $k < $i; $k++)
1857             { # Used scaled Q for transformation
1858 11150         19339 $Q->[0][$i][$k] /= $scale;
1859             # Form sigma in h
1860 11150         32256 $h += $Q->[0][$i][$k] * $Q->[0][$i][$k];
1861             }
1862 814         1371 my $t1 = $Q->[0][$i][$i-1];
1863 814 100       1737 my $t2 = (($t1 >= 0.0) ? -sqrt($h) : sqrt($h));
1864 814         1501 $e->[$i-1] = $scale * $t2; # Update off-diagonals
1865 814         954 $h -= $t1 * $t2;
1866 814         1257 $Q->[0][$i][$i-1] -= $t2;
1867 814         848 my $f = 0.0;
1868 814         1715 for (my $j = 0; $j < $i; $j++)
1869             {
1870 11150         19391 $Q->[0][$j][$i] = $Q->[0][$i][$j] / $h;
1871 11150         11134 my $g = 0.0;
1872 11150         21210 for (my $k = 0; $k <= $j; $k++)
1873             {
1874 108062         264728 $g += $Q->[0][$j][$k] * $Q->[0][$i][$k];
1875             }
1876 11150         25190 for (my $k = $j+1; $k < $i; $k++)
1877             {
1878 96912         237844 $g += $Q->[0][$k][$j] * $Q->[0][$i][$k];
1879             }
1880             # Form elements of P
1881 11150         18233 $p[$j] = $g / $h;
1882 11150         32826 $f += $p[$j] * $Q->[0][$i][$j];
1883             }
1884 814         1022 my $hh = $f / ($h + $h);
1885 814         1910 for (my $j = 0; $j < $i; $j++)
1886             {
1887 11150         20365 my $t3 = $Q->[0][$i][$j];
1888 11150         14852 my $t4 = $p[$j] - $hh * $t3;
1889 11150         11659 $p[$j] = $t4;
1890 11150         22139 for (my $k = 0; $k <= $j; $k++)
1891             {
1892 108062         308542 $Q->[0][$j][$k] -= $t3 * $p[$k]
1893             + $t4 * $Q->[0][$i][$k];
1894             }
1895             }
1896             }
1897             }
1898             # Updates for i == 0,1
1899 38         124 $e->[0] = $Q->[0][1][0];
1900 38         159 $d->[0] = $Q->[0][0][0]; # i==0
1901 38         94 $Q->[0][0][0] = 1.0;
1902 38         98 $d->[1] = $Q->[0][1][1]; # i==1
1903 38         87 $Q->[0][1][1] = 1.0;
1904 38         123 $Q->[0][1][0] = $Q->[0][0][1] = 0.0;
1905 38         244 for (my $i = 2; $i < $rows; $i++)
1906             {
1907 814         1648 for (my $j = 0; $j < $i; $j++)
1908             {
1909 11150         11665 my $g = 0.0;
1910 11150         26983 for (my $k = 0; $k < $i; $k++)
1911             {
1912 204974         539298 $g += $Q->[0][$i][$k] * $Q->[0][$k][$j];
1913             }
1914 11150         21678 for (my $k = 0; $k < $i; $k++)
1915             {
1916 204974         542343 $Q->[0][$k][$j] -= $g * $Q->[0][$k][$i];
1917             }
1918             }
1919 814         2003 $d->[$i] = $Q->[0][$i][$i];
1920             # Reset row and column of Q for next iteration
1921 814         1244 $Q->[0][$i][$i] = 1.0;
1922 814         1740 for (my $j = 0; $j < $i; $j++)
1923             {
1924 11150         27540 $Q->[0][$i][$j] = $Q->[0][$j][$i] = 0.0;
1925             }
1926             }
1927 38         327 return ($d, $e);
1928             }
1929              
1930             # Computes sqrt(a*a + b*b), but more carefully...
1931             sub _pythag ($$)
1932             {
1933 60134     60134   80989 my ($a, $b) = @_;
1934 60134         72545 my $aa = abs($a);
1935 60134         75705 my $ab = abs($b);
1936 60134 100       116209 if ($aa > $ab)
1937             {
1938             # NB: Not needed!: return 0.0 if ($aa == 0.0);
1939 17613         23640 my $t = $ab / $aa;
1940 17613         38586 return ($aa * sqrt(1.0 + $t*$t));
1941             } else {
1942 42521 50       80376 return 0.0 if ($ab == 0.0);
1943 42521         48370 my $t = $aa / $ab;
1944 42521         94133 return ($ab * sqrt(1.0 + $t*$t));
1945             }
1946             }
1947              
1948             # QL algorithm with implicit shifts to determine the eigenvalues
1949             # of a tridiagonal matrix. Internal routine.
1950             sub _tridiagonal_QLimplicit
1951             {
1952 38     38   72 my ($EV, $d, $e) = @_;
1953 38         102 my ($rows, $cols) = ($EV->[1], $EV->[2]);
1954              
1955 38         94 $e->[$rows-1] = 0.0;
1956             # Start real computation
1957 38         107 for (my $l = 0; $l < $rows; $l++)
1958             {
1959 890         1091 my $iter = 0;
1960 890         1049 my $m;
1961             OUTER:
1962 890         1084 do {
1963 2804         6946 for ($m = $l; $m < ($rows - 1); $m++)
1964             {
1965 29106         51659 my $dd = abs($d->[$m]) + abs($d->[$m+1]);
1966 29106 100       89213 last if ((abs($e->[$m]) + $dd) == $dd);
1967             }
1968 2804 100       8328 if ($m != $l)
1969             {
1970             ## why only allow 30 iterations?
1971 1914 50       4475 croak("Too many iterations!") if ($iter++ >= 30);
1972 1914         3950 my $g = ($d->[$l+1] - $d->[$l])
1973             / (2.0 * $e->[$l]);
1974 1914         8647 my $r = _pythag($g, 1.0);
1975 1914 100       5654 $g = $d->[$m] - $d->[$l]
1976             + $e->[$l] / ($g + (($g >= 0.0) ? abs($r) : -abs($r)));
1977 1914         2737 my ($p,$s,$c) = (0.0, 1.0,1.0);
1978 1914         4632 for (my $i = ($m-1); $i >= $l; $i--)
1979             {
1980 27060         35704 my $ii = $i + 1;
1981 27060         34639 my $f = $s * $e->[$i];
1982 27060         45819 my $t = _pythag($f, $g);
1983 27060         37614 $e->[$ii] = $t;
1984 27060 50       53735 if ($t == 0.0)
1985             {
1986 0         0 $d->[$ii] -= $p;
1987 0         0 $e->[$m] = 0.0;
1988 0         0 next OUTER;
1989             }
1990 27060         36559 my $b = $c * $e->[$i];
1991 27060         30701 $s = $f / $t;
1992 27060         27438 $c = $g / $t;
1993 27060         32896 $g = $d->[$ii] - $p;
1994 27060         45744 my $t2 = ($d->[$i] - $g) * $s + 2.0 * $c * $b;
1995 27060         38060 $p = $s * $t2;
1996 27060         33476 $d->[$ii] = $g + $p;
1997 27060         29775 $g = $c * $t2 - $b;
1998 27060         63099 for (my $k = 0; $k < $rows; $k++)
1999             {
2000 753266         1042340 my $t1 = $EV->[0][$k][$ii];
2001 753266         959937 my $t2 = $EV->[0][$k][$i];
2002 753266         1169695 $EV->[0][$k][$ii] = $s * $t2 + $c * $t1;
2003 753266         2006312 $EV->[0][$k][$i] = $c * $t2 - $s * $t1;
2004             }
2005             }
2006 1914         2612 $d->[$l] -= $p;
2007 1914         2432 $e->[$l] = $g;
2008 1914         11296 $e->[$m] = 0.0;
2009             }
2010             } while ($m != $l);
2011             }
2012 38         120 return;
2013             }
2014              
2015             # Core householder reduction routine (when eigenvector
2016             # are NOT wanted).
2017             sub _householder_values ($)
2018             {
2019 45     45   81 my ($Q) = @_; # NB: Q is destroyed on output...
2020 45         99 my ($rows, $cols) = ($Q->[1], $Q->[2]);
2021            
2022             # Creates tridiagonal
2023             # Set up tridiagonal needed elements
2024 45         77 my $d = []; # N Diagonal elements 0...n-1
2025 45         69 my $e = []; # N-1 Off-Diagonal elements 0...n-2
2026            
2027 45         69 my @p = ();
2028 45         133 for (my $i = ($rows - 1); $i > 1; $i--)
2029             {
2030 849         937 my $scale = 0.0;
2031 849         1742 for (my $k = 0; $k < $i; $k++)
2032             {
2033 12110         29421 $scale += abs($Q->[0][$i][$k]);
2034             }
2035 849 100       1675 if ($scale == 0.0)
2036             { # skip the transformation
2037 15         46 $e->[$i-1] = $Q->[0][$i][$i-1];
2038             }
2039             else
2040             {
2041 834         927 my $h = 0.0;
2042 834         1816 for (my $k = 0; $k < $i; $k++)
2043             { # Used scaled Q for transformation
2044 12065         15142 $Q->[0][$i][$k] /= $scale;
2045             # Form sigma in h
2046 12065         34064 $h += $Q->[0][$i][$k] * $Q->[0][$i][$k];
2047             }
2048 834         1388 my $t = $Q->[0][$i][$i-1];
2049 834 100       1695 my $t2 = (($t >= 0.0) ? -sqrt($h) : sqrt($h));
2050 834         1452 $e->[$i-1] = $scale * $t2; # Updates off-diagonal
2051 834         1166 $h -= $t * $t2;
2052 834         1287 $Q->[0][$i][$i-1] -= $t2;
2053 834         866 my $f = 0.0;
2054 834         1689 for (my $j = 0; $j < $i; $j++)
2055             {
2056 12065         12157 my $g = 0.0;
2057 12065         23364 for (my $k = 0; $k <= $j; $k++)
2058             {
2059 133392         324122 $g += $Q->[0][$j][$k] * $Q->[0][$i][$k];
2060             }
2061 12065         26622 for (my $k = $j+1; $k < $i; $k++)
2062             {
2063 121327         308142 $g += $Q->[0][$k][$j] * $Q->[0][$i][$k];
2064             }
2065             # Form elements of P
2066 12065         14850 $p[$j] = $g / $h;
2067 12065         33761 $f += $p[$j] * $Q->[0][$i][$j];
2068             }
2069 834         1111 my $hh = $f / ($h + $h);
2070 834         1624 for (my $j = 0; $j < $i; $j++)
2071             {
2072 12065         16434 my $t = $Q->[0][$i][$j];
2073 12065         15523 my $g = $p[$j] - $hh * $t;
2074 12065         12741 $p[$j] = $g;
2075 12065         22859 for (my $k = 0; $k <= $j; $k++)
2076             {
2077 133392         382989 $Q->[0][$j][$k] -= $t * $p[$k]
2078             + $g * $Q->[0][$i][$k];
2079             }
2080             }
2081             }
2082             }
2083             # Updates for i==1
2084 45         256 $e->[0] = $Q->[0][1][0];
2085             # Updates diagonal elements
2086 45         149 for (my $i = 0; $i < $rows; $i++)
2087             {
2088 939         2556 $d->[$i] = $Q->[0][$i][$i];
2089             }
2090 45         322 return ($d, $e);
2091             }
2092              
2093             # QL algorithm with implicit shifts to determine the
2094             # eigenvalues ONLY. This is O(N^2) only...
2095             sub _tridiagonal_QLimplicit_values
2096             {
2097 45     45   93 my ($M, $d, $e) = @_; # NB: M is not touched...
2098 45         109 my ($rows, $cols) = ($M->[1], $M->[2]);
2099              
2100 45         94 $e->[$rows-1] = 0.0;
2101             # Start real computation
2102 45         153 for (my $l = 0; $l < $rows; $l++)
2103             {
2104 939         1052 my $iter = 0;
2105 939         934 my $m;
2106             OUTER:
2107 939         989 do {
2108 2895         6356 for ($m = $l; $m < ($rows - 1); $m++)
2109             {
2110 31331         49742 my $dd = abs($d->[$m]) + abs($d->[$m+1]);
2111 31331 100       127617 last if ((abs($e->[$m]) + $dd) == $dd);
2112             }
2113 2895 100       7368 if ($m != $l)
2114             {
2115 1956 50       3852 croak("Too many iterations!") if ($iter++ >= 30);
2116 1956         3588 my $g = ($d->[$l+1] - $d->[$l])
2117             / (2.0 * $e->[$l]);
2118 1956         3323 my $r = _pythag($g, 1.0);
2119 1956 100       5853 $g = $d->[$m] - $d->[$l]
2120             + $e->[$l] / ($g + (($g >= 0.0) ? abs($r) : -abs($r)));
2121 1956         2680 my ($p,$s,$c) = (0.0, 1.0,1.0);
2122 1956         4532 for (my $i = ($m-1); $i >= $l; $i--)
2123             {
2124 29204         34055 my $ii = $i + 1;
2125 29204         38463 my $f = $s * $e->[$i];
2126 29204         74151 my $t = _pythag($f, $g);
2127 29204         41346 $e->[$ii] = $t;
2128 29204 50       63465 if ($t == 0.0)
2129             {
2130 0         0 $d->[$ii] -= $p;
2131 0         0 $e->[$m] = 0.0;
2132 0         0 next OUTER;
2133             }
2134 29204         35317 my $b = $c * $e->[$i];
2135 29204         30206 $s = $f / $t;
2136 29204         28215 $c = $g / $t;
2137 29204         33712 $g = $d->[$ii] - $p;
2138 29204         46628 my $t2 = ($d->[$i] - $g) * $s + 2.0 * $c * $b;
2139 29204         32887 $p = $s * $t2;
2140 29204         35031 $d->[$ii] = $g + $p;
2141 29204         70522 $g = $c * $t2 - $b;
2142             }
2143 1956         2770 $d->[$l] -= $p;
2144 1956         2120 $e->[$l] = $g;
2145 1956         5032 $e->[$m] = 0.0;
2146             }
2147             } while ($m != $l);
2148             }
2149 45         145 return;
2150             }
2151              
2152             # Householder reduction of a real, symmetric matrix A.
2153             # Returns a tridiagonal matrix T and the orthogonal matrix
2154             # Q effecting the transformation between A and T.
2155             sub householder ($)
2156             {
2157 33     33 0 13502 my ($A) = @_;
2158 33         101 my ($rows, $cols) = ($A->[1], $A->[2]);
2159              
2160 33 50       106 croak "Matrix is not quadratic"
2161             unless ($rows = $cols);
2162 33 50       133 croak "Matrix is not symmetric"
2163             unless ($A->is_symmetric());
2164              
2165             # Copy given matrix TODO: study if we should do in-place modification
2166 33         164 my $Q = $A->clone();
2167              
2168             # Do the computation of tridiagonal elements and of
2169             # transformation matrix
2170 33         171 my ($diag, $offdiag) = $Q->_householder_vectors();
2171              
2172             # Creates the tridiagonal matrix
2173 33         257 my $T = $A->shadow();
2174 33         145 for (my $i = 0; $i < $rows; $i++)
2175             { # Set diagonal
2176 790         1692 $T->[0][$i][$i] = $diag->[$i];
2177             }
2178 33         132 for (my $i = 0; $i < ($rows-1); $i++)
2179             { # Set off diagonals
2180 757         1039 $T->[0][$i+1][$i] = $offdiag->[$i];
2181 757         1782 $T->[0][$i][$i+1] = $offdiag->[$i];
2182             }
2183 33         3431 return ($T, $Q);
2184             }
2185              
2186             # QL algorithm with implicit shifts to determine the eigenvalues
2187             # and eigenvectors of a real tridiagonal matrix - or of a matrix
2188             # previously reduced to tridiagonal form.
2189             sub tri_diagonalize ($;$)
2190             {
2191 33     33 0 18474 my ($T,$Q) = @_; # Q may be 0 if the original matrix is really tridiagonal
2192              
2193 33         122 my ($rows, $cols) = ($T->[1], $T->[2]);
2194              
2195 33 50       128 croak "Matrix is not quadratic"
2196             unless ($rows = $cols);
2197 33 50       163 croak "Matrix is not tridiagonal"
2198             unless ($T->is_tridiagonal()); # DONE
2199              
2200 33         106 my $EV;
2201             # Obtain/Creates the todo eigenvectors matrix
2202 33 50       226 if ($Q)
2203             {
2204 33         234 $EV = $Q->clone();
2205             }
2206             else
2207             {
2208 0         0 $EV = $T->shadow();
2209 0         0 $EV->one();
2210             }
2211             # Allocates diagonal vector
2212 33         68 my $diag = [ ];
2213             # Initializes it with T
2214 33         103 for (my $i = 0; $i < $rows; $i++)
2215             {
2216 790         2029 $diag->[$i] = $T->[0][$i][$i];
2217             }
2218             # Allocate temporary vector for off-diagonal elements
2219 33         62 my $offdiag = [ ];
2220 33         117 for (my $i = 1; $i < $rows; $i++)
2221             {
2222 757         2042 $offdiag->[$i-1] = $T->[0][$i][$i-1];
2223             }
2224              
2225             # Calls the calculus routine
2226 33         169 $EV->_tridiagonal_QLimplicit($diag, $offdiag);
2227              
2228             # Allocate eigenvalues vector
2229 33         280 my $v = Math::MatrixReal->new($rows,1);
2230             # Fills it
2231 33         115 for (my $i = 0; $i < $rows; $i++)
2232             {
2233 790         1914 $v->[0][$i][0] = $diag->[$i];
2234             }
2235 33         3297 return ($v, $EV);
2236             }
2237              
2238             # Main routine for diagonalization of a real symmetric
2239             # matrix M. Operates by transforming M into a tridiagonal
2240             # matrix and then obtaining the eigenvalues and eigenvectors
2241             # for that matrix (taking into account the transformation to
2242             # tridiagonal).
2243             sub sym_diagonalize ($)
2244             {
2245 5     5 0 36094 my ($M) = @_;
2246 5         20 my ($rows, $cols) = ($M->[1], $M->[2]);
2247            
2248 5 50       26 croak "Matrix is not quadratic"
2249             unless ($rows = $cols);
2250 5 50       29 croak "Matrix is not symmetric"
2251             unless ($M->is_symmetric());
2252            
2253             # Copy initial matrix
2254             # TODO: study if we should allow in-place modification
2255 5         29 my $VEC = $M->clone();
2256              
2257             # Do the computation of tridiagonal elements and of
2258             # transformation matrix
2259 5         37 my ($diag, $offdiag) = $VEC->_householder_vectors();
2260             # Calls the calculus routine for diagonalization
2261 5         39 $VEC->_tridiagonal_QLimplicit($diag, $offdiag);
2262              
2263             # Allocate eigenvalues vector
2264 5         46 my $val = Math::MatrixReal->new($rows,1);
2265             # Fills it
2266 5         26 for (my $i = 0; $i < $rows; $i++)
2267             {
2268 100         247 $val->[0][$i][0] = $diag->[$i];
2269             }
2270 5         340 return ($val, $VEC);
2271             }
2272              
2273             # Householder reduction of a real, symmetric matrix A.
2274             # Returns a tridiagonal matrix T equivalent to A.
2275             sub householder_tridiagonal ($)
2276             {
2277 33     33 0 21349 my ($A) = @_;
2278 33         193 my ($rows, $cols) = ($A->[1], $A->[2]);
2279              
2280 33 50       127 croak "Matrix is not quadratic"
2281             unless ($rows = $cols);
2282 33 50       127 croak "Matrix is not symmetric"
2283             unless ($A->is_symmetric());
2284              
2285             # Copy given matrix
2286 33         170 my $Q = $A->clone();
2287              
2288             # Do the computation of tridiagonal elements and of
2289             # transformation matrix
2290             # Q is destroyed after reduction
2291 33         132 my ($diag, $offdiag) = $Q->_householder_values();
2292              
2293             # Creates the tridiagonal matrix in Q (avoid allocation)
2294 33         67 my $T = $Q;
2295 33         256 $T->zero();
2296 33         112 for (my $i = 0; $i < $rows; $i++)
2297             { # Set diagonal
2298 790         1619 $T->[0][$i][$i] = $diag->[$i];
2299             }
2300 33         117 for (my $i = 0; $i < ($rows-1); $i++)
2301             { # Set off diagonals
2302 757         1060 $T->[0][$i+1][$i] = $offdiag->[$i];
2303 757         1662 $T->[0][$i][$i+1] = $offdiag->[$i];
2304             }
2305 33         2999 return $T;
2306             }
2307              
2308             # QL algorithm with implicit shifts to determine ONLY
2309             # the eigenvalues a real tridiagonal matrix - or of a
2310             # matrix previously reduced to tridiagonal form.
2311             sub tri_eigenvalues ($;$)
2312             {
2313 33     33 1 18371 my ($T) = @_;
2314 33         108 my ($rows, $cols) = ($T->[1], $T->[2]);
2315              
2316 33 50       289 croak "Matrix is not quadratic"
2317             unless ($rows = $cols);
2318 33 50       166 croak "Matrix is not tridiagonal"
2319             unless ($T->is_tridiagonal() ); # DONE
2320              
2321             # Allocates diagonal vector
2322 33         149 my $diag = [ ];
2323             # Initializes it with T
2324 33         142 for (my $i = 0; $i < $rows; $i++)
2325             {
2326 790         2315 $diag->[$i] = $T->[0][$i][$i];
2327             }
2328             # Allocate temporary vector for off-diagonal elements
2329 33         82 my $offdiag = [ ];
2330 33         124 for (my $i = 1; $i < $rows; $i++)
2331             {
2332 757         2253 $offdiag->[$i-1] = $T->[0][$i][$i-1];
2333             }
2334              
2335             # Calls the calculus routine (T is not touched)
2336 33         215 $T->_tridiagonal_QLimplicit_values($diag, $offdiag);
2337              
2338             # Allocate eigenvalues vector
2339 33         304 my $v = Math::MatrixReal->new($rows,1);
2340             # Fills it
2341 33         124 for (my $i = 0; $i < $rows; $i++)
2342             {
2343 790         1899 $v->[0][$i][0] = $diag->[$i];
2344             }
2345 33         2185 return $v;
2346             }
2347              
2348             ## more general routine than sym_eigenvalues
2349             sub eigenvalues ($){
2350 19     19 0 44 my ($matrix) = @_;
2351 19         40 my ($rows,$cols) = $matrix->dim();
2352              
2353 19 50       53 croak "Matrix is not quadratic" unless ($rows == $cols);
2354              
2355 19 100 100     51 if($matrix->is_upper_triangular() || $matrix->is_lower_triangular() ){
2356 17         42 my $l = Math::MatrixReal->new($rows,1);
2357 17         34 map { $l->[0][$_][0] = $matrix->[0][$_][$_] } (0 .. $rows-1);
  64         133  
2358 17         50 return $l;
2359             }
2360              
2361 2 50       5 return sym_eigenvalues($matrix) if $matrix->is_symmetric();
2362              
2363 0         0 carp "Math::MatrixReal::eigenvalues(): Matrix is not symmetric or triangular";
2364 0         0 return undef;
2365              
2366             }
2367             # Main routine for diagonalization of a real symmetric
2368             # matrix M. Operates by transforming M into a tridiagonal
2369             # matrix and then obtaining the eigenvalues and eigenvectors
2370             # for that matrix (taking into account the transformation to
2371             # tridiagonal).
2372             sub sym_eigenvalues ($)
2373             {
2374 12     12 0 37555 my ($M) = @_;
2375 12         33 my ($rows, $cols) = ($M->[1], $M->[2]);
2376            
2377 12 50       50 croak "Matrix is not quadratic" unless ($rows == $cols);
2378 12 50       45 croak "Matrix is not symmetric" unless ($M->is_symmetric);
2379              
2380             # Copy matrix in temporary
2381 12         41 my $A = $M->clone();
2382             # Do the computation of tridiagonal elements and of
2383             # transformation matrix. A is destroyed
2384 12         46 my ($diag, $offdiag) = $A->_householder_values();
2385             # Calls the calculus routine for diagonalization
2386             # (M is not touched)
2387 12         51 $M->_tridiagonal_QLimplicit_values($diag, $offdiag);
2388              
2389             # Allocate eigenvalues vector
2390 12         84 my $val = Math::MatrixReal->new($rows,1);
2391             # Fills it
2392 12         38 map { $val->[0][$_][0] = $diag->[$_] } ( 0 .. $rows-1);
  149         258  
2393              
2394 12         459 return $val;
2395             }
2396             #TODO: docs+test
2397             sub is_positive_definite {
2398 4     4 0 23 my ($matrix) = @_;
2399 4         10 my ($r,$c) = $matrix->dim;
2400              
2401 4 50       8 croak "Math::MatrixReal::is_positive_definite(): Matrix is not square" unless ($r == $c);
2402             # must have positive (i.e REAL) eigenvalues to be positive definite
2403 4 100       11 return 0 unless $matrix->is_symmetric;
2404              
2405 3         6 my $ev = $matrix->eigenvalues;
2406 3         6 my $pos = 1;
2407 3 100   9   18 $ev->each(sub { my $x = shift; if ($x <= 0){ $pos=0;return; } } );
  9         10  
  9         43  
  2         3  
  2         34  
2408 3         22 return $pos;
2409             }
2410             #TODO: docs+test
2411             sub is_positive_semidefinite {
2412 4     4 0 22 my ($matrix) = @_;
2413 4         9 my ($r,$c) = $matrix->dim;
2414              
2415 4 50       16 croak "Math::MatrixReal::is_positive_semidefinite(): Matrix is not square" unless ($r == $c);
2416             # must have nonnegative (i.e REAL) eigenvalues to be positive semidefinite
2417 4 100       9 return 0 unless $matrix->is_symmetric;
2418              
2419 3         6 my $ev = $matrix->eigenvalues;
2420 3         4 my $pos = 1;
2421 3 100   9   13 $ev->each(sub { my $x = shift; if ($x < 0){ $pos=0;return; } } );
  9         11  
  9         44  
  1         2  
  1         4  
2422 3         21 return $pos;
2423             }
2424 0     0 0 0 sub is_row { return (shift)->is_row_vector }
2425 0     0 0 0 sub is_col { return (shift)->is_col_vector }
2426              
2427             sub is_row_vector {
2428 9     9 0 18 my ($m) = @_;
2429 9         17 my $r = $m->[1];
2430 9 100       50 $r == 1 ? 1 : 0;
2431             }
2432             sub is_col_vector {
2433 11     11 0 32 my ($m) = @_;
2434 11         19 my $c = $m->[2];
2435 11 100       58 $c == 1 ? 1 : 0;
2436             }
2437              
2438             sub is_orthogonal($) {
2439 4     4 0 21 my ($matrix) = @_;
2440              
2441 4 50       10 return 0 unless $matrix->is_quadratic;
2442              
2443 4         11 my $one = $matrix->shadow();
2444 4         12 $one->one;
2445              
2446 4 50       10 abs(~$matrix * $matrix - $one) < 1e-12 ? return 1 : return 0;
2447              
2448             }
2449              
2450             sub is_positive($) {
2451 4     4 0 27 my ($m) = @_;
2452 4         9 my $pos = 1;
2453 4 100   100   24 $m->each( sub { if( (shift) <= 0){ $pos = 0;return;} } );
  100         545  
  68         70  
  68         223  
2454 4         29 return $pos;
2455             }
2456             sub is_negative($) {
2457 4     4 0 8 my ($m) = @_;
2458 4         7 my $neg = 1;
2459 4 100   100   21 $m->each( sub { if( (shift) >= 0){ $neg = 0;return;} } );
  100         345  
  75         74  
  75         397  
2460 4         27 return $neg;
2461             }
2462              
2463              
2464             sub is_periodic($$) {
2465 7     7 0 18 my ($m,$k) = @_;
2466 7 100       22 return 0 unless $m->is_quadratic();
2467 5 50       26 abs($m**(int($k)+1) - $m) < 1e-12 ? return 1 : return 0;
2468             }
2469             sub is_idempotent($) {
2470 3     3 0 29 return (shift)->is_periodic(1);
2471             }
2472              
2473             # Boolean check routine to see if a matrix is
2474             # symmetric
2475             sub is_symmetric ($)
2476             {
2477 108     108 0 248 my ($M) = @_;
2478 108         265 my ($rows, $cols) = ($M->[1], $M->[2]);
2479             # if it is not quadratic it cannot be symmetric...
2480 108 100       306 return 0 unless ($rows == $cols);
2481             # skip when $i=$j?
2482 106         327 for (my $i = 1; $i < $rows; $i++)
2483             {
2484 1805         3356 for (my $j = 0; $j < $i; $j++)
2485             {
2486 23484 100       70391 return 0 unless ($M->[0][$i][$j] == $M->[0][$j][$i]);
2487             }
2488             }
2489 102         383 return 1;
2490             }
2491             # Boolean check to see if matrix is tridiagonal
2492             sub is_tridiagonal ($) {
2493 72     72 0 179 my ($M) = @_;
2494 72         183 my ($rows,$cols) = ($M->[1],$M->[2]);
2495 72         145 my ($i,$j) = (0,0);
2496             # if it is not quadratic it cannot be tridiag
2497 72 100       225 return 0 unless ($rows == $cols);
2498              
2499 71         228 for(;$i < $rows; $i++ ){
2500 1600         4086 for(;$j < $cols; $j++ ){
2501             #print "debug: testing $i,$j = " . $M->[0][$i][$j] . "\n";
2502             # skip diag and diag+-1
2503 39526 100       69370 next if ($i == $j);
2504 37926 100       65828 next if ($i+1 == $j);
2505 36396 100       60249 next if ($i-1 == $j);
2506 34867 100       108734 return 0 if $M->[0][$i][$j];
2507             }
2508 1599         3323 $j = 0;
2509             }
2510 70         368 return 1;
2511             }
2512             # Boolean check to see if matrix is upper triangular
2513             # i.e all nonzero elements are above main diagonal
2514             sub is_upper_triangular {
2515 196     196 0 294 my ($M) = @_;
2516 196         386 my ($rows,$cols) = $M->dim();
2517 196         280 my ($i,$j) = (1,0);
2518 196 100       445 return 0 unless ($rows == $cols);
2519            
2520 194         923 for(;$i < $rows; $i++ ){
2521 261         535 for(;$j < $cols;$j++ ){
2522 646 100       1866 next if ($i <= $j);
2523 393 100       2819 return 0 if $M->[0][$i][$j];
2524             }
2525 109         237 $j = 0;
2526             }
2527 42         123 return 1;
2528             }
2529             # Boolean check to see if matrix is lower triangular
2530             # i.e all nonzero elements are lower main diagonal
2531             sub is_lower_triangular {
2532 156     156 0 254 my ($M) = @_;
2533 156         1485 my ($rows,$cols) = $M->dim();
2534 156         254 my ($i,$j) = (0,1);
2535 156 100       378 return 0 unless ($rows == $cols);
2536              
2537 154         330 for(;$i < $rows; $i++ ){
2538 176         439 for(;$j < $cols;$j++ ){
2539 309 100       759 next if ($i >= $j);
2540 219 100       840 return 0 if $M->[0][$i][$j];
2541             }
2542 28         57 $j = 0;
2543             }
2544 6         35 return 1;
2545             }
2546              
2547             # Boolean check to see if matrix is diagonal
2548             sub is_diagonal ($) {
2549 17     17 0 70 my ($M) = @_;
2550 17         39 my ($rows,$cols) = ($M->[1],$M->[2]);
2551 17         27 my ($i,$j) = (0,0);
2552 17 100       55 return 0 unless ($rows == $cols );
2553 16         42 for(;$i < $rows; $i++ ){
2554 91         178 for(;$j < $cols; $j++ ){
2555             # skip diag elements
2556 707 100       1257 next if ($i == $j);
2557 616 100       1937 return 0 if $M->[0][$i][$j];
2558             }
2559 88         174 $j = 0;
2560             }
2561 13         46 return 1;
2562             }
2563             sub is_quadratic ($) {
2564 14 50   14 0 61 croak "Usage: \$matrix->is_quadratic()" unless (@_ == 1);
2565 14         29 my ($matrix) = @_;
2566 14 100       78 $matrix->[1] == $matrix->[2] ? return 1 : return 0;
2567             }
2568              
2569             sub is_square($) {
2570 1 50   1 0 11 croak "Usage: \$matrix->is_square()" unless (@_ == 1);
2571 1         3 return (shift)->is_quadratic();
2572             }
2573              
2574             sub is_LR($) {
2575 9 50   9 0 35 croak "Usage: \$matrix->is_LR()" unless (@_ == 1);
2576 9 100       56 return (shift)->[3] ? 1 : 0;
2577             }
2578              
2579             sub is_normal{
2580 2     2 0 11 my ($matrix,$eps) = @_;
2581 2         9 my ($rows,$cols) = $matrix->dim;
2582            
2583 2   50     13 $eps ||= 1e-8;
2584              
2585 2 100       8 (~$matrix * $matrix - $matrix * ~$matrix < $eps ) ? 1 : 0;
2586              
2587             }
2588              
2589             sub is_skew_symmetric{
2590 4     4 0 21 my ($m) = @_;
2591 4         14 my ($rows, $cols) = $m->dim;
2592             # if it is not quadratic it cannot be skew symmetric...
2593 4 100       13 return 0 unless ($rows == $cols);
2594 3         17 for (my $i = 1; $i < $rows; $i++) {
2595 11         22 for (my $j = 0; $j < $i; $j++) {
2596 26 50       88 return 0 unless ($m->[0][$i][$j] == -$m->[0][$j][$i]);
2597             }
2598             }
2599 3         12 return 1;
2600              
2601             }
2602             ####
2603             sub is_gramian{
2604 6     6 0 39 my ($m) = @_;
2605 6         14 my ($rows,$cols) = $m->dim;
2606 6         10 my $neg=0;
2607             # gramian matrix must be symmetric
2608 6 100       15 return 0 unless $m->is_symmetric;
2609              
2610             # must have all non-negative eigenvalues
2611 4         11 my $ev = $m->eigenvalues;
2612 4 100   15   22 $ev->each(sub { $neg++ if ((shift)<0) } );
  15         86  
2613              
2614 4 100       35 return $neg ? 0 : 1;
2615             }
2616             sub is_binary{
2617 4     4 0 23 my ($m) = @_;
2618 4         11 my ($rows, $cols) = $m->dim;
2619 4         12 for (my $i = 0; $i < $rows; $i++) {
2620 13         23 for (my $j = 0; $j < $cols; $j++) {
2621 62 100 100     284 return 0 unless ($m->[0][$i][$j] == 1 || $m->[0][$i][$j] == 0);
2622             }
2623             }
2624 3         12 return 1;
2625              
2626             }
2627             sub as_scilab {
2628 0     0 0 0 return (shift)->as_matlab;
2629             }
2630              
2631             sub as_matlab {
2632 2     2 0 15 my ($m) = shift;
2633 2         10 my %args = (
2634             format => "%s",
2635             name => "",
2636             semi => 0,
2637             @_);
2638 2         6 my ($row,$col) = $m->dim;
2639 2         4 my $s = "";
2640            
2641 2 100       7 if( $args{name} ){
2642 1         2 $s = "$args{name} = ";
2643             }
2644 2         4 $s .= "[";
2645             $m->each(
2646 12     12   20 sub { my($x,$i,$j) = @_;
2647 12         35 $s .= sprintf(" $args{format}",$x);
2648 12 100 100     97 $s .= ";\n" if( $j == $col && $i != $row);
2649             }
2650 2         15 );
2651 2         10 $s .= "]";
2652 2 50       7 $s .= ";" if $args{semi};
2653 2         16 return $s;
2654             }
2655             #TODO: docs+test
2656             sub as_yacas{
2657 2     2 0 20 my ($m) = shift;
2658 2         10 my %args = (
2659             format => "%s",
2660             name => "",
2661             semi => 0,
2662             @_);
2663 2         5 my ($row,$col) = $m->dim;
2664 2         4 my $s = "";
2665              
2666 2 100       5 if( $args{name} ){
2667 1         3 $s = "$args{name} := ";
2668             }
2669 2         4 $s .= "{";
2670              
2671             $m->each(
2672 12     12   13 sub { my($x,$i,$j) = @_;
2673 12 100       27 $s .= "{" if ($j == 1);
2674 12         37 $s .= sprintf("$args{format}",$x);
2675 12 100       20 $s .= "," if( $j != $col );
2676 12 100 100     85 $s .= "}," if ($j == $col && $i != $row);
2677             }
2678 2         21 );
2679 2         10 $s .= "}}";
2680              
2681 2         11 return $s;
2682             }
2683              
2684             sub as_latex{
2685 2     2 0 16 my ($m) = shift;
2686 2         16 my %args = (
2687             format => "%s",
2688             name => "",
2689             align => "c",
2690             display_math => 0,
2691             @_);
2692              
2693              
2694 2         5 my ($row,$col) = $m->dim;
2695 2         4 my $inside;
2696 2         5 my $s = <
2697             \\left( \\begin{array}{%COLS%}
2698             %INSIDE%\\end{array} \\right)
2699             LATEX
2700 2         14 $args{align} = lc $args{align};
2701 2 50       13 if( $args{align} !~ m/^(c|l|r)$/ ){
2702 0         0 croak "Math::MatrixReal::as_latex(): Invalid alignment '$args{align}'";
2703             }
2704              
2705 2         18 $s =~ s/%COLS%/$args{align} x $col/em;
  2         11  
2706              
2707 2 100       8 if( $args{name} ){
2708 1         4 $s = "$args{name} = $s";
2709             }
2710             $m->each(
2711             sub {
2712 12     12   17 my ($x,$i,$j) = @_;
2713 12         44 $x = sprintf($args{format},$x);
2714              
2715             # last element in each row gets a \\
2716 12 100 100     97 if ($j == $col && $i != $row){
    100 66        
2717 4         25 $inside .= "$x \\\\"."\n";
2718             # the annoying last line has neither
2719             } elsif( $j == $col && $i == $row){
2720 2         14 $inside .= "$x\n";
2721             } else {
2722 6         31 $inside .= "$x&";
2723             }
2724             }
2725 2         24 );
2726 2 50       16 if($args{displaymath}){
2727 0         0 $s = "\\[$s\\]";
2728             } else {
2729 2         6 $s = "\$$s\$";
2730             }
2731 2         11 $s =~ s/%INSIDE%/$inside/gm;
2732 2         20 return $s;
2733             }
2734             ####
2735             sub spectral_radius
2736             {
2737 5     5 0 23 my ($matrix) = @_;
2738 5         16 my ($r,$c) = $matrix->dim;
2739 5         22 my $ev = $matrix->eigenvalues;
2740 5         8 my $radius=0;
2741 5 100   19   61 $ev->each(sub { my $x = shift; $radius = $x if (abs($x) > $radius); } );
  19         21  
  19         127  
2742 5         40 return $radius;
2743             }
2744              
2745             sub maximum {
2746 0     0 1 0 my ($matrix) = @_;
2747 0         0 my ($rows, $columns) = $matrix->dim;
2748              
2749 0         0 my $max = [];
2750 0         0 my $max_p = [];
2751              
2752 0 0       0 if ($rows == 1) {
    0          
2753 0         0 ($max, $max_p) = _max_column($matrix->row(1)->_transpose, $columns);
2754             } elsif ($columns == 1) {
2755 0         0 ($max, $max_p) = _max_column($matrix->column(1), $rows);
2756             } else {
2757 0         0 for my $c (1..$columns) {
2758 0         0 my ($m, $mp) = _max_column($matrix->column($c), $rows);
2759 0         0 push @$max, $m;
2760 0         0 push @$max_p, $mp;
2761             }
2762             }
2763 0 0       0 return wantarray ? ($max, $max_p) : $max
2764             }
2765              
2766             sub _max_column {
2767             # passing $rows allows for some extra (minimal) efficiency
2768 0     0   0 my ($column, $rows) = @_;
2769              
2770 0         0 my ($m, $mp) = ($column->element(1, 1), 1);
2771 0         0 for my $l (1..$rows) {
2772 0 0       0 if ($column->element($l, 1) > $m) {
2773 0         0 $m = $column->element($l, 1);
2774 0         0 $mp = $l;
2775             }
2776             }
2777 0         0 return ($m, $mp);
2778             }
2779              
2780             sub minimum {
2781 0     0 1 0 my ($matrix) = @_;
2782 0         0 my ($rows, $columns) = $matrix->dim;
2783              
2784 0         0 my $min = [];
2785 0         0 my $min_p = [];
2786              
2787 0 0       0 if ($rows == 1) {
    0          
2788 0         0 ($min, $min_p) = _min_column($matrix->row(1)->_transpose, $columns);
2789             } elsif ($columns == 1) {
2790 0         0 ($min, $min_p) = _min_column($matrix->column(1), $rows);
2791             } else {
2792 0         0 for my $c (1..$columns) {
2793 0         0 my ($m, $mp) = _min_column($matrix->column($c), $rows);
2794 0         0 push @$min, $m;
2795 0         0 push @$min_p, $mp;
2796             }
2797             }
2798 0 0       0 return wantarray ? ($min, $min_p) : $min
2799             }
2800              
2801             sub _min_column {
2802             # passing $rows allows for some extra (minimal) efficiency
2803 0     0   0 my ($column, $rows) = @_;
2804              
2805 0         0 my ($m, $mp) = ($column->element(1, 1), 1);
2806 0         0 for my $l (1..$rows) {
2807 0 0       0 if ($column->element($l, 1) < $m) {
2808 0         0 $m = $column->element($l, 1);
2809 0         0 $mp = $l;
2810             }
2811             }
2812 0         0 return ($m, $mp);
2813             }
2814              
2815              
2816              
2817             ########################################
2818             # #
2819             # define overloaded operators section: #
2820             # #
2821             ########################################
2822             sub _concat
2823             {
2824 5     5   968 my($object,$argument,$flag) = @_;
2825 5         12 my($orows,$ocols) = ($object->[1],$object->[2]);
2826 5         12 my($name) = "concat";
2827              
2828              
2829 5 100 66     64 if ((defined $argument) && ref($argument) && (ref($argument) !~ /^SCALAR$|^ARRAY$|^HASH$|^CODE$|^REF$/)) {
    50 66        
2830 4         8 my($arows,$acols) = ($argument->[1],$argument->[2]);
2831 4 100       204 croak "Math::MatrixReal: Matrices must have same number of rows in concatenation" unless ($orows == $arows);
2832 3         9 my $result = $object->new($orows,$ocols+$acols);
2833 3         8 for ( my $i = 0; $i < $arows; $i++ ) {
2834 9         19 for ( my $j = 0; $j < $ocols + $acols; $j++ ) {
2835 51 100       160 $result->[0][$i][$j] = ( $j < $ocols ) ? $object->[0][$i][$j] : $argument->[0][$i][$j - $ocols] ;
2836             }
2837             }
2838 3         12 return $result;
2839             } elsif (defined $argument) {
2840 1         3 return "$object" . $argument;
2841              
2842             } else {
2843 0         0 croak "Math::MatrixReal $name: wrong argument type";
2844             }
2845             }
2846             sub _negate
2847             {
2848 1     1   2 my($object) = @_;
2849              
2850 1         4 my $temp = $object->new($object->[1],$object->[2]);
2851 1         5 $temp->negate($object);
2852 1         6 return($temp);
2853             }
2854              
2855             sub _transpose
2856             {
2857 50     50   919 my ($object) = @_;
2858 50         198 my $temp = $object->new($object->[2],$object->[1]);
2859 50         224 $temp->transpose($object);
2860 50         240 return $temp;
2861             }
2862              
2863             sub _boolean
2864             {
2865 214     214   379 my($object) = @_;
2866 214         421 my($rows,$cols) = ($object->[1],$object->[2]);
2867              
2868 214         383 my $result = 0;
2869              
2870             BOOL:
2871 214         727 for ( my $i = 0; $i < $rows; $i++ )
2872             {
2873 286         904 for ( my $j = 0; $j < $cols; $j++ )
2874             {
2875 427 100       2325 if ($object->[0][$i][$j] != 0)
2876             {
2877 165         223 $result = 1;
2878 165         508 last BOOL;
2879             }
2880             }
2881             }
2882 214         919 return($result);
2883             }
2884             #TODO: ugly copy+paste
2885             sub _not_boolean
2886             {
2887 1     1   3 my ($object) = @_;
2888 1         4 my ($rows,$cols) = ($object->[1],$object->[2]);
2889              
2890 1         3 my $result = 1;
2891             NOTBOOL:
2892 1         6 for ( my $i = 0; $i < $rows; $i++ )
2893             {
2894 5         13 for ( my $j = 0; $j < $cols; $j++ )
2895             {
2896 25 50       84 if ($object->[0][$i][$j] != 0)
2897             {
2898 0         0 $result = 0;
2899 0         0 last NOTBOOL;
2900             }
2901             }
2902             }
2903 1         7 return($result);
2904             }
2905              
2906             sub _stringify
2907             {
2908 7     7   34 my ($self) = @_;
2909 7         17 my ($rows,$cols) = ($self->[1],$self->[2]);
2910              
2911 7         16 my $precision = $self->[4];
2912              
2913 7 100       26 my $format = !defined $precision ? '% #-19.12E ' : '% #-19.'.$precision.'f ';
2914 7 100 100     35 $format = '% #-12d' if defined $precision && $precision == 0;
2915              
2916 7         46 my $s = '';
2917 7         25 for ( my $i = 0; $i < $rows; $i++ )
2918             {
2919 26         37 $s .= "[ ";
2920 26         63 for ( my $j = 0; $j < $cols; $j++ )
2921             {
2922 98         527 $s .= sprintf $format , $self->[0][$i][$j];
2923             }
2924 26         100 $s .= "]\n";
2925             }
2926 7         55 return $s;
2927             }
2928              
2929             sub _norm
2930             {
2931 159     159   280 my ($self) = @_;
2932              
2933 159         509 return $self->norm_one() ;
2934             }
2935              
2936             sub _add
2937             {
2938 25     25   64 my($object,$argument,$flag) = @_;
2939 25         70 my($name) = "'+'";
2940              
2941 25 50 33     437 if ((defined $argument) && ref($argument) &&
      33        
2942             (ref($argument) !~ /^SCALAR$|^ARRAY$|^HASH$|^CODE$|^REF$/))
2943             {
2944 25 100       92 if (defined $flag)
2945             {
2946 23         87 my $temp = $object->new($object->[1],$object->[2]);
2947 23         105 $temp->add($object,$argument);
2948 23         176 return($temp);
2949             }
2950             else
2951             {
2952 2         12 $object->add($object,$argument);
2953 2         9 return($object);
2954             }
2955             }
2956             else
2957             {
2958 0         0 croak "Math::MatrixReal $name: wrong argument type";
2959             }
2960             }
2961              
2962             sub _subtract
2963             {
2964 164     164   1980 my($object,$argument,$flag) = @_;
2965 164         276 my($name) = "'-'";
2966              
2967 164 50 33     6079 if ((defined $argument) && ref($argument) &&
      33        
2968             (ref($argument) !~ /^SCALAR$|^ARRAY$|^HASH$|^CODE$|^REF$/))
2969             {
2970 164 100       350 if (defined $flag)
2971             {
2972 163         549 my $temp = $object->new($object->[1],$object->[2]);
2973 163 50       402 if ($flag) { $temp->subtract($argument,$object); }
  0         0  
2974 163         532 else { $temp->subtract($object,$argument); }
2975 163         684 return $temp;
2976             }
2977             else
2978             {
2979 1         3 $object->subtract($object,$argument);
2980 1         4 return($object);
2981             }
2982             }
2983             else
2984             {
2985 0         0 croak "Math::MatrixReal $name: wrong argument type";
2986             }
2987             }
2988              
2989             sub _exponent
2990             {
2991 20     20   65 my($matrix, $exp) = @_;
2992 20         42 my($rows,$cols) = ($matrix->[1],$matrix->[2]);
2993              
2994 20         70 return $matrix->exponent( $exp );
2995             }
2996             sub _divide
2997             {
2998 6     6   22 my($matrix,$argument,$flag) = @_;
2999             # TODO: check dimensions of everything!
3000 6         10 my($mrows,$mcols) = ($matrix->[1],$matrix->[2]);
3001 6         7 my($arows,$acols)=(0,0);
3002 6         9 my($name) = "'/'";
3003 6         15 my $temp = $matrix->clone();
3004 6         7 my $arg;
3005 6         6 my ($inv,$m1);
3006              
3007 6 100       15 if( ref($argument) =~ /Math::MatrixReal/ ){
3008 2         6 $arg = $argument->clone();
3009 2         4 ($arows,$acols)=($arg->[1],$arg->[2]);
3010             }
3011             #print "DEBUG: flag= $flag\n";
3012             #print "DEBUG: arg=$arg\n";
3013 6 100       12 if( $flag == 1) {
3014             #print "DEBUG: ref(arg)= " . ref($arg) . "\n";
3015 2 50       5 if( ref($argument) =~ /Math::MatrixReal/ ){
3016             #print "DEBUG: arg is a matrix \n";
3017             # Matrix Division = A/B = A*B^(-1)
3018 0 0       0 croak "Math::MatrixReal $name: this operation is defined only for square matrices" unless ($arows == $acols);
3019 0         0 return $temp->multiply( $arg->inverse() );
3020              
3021             } else {
3022             #print "DEBUG: Arg is scalar\n";
3023             #print "DEBUG:arows,acols=$arows,$acols\n";
3024             #print "DEBGU:mrows,mcols=$mrows,$mcols\n";
3025 2 100       199 croak "Math::MatrixReal $name: this operation is defined only for square matrices" unless ($mrows == $mcols);
3026 1         3 $temp->multiply_scalar( $temp , $argument);
3027 1         4 return $temp;
3028             }
3029             } else {
3030             #print "DEBUG: temp=\n";
3031             #print $temp . "\n";
3032             #print "DEBUG: ref(arg)= " . ref($arg) . "\n";
3033             #print "DEBUG: arg=\n";
3034             #print $arg ."\n";
3035 4 100       9 if( ref($arg) =~ /Math::MatrixReal/ ){
3036             #print "DEBUG: matrix division\n";
3037 2 50       6 if( $arg->is_col_vector() ){
3038 0         0 print "DEBUG: $arg is a col vector\n";
3039             }
3040 2 50       4 croak "Math::MatrixReal $name: this operation is defined only for square matrices" unless ($arows == $acols);
3041 2         5 $inv = $arg->inverse();
3042 2         11 return $temp->multiply($inv);
3043             } else {
3044 2         95 $temp->multiply_scalar($temp,1/$argument);
3045 2         8 return $temp;
3046             }
3047             }
3048            
3049             }
3050              
3051             sub _multiply
3052             {
3053 53     53   186 my($object,$argument,$flag) = @_;
3054 53         95 my($name) = "'*'";
3055 53         74 my($temp);
3056              
3057 53 100 66     772 if ((defined $argument) && ref($argument) &&
    50 66        
      33        
3058             (ref($argument) !~ /^SCALAR$|^ARRAY$|^HASH$|^CODE$|^REF$/))
3059             {
3060 32 50 33     505 if ((defined $flag) && $flag)
3061             {
3062 0         0 return( multiply($argument,$object) );
3063             }
3064             else
3065             {
3066 32         123 return( multiply($object,$argument) );
3067             }
3068             }
3069             elsif ((defined $argument) && !(ref($argument)))
3070             {
3071 21 100       61 if (defined $flag)
3072             {
3073 20         129 $temp = $object->new($object->[1],$object->[2]);
3074 20         77 $temp->multiply_scalar($object,$argument);
3075 20         344 return($temp);
3076             }
3077             else
3078             {
3079 1         5 $object->multiply_scalar($object,$argument);
3080 1         4 return($object);
3081             }
3082             }
3083             else
3084             {
3085 0         0 croak "Math::MatrixReal $name: wrong argument type";
3086             }
3087             }
3088              
3089             sub _assign_add
3090             {
3091 2     2   4 my($object,$argument) = @_;
3092              
3093 2         8 return( &_add($object,$argument,undef) );
3094             }
3095              
3096             sub _assign_subtract
3097             {
3098 1     1   1 my($object,$argument) = @_;
3099              
3100 1         3 return( &_subtract($object,$argument,undef) );
3101             }
3102              
3103             sub _assign_multiply
3104             {
3105 1     1   2 my($object,$argument) = @_;
3106              
3107 1         5 return( &_multiply($object,$argument,undef) );
3108             }
3109              
3110             sub _assign_exponent {
3111 1     1   2 my($object,$arg) = @_;
3112 1         3 return ( &_exponent($object,$arg,undef) );
3113             }
3114              
3115             sub _equal
3116             {
3117 10     10   309 my($object,$argument,$flag) = @_;
3118 10         15 my($name) = "'=='";
3119 10         20 my($rows,$cols) = ($object->[1],$object->[2]);
3120 10         75 my($i,$j,$result);
3121              
3122 10 100 66     131 if ((defined $argument) && ref($argument) &&
      66        
3123             (ref($argument) !~ /^SCALAR$|^ARRAY$|^HASH$|^CODE$|^REF$/))
3124             {
3125 9         12 $result = 1;
3126             EQUAL:
3127 9         28 for ( $i = 0; $i < $rows; $i++ )
3128             {
3129 65         123 for ( $j = 0; $j < $cols; $j++ )
3130             {
3131 485 50       1486 if ($object->[0][$i][$j] != $argument->[0][$i][$j])
3132             {
3133 0         0 $result = 0;
3134 0         0 last EQUAL;
3135             }
3136             }
3137             }
3138 9         62 return($result);
3139             }
3140             else
3141             {
3142 1         160 croak "Math::MatrixReal $name: wrong argument type";
3143             }
3144             }
3145              
3146             sub _not_equal
3147             {
3148 4     4   8 my($object,$argument,$flag) = @_;
3149 4         6 my($name) = "'!='";
3150 4         7 my($rows,$cols) = ($object->[1],$object->[2]);
3151              
3152 4 100 66     53 if ((defined $argument) && ref($argument) &&
      66        
3153             (ref($argument) !~ /^SCALAR$|^ARRAY$|^HASH$|^CODE$|^REF$/))
3154             {
3155 3         11 my ($r,$c) = $argument->dim;
3156 3 100 66     27 return 1 unless ($r == $rows && $c == $cols );
3157 2         3 my $result = 0;
3158             NOTEQUAL:
3159 2         6 for ( my $i = 0; $i < $rows; $i++ )
3160             {
3161 2         6 for ( my $j = 0; $j < $cols; $j++ )
3162             {
3163 2 50       6 if ($object->[0][$i][$j] != $argument->[0][$i][$j])
3164             {
3165 2         2 $result = 1;
3166 2         4 last NOTEQUAL;
3167             }
3168             }
3169             }
3170 2         18 return $result;
3171             } else {
3172 1         170 croak "Math::MatrixReal $name: wrong argument type";
3173             }
3174             }
3175              
3176             sub _less_than
3177             {
3178 5     5   12 my($object,$argument,$flag) = @_;
3179 5         21 my($name) = "'<'";
3180              
3181 5 100 66     70 if ((defined $argument) && ref($argument) &&
    50 66        
      33        
3182             (ref($argument) !~ /^SCALAR$|^ARRAY$|^HASH$|^CODE$|^REF$/))
3183             {
3184 3 50 33     22 if ((defined $flag) && $flag)
3185             {
3186 0         0 return( $argument->norm_one() < $object->norm_one() );
3187             } else {
3188 3         8 return( $object->norm_one() < $argument->norm_one() );
3189             }
3190             }
3191             elsif ((defined $argument) && !(ref($argument)))
3192             {
3193 2 50 33     26 if ((defined $flag) && $flag)
3194             {
3195 0         0 return( abs($argument) < $object->norm_one() );
3196             } else {
3197 2         10 return( $object->norm_one() < abs($argument) );
3198             }
3199             } else {
3200 0         0 croak "Math::MatrixReal $name: wrong argument type";
3201             }
3202             }
3203              
3204             sub _less_than_or_equal
3205             {
3206 2     2   10 my($object,$argument,$flag) = @_;
3207 2         5 my($name) = "'<='";
3208              
3209 2 50 33     34 if ((defined $argument) && ref($argument) &&
    0 33        
      0        
3210             (ref($argument) !~ /^SCALAR$|^ARRAY$|^HASH$|^CODE$|^REF$/))
3211             {
3212 2 50 33     11 if ((defined $flag) && $flag)
3213             {
3214 0         0 return( $argument->norm_one() <= $object->norm_one() );
3215             } else {
3216 2         8 return( $object->norm_one() <= $argument->norm_one() );
3217             }
3218             } elsif ((defined $argument) && !(ref($argument))) {
3219 0 0 0     0 if ((defined $flag) && $flag)
3220             {
3221 0         0 return( abs($argument) <= $object->norm_one() );
3222             } else {
3223 0         0 return( $object->norm_one() <= abs($argument) );
3224             }
3225             } else {
3226 0         0 croak "Math::MatrixReal $name: wrong argument type";
3227             }
3228             }
3229              
3230             sub _greater_than
3231             {
3232 3     3   7 my($object,$argument,$flag) = @_;
3233 3         5 my($name) = "'>'";
3234              
3235 3 50 33     41 if ((defined $argument) && ref($argument) &&
    0 33        
      0        
3236             (ref($argument) !~ /^SCALAR$|^ARRAY$|^HASH$|^CODE$|^REF$/))
3237             {
3238 3 50 33     15 if ((defined $flag) && $flag)
3239             {
3240 0         0 return( $argument->norm_one() > $object->norm_one() );
3241             } else {
3242 3         8 return( $object->norm_one() > $argument->norm_one() );
3243             }
3244             } elsif ((defined $argument) && !(ref($argument))) {
3245 0 0 0     0 if ((defined $flag) && $flag)
3246             {
3247 0         0 return( abs($argument) > $object->norm_one() );
3248             } else {
3249 0         0 return( $object->norm_one() > abs($argument) );
3250             }
3251             } else {
3252 0         0 croak "Math::MatrixReal $name: wrong argument type";
3253             }
3254             }
3255              
3256             sub _greater_than_or_equal
3257             {
3258 2     2   6 my($object,$argument,$flag) = @_;
3259 2         5 my($name) = "'>='";
3260              
3261 2 50 33     32 if ((defined $argument) && ref($argument) &&
    0 33        
      0        
3262             (ref($argument) !~ /^SCALAR$|^ARRAY$|^HASH$|^CODE$|^REF$/))
3263             {
3264 2 50 33     30 if ((defined $flag) && $flag)
3265             {
3266 0         0 return( $argument->norm_one() >= $object->norm_one() );
3267             } else {
3268 2         6 return( $object->norm_one() >= $argument->norm_one() );
3269             }
3270             } elsif ((defined $argument) && !(ref($argument))) {
3271 0 0 0     0 if ((defined $flag) && $flag)
3272             {
3273 0         0 return( abs($argument) >= $object->norm_one() );
3274             } else {
3275 0         0 return( $object->norm_one() >= abs($argument) );
3276             }
3277             } else {
3278 0         0 croak "Math::MatrixReal $name: wrong argument type";
3279             }
3280             }
3281              
3282             sub _clone
3283             {
3284 4     4   8 my($object) = @_;
3285              
3286 4         16 my $temp = $object->new($object->[1],$object->[2]);
3287 4         13 $temp->copy($object);
3288 4         11 $temp->_undo_LR();
3289 4         12 return $temp;
3290             }
3291 57     57   2003 { no warnings; 42 }
  57         136  
  57         7937  
3292             __END__