File Coverage

blib/lib/Math/MatrixReal.pm
Criterion Covered Total %
statement 1370 1648 83.1
branch 453 726 62.4
condition 157 341 46.0
subroutine 156 167 93.4
pod 27 100 27.0
total 2163 2982 72.5


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