File Coverage

blib/lib/Math/MatrixReal.pm
Criterion Covered Total %
statement 1370 1648 83.1
branch 453 726 62.4
condition 158 341 46.3
subroutine 156 167 93.4
pod 28 100 28.0
total 2165 2982 72.6


line stmt bran cond sub pod time code
1             # Copyright (c) 1996, 1997 by Steffen Beyer. All rights reserved.
2             # Copyright (c) 1999 by Rodolphe Ortalo. All rights reserved.
3             # Copyright (c) 2001-2015 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   1321345 use strict;
  59         130  
  59         2215  
10 59     59   450 use warnings;
  59         135  
  59         1926  
11 59     59   8505 use Carp;
  59         106  
  59         5400  
12 59     59   42597 use Data::Dumper;
  59         518633  
  59         4899  
13 59     59   485 use Scalar::Util qw/reftype/;
  59         94  
  59         5652  
14 59     59   301 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION);
  59         90  
  59         11845  
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.12';
22              
23             use overload
24 59         482 '.' => '_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   331 'fallback' => undef;
  59         93  
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 1821 50   1821 1 3896 croak "Usage: \$new_matrix = Math::MatrixReal->new(\$rows,\$columns);" if (@_ != 3);
95              
96 1821         2456 my ($self,$rows,$cols) = @_;
97 1821   50     5919 my $class = ref($self) || $self || 'Math::MatrixReal';
98              
99 1821 50 33     7681 croak "Math::MatrixReal::new(): number of rows must be integer > 0"
100             unless ($rows > 0 and $rows == int($rows) );
101              
102 1821 50 33     7033 croak "Math::MatrixReal::new(): number of columns must be integer > 0"
103             unless ($cols > 0 and $cols == int($cols) );
104              
105 1821         3979 my $this = [ [ ], $rows, $cols ];
106              
107             # Create the first empty row and pre-lengthen
108 1821         2253 my $empty = [ ];
109 1821         5706 $#$empty = $cols - 1;
110              
111 1821         4097 map { $empty->[$_] = 0.0 } ( 0 .. $cols-1 );
  11195         14037  
112              
113             # Create a row at a time
114 1821         3439 map { $this->[0][$_] = [ @$empty ] } ( 0 .. $rows-1);
  14617         38677  
115              
116 1821         7926 bless $this, $class;
117             }
118              
119             sub new_diag {
120 28 50   28 1 219 croak "Usage: \$new_matrix = Math::MatrixReal->new_diag( [ 1, 2, 3] );" unless (@_ == 2 );
121 28         48 my ($self,$diag) = @_;
122 28         46 my $n = scalar @$diag;
123              
124 28 50       86 croak "Math::MatrixReal::new_diag(): Third argument must be an arrayref" unless (ref($diag) eq "ARRAY");
125              
126 28         86 my $matrix = Math::MatrixReal->new($n,$n);
127              
128 28         70 map { $matrix->[0][$_][$_] = shift @$diag } ( 0 .. $n-1);
  108         784  
129 28         91 return $matrix;
130             }
131             sub new_tridiag {
132 2 50   2 1 327 croak "Usage: \$new_matrix = Math::MatrixReal->new_tridiag( [ 1, 2, 3], [ 4, 5, 6, 7], [-1,-2,-3] );" unless (@_ == 4 );
133 2         5 my ($self,$lower,$diag,$upper) = @_;
134 2         2 my $matrix;
135 2         6 my ($l,$n,$m) = (scalar(@$lower),scalar(@$diag),scalar(@$upper));
136 2         4 my ($k,$p)=(-1,-1);
137              
138 2 50 33     22 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     14 croak "Math::MatrixReal::new_tridiag(): new_tridiag(\$lower,\$diag,\$upper) diagonal dimensions incompatible" unless
141             ($l == $m && $n == ($l+1));
142              
143 2         19 $matrix = Math::MatrixReal->new_diag($diag);
144             $matrix = $matrix->each(
145             sub {
146 32     32   47 my ($e,$i,$j) = @_;
147 32 100       130 if (($i-$j) == -1) { $k++; return $upper->[$k];}
  6 100       5  
  6 100       20  
148 8         28 elsif ( $i == $j) { return $e; }
149 6         6 elsif (($i-$j) == 1) { $p++; return $lower->[$p];}
  6         20  
150             }
151 2         17 );
152 2         35 return $matrix;
153             }
154              
155             sub new_random {
156 38 100   38 1 8336 croak "Usage: \$new_matrix = Math::MatrixReal->new_random(\$n,\$m, { symmetric => 1, bounded_by => [-5,5], integer => 1 } );"
157             if (@_ < 2);
158 37         80 my ($self, $rows, $cols, $options ) = @_;
159 37 100 33     144 (($options = $cols) and ($cols = $rows)) if ref $cols eq 'HASH';
160 37 100       144 my ($min,$max) = defined $options->{bounded_by} ? @{ $options->{bounded_by} } : ( 0, 10);
  10         21  
161 37         63 my $integer = $options->{integer};
162 37   50     236 $self = ref($self) || $self || 'Math::MatrixReal';
163            
164 37   66     116 $cols ||= $rows;
165 37 100 100     311 croak "Math::MatrixReal::new_random(): number of rows must = number of cols for symmetric option"
166             if ($rows != $cols and $options->{symmetric} );
167              
168 36 50 66     524 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     644 croak "Math::MatrixReal::new_random(): bounded_by interval length must be > 0"
      100        
172             unless (defined $min && defined $max && $min < $max );
173              
174 33 100 66     317 croak "Math::MatrixReal::new_random(): tridiag option only for square matrices"
      100        
175             if (($options->{tridiag} || $options->{tridiagonal}) && $rows != $cols);
176              
177 32 100 66     349 croak "Math::MatrixReal::new_random(): diagonal option only for square matrices "
      100        
178             if (($options->{diag} || $options->{diagonal}) && ($rows != $cols));
179              
180 31         114 my $matrix = Math::MatrixReal->new($rows,$cols);
181 31 100   10962   189 my $random_code = sub { $integer ? int($min + rand($max-$min)) : $min + rand($max-$min) } ;
  10962         28281  
182              
183 31 100 66     257 $matrix = $options->{diag} || $options->{diagonal} ? $matrix->each_diag($random_code) : $matrix->each($random_code);
184 31 100 66 50   614 $matrix = $matrix->each( sub {my($e,$i,$j)=@_; ( abs($i-$j)>1 ) ? 0 : $e } ) if ($options->{tridiag} || $options->{tridiagonal} );
  50 100       44  
  50         121  
185 31 100       248 $options->{symmetric} ? 0.5*($matrix + ~$matrix) : $matrix;
186             }
187            
188             sub new_from_string#{{{
189             {#{{{
190 179 50   179 1 3589 croak "Usage: \$new_matrix = Math::MatrixReal->new_from_string(\$string);"
191             if (@_ != 2);
192              
193 179         293 my ($self,$string) = @_;
194 179   50     947 my $class = ref($self) || $self || 'Math::MatrixReal';
195 179         198 my ($line,$values);
196 0         0 my ($rows,$cols);
197 0         0 my ($row,$col);
198 0         0 my ($warn,$this);
199              
200 179         268 $warn = $rows = $cols = 0;
201              
202 179         276 $values = [ ];
203 179         2100 while ($string =~ m!^\s* \[ \s+ ( (?: [+-]? \d+ (?: \. \d*)? (?: E [+-]? \d+ )? \s+ )+ ) \] \s*? \n !ix) {
204 521         848 $line = $1; $string = $';
  521         688  
205 521         772 $values->[$rows] = [ ]; @{$values->[$rows]} = split(' ', $line);
  521         1023  
  521         1021  
206 521         511 $col = @{$values->[$rows]};
  521         595  
207 521 100       881 if ($col != $cols) {
208 178 50       384 unless ($cols == 0) { $warn = 1; }
  0         0  
209 178 50       356 if ($col > $cols) { $cols = $col; }
  178         726  
210             }
211 521         2317 $rows++;
212             }
213 179 50       615 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 179 100       390 if ($rows == 0) { croak "Math::MatrixReal::new_from_string(): empty input string"; }
  1         129  
219 178 50       336 if ($warn) { warn "Math::MatrixReal::new_from_string(): missing elements will be set to zero!\n"; }
  0         0  
220 178         428 $this = Math::MatrixReal::new($class,$rows,$cols);
221 178         478 for ( $row = 0; $row < $rows; $row++ ) {
222 521         528 for ( $col = 0; $col < @{$values->[$row]}; $col++ ) {
  2349         4238  
223 1828         6337 $this->[0][$row][$col] = $values->[$row][$col];
224             }
225             }
226 178         638 return $this;
227             }#}}}#}}}
228              
229             # from Math::MatrixReal::Ext1 (msouth@fulcrum.org)
230             sub new_from_cols {
231 25     25 1 1670 my $self = shift;
232 25 50 33     121 my $extra_args = ( @_ > 1 && ref($_[-1]) eq 'HASH' ) ? pop : {};
233 25         61 $extra_args->{_type} = 'column';
234 25         73 $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 128 my $self = shift;
239 4         12 $self->new_from_cols(@_);
240             }
241             # from Math::MatrixReal::Ext1 (msouth@fulcrum.org)
242             sub new_from_rows {
243 29     29 1 879 my $self = shift;
244 29 50 33     210 my $extra_args = ( @_ > 1 && ref($_[-1]) eq 'HASH' ) ? pop : {};
245 29         232 $extra_args->{_type} = 'row';
246              
247 29         96 $self->_new_from_rows_or_cols(@_, $extra_args );
248             }
249              
250             sub reshape {
251 1     1 1 314 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         6 push @cols, [@{$values}[$p .. $p + $rows - 1]];
  3         4  
257 3         5 $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 54     54   72 my $proto = shift;
266 54   66     236 my $class = ref($proto) || $proto;
267 54         70 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 54         61 my $args = pop;
272 54         80 my $vector_type = $args->{_type};
273 54 50       330 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 54         265 my $caller_subname = (caller(1))[3];
277              
278 54 100       535 croak "$caller_subname: need a reference to an array of ${vector_type}s" unless reftype($ref_to_vectors) eq 'ARRAY';
279              
280 52         63 my @vectors = @{$ref_to_vectors};
  52         117  
281 52         56 my $matrix;
282 52         169 my $other_type = {row=>'column', column=>'row'}->{$vector_type};
283 52         153 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 52         212 my $current_vector_count = 1;
290 52         122 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 101         157 my $ref = ref( $current_vector ) ;
296              
297 101 100 33     336 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         24 $current_vector = $class->new_from_string( $current_vector );
302             } elsif ( $ref eq 'ARRAY' ) {
303 74         329 my @array = @$current_vector;
304 74 100       254 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 73 100       136 if ($vector_type eq 'row') {
308 41         242 $current_vector = $class->new_from_string( '[ '. join( " ", @array) ." ]\n" );
309             } else {
310 32         260 $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         198 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 98         246 my @vector_dims = $current_vector->dim;
326              
327             #die unless the appropriate dimension is 1
328 98 100       502 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 96 100       188 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 96   66     420 $matrix_dim{$other_type} ||= $length;
336              
337             # die unless length of this vector matches the first length
338 96 100       460 croak "$caller_subname: one $vector_type has [$length] elements and another one had [$matrix_dim{$other_type}]--all of the ${vector_type}s passed in must have the same dimension"
339             unless ($length == $matrix_dim{$other_type}) ;
340              
341             # create the matrix the first time through
342 94   66     1200 $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 94         185 foreach my $element_index ( 1..$length ){
347             # args for vector assignment:
348             # initialize both to one and reset the correct
349             # one below
350 247         246 my ($v_r, $v_c) = (1,1);
351              
352             # args for matrix assignment
353 247         202 my ($row_index, $column_index, $value);
354              
355 247 100       357 if ($vector_type eq 'row') {
356 134         116 $row_index = $current_vector_count;
357 134         117 $v_c = $column_index = $element_index;
358             } else {
359 113         100 $v_r = $row_index = $element_index;
360 113         157 $column_index = $current_vector_count;
361             }
362 247         391 $value = $current_vector->element($v_r, $v_c);
363 247         418 $matrix->assign($row_index, $column_index, $value);
364             }
365 94         194 $current_vector_count ++ ;
366             }
367 45         361 return $matrix;
368             }
369              
370             sub shadow
371             {
372 52 50   52 1 168 croak "Usage: \$new_matrix = \$some_matrix->shadow();" if (@_ != 1);
373              
374 52         71 my ($matrix) = @_;
375              
376 52         213 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 25 my ($self,$n) = @_;
396 5 50       11 if (defined $n) {
397 5 100       190 croak "Usage: \$matrix->display_precision(\$nonnegative_integer);" if ($n < 0);
398 4         13 $self->[4] = int $n;
399             } else {
400 0         0 $self->[4] = undef;
401             }
402             }
403              
404             sub copy
405             {
406 471 50   471 1 1203 croak "Usage: \$matrix1->copy(\$matrix2);"
407             if (@_ != 2);
408              
409 471         694 my ($matrix1,$matrix2) = @_;
410 471         835 my ($rows1,$cols1) = ($matrix1->[1],$matrix1->[2]);
411 471         869 my ($rows2,$cols2) = ($matrix2->[1],$matrix2->[2]);
412 471         526 my ($i,$j);
413              
414 471 50 33     2143 croak "Math::MatrixReal::copy(): matrix size mismatch" unless $rows1 == $rows2 && $cols1 == $cols2;
415              
416 471         1237 for ( $i = 0; $i < $rows1; $i++ )
417             {
418 4616         4234 my $r1 = [];
419 4616         4892 my $r2 = $matrix2->[0][$i];
420 4616         12257 @$r1 = @$r2; # Copy whole array directly
421 4616         11176 $matrix1->[0][$i] = $r1;
422             }
423 471 100       1827 if (defined $matrix2->[3]) # is an LR decomposition matrix!
424             {
425 5         41 $matrix1->[3] = $matrix2->[3]; # $sign
426 5         11 $matrix1->[4] = $matrix2->[4]; # $perm_row
427 5         14 $matrix1->[5] = $matrix2->[5]; # $perm_col
428             }
429             }
430              
431             sub clone
432             {
433 290 50   290 1 858 croak "Usage: \$twin_matrix = \$some_matrix->clone();" if (@_ != 1);
434              
435 290         424 my($matrix) = @_;
436 290         343 my($temp);
437              
438 290         868 $temp = $matrix->new($matrix->[1],$matrix->[2]);
439 290         871 $temp->copy($matrix);
440 290         503 return $temp;
441             }
442              
443             ## trace() : return the sum of the diagonal elements
444             sub trace {
445 13 50   13 0 44 croak "Usage: \$trace = \$matrix->trace();" if (@_ != 1);
446              
447 13         16 my $matrix = shift;
448 13         21 my($rows,$cols) = ($matrix->[1],$matrix->[2]);
449 13         12 my $trace = 0;
450              
451 13 50       25 croak "Math::MatrixReal::trace(): matrix is not quadratic" unless ($rows == $cols);
452              
453 13         35 map { $trace += $matrix->[0][$_][$_] } (0 .. $cols-1);
  188         249  
454              
455 13         71 return $trace;
456             }
457             sub submatrix {
458 4     4 0 17 my $self = shift;
459 4         6 my ($x1, $y1, $x2, $y2) = @_;
460 4 100 66     294 croak "Math::MatrixReal::submatrix(): indices must be positive integers"
      66        
      66        
461             unless ($x1 >= 1 && $x2 >= 1 && $y1 >=1 && $y2 >=1 );
462 2         13 my($rows,$cols) = ($self->[1],$self->[2]);
463 2         9 my($sr,$sc) = ( 1+abs($x1-$x2), 1+abs($y1-$y2) );
464 2         6 my $submatrix = $self->new( $sr, $sc );
465              
466 2         5 for (my $i = $x1-1; $i < $x2; $i++ ) {
467 3         6 for (my $j = $y1-1; $j < $y2; $j++ ) {
468 5         16 $submatrix->[0][$i-($x1-1)][$j-($y1-1)] = $self->[0][$i][$j];
469             }
470             }
471 2         8 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 271 croak "Usage: \$minor = \$matrix->minor(\$r,\$c);" unless (@_ == 3);
477 137         147 my ($matrix,$r,$c) = @_;
478 137         244 my ($rows,$cols) = $matrix->dim();
479              
480 137 50 33     540 croak "Math::MatrixReal::minor(): \$matrix must be at least 2x2"
481             unless ($rows > 1 and $cols > 1);
482 137 50 33     450 croak "Math::MatrixReal::minor(): $r and $c must be positive"
483             unless ($r > 0 and $c > 0 );
484 137 50 33     466 croak "Math::MatrixReal::minor(): matrix has no $r,$c element"
485             unless ($r <= $rows and $c <= $cols );
486              
487 137         362 my $minor = new Math::MatrixReal($rows-1,$cols-1);
488 137         182 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         278 for(; $i < $rows; $i++){
497 677         994 for(;$j < $rows; $j++ ){
498 3577 100 100     17901 if( $i >= $r && $j >= $c ){
    100 66        
    100 66        
    50 33        
499 595         1419 $minor->[0][$i-1][$j-1] = $matrix->[0][$i][$j];
500             } elsif ( $i >= $r && $j < $c ){
501 864         1976 $minor->[0][$i-1][$j] = $matrix->[0][$i][$j];
502             } elsif ( $i < $r && $j < $c ){
503 1260         3087 $minor->[0][$i][$j] = $matrix->[0][$i][$j];
504             } elsif ( $i < $r && $j >= $c ){
505 858         2023 $minor->[0][$i][$j-1] = $matrix->[0][$i][$j];
506             } else {
507 0         0 croak "Very bad things";
508             }
509             }
510 677         1204 $j = 0;
511             }
512 137         502 return ($minor);
513             }
514             sub swap_col {
515 1 50   1 1 9 croak "Usage: \$matrix->swap_col(\$col1,\$col2); " unless (@_ == 3);
516 1         1 my ($matrix,$col1,$col2) = @_;
517 1         6 my ($rows,$cols) = $matrix->dim();
518 1         3 my (@temp);
519              
520 1 50 33     12 croak "Math::MatrixReal::swap_col(): col index is not valid"
      33        
      33        
521             unless ( $col1 <= $cols && $col2 <= $cols &&
522             $col1 == int($col1) &&
523             $col2 == int($col2) );
524 1         2 $col1--;$col2--;
  1         1  
525 1         3 for(my $i=0;$i < $rows;$i++){
526 4         12 $temp[$i] = $matrix->[0][$i][$col1];
527 4         7 $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 7 croak "Usage: \$matrix->swap_row(\$row1,\$row2); " unless (@_ == 3);
533 1         2 my ($matrix,$row1,$row2) = @_;
534 1         4 my ($rows,$cols) = $matrix->dim();
535 1         2 my (@temp);
536              
537 1 50 33     12 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         1  
542 1         4 for(my $j=0;$j < $cols;$j++){
543 4         5 $temp[$j] = $matrix->[0][$row1][$j];
544 4         10 $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 155 croak "Usage: \$matrix->assign_row(\$row,\$row_vec);" unless (@_ == 3);
551 3         6 my ($matrix,$row,$row_vec) = @_;
552 3         44 my ($rows1,$cols1) = $matrix->dim();
553 3         30 my ($rows2,$cols2) = $row_vec->dim();
554            
555 2 100       249 croak "Math::MatrixReal::assign_row(): number of columns mismatch" if ($cols1 != $cols2);
556 1 50       4 croak "Math::MatrixReal::assign_row(): not a row vector" unless( $rows2 == 1);
557              
558 1         2 @{$matrix->[0][--$row]} = @{$row_vec->[0][0]};
  1         6  
  1         4  
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 26 my ($matrix) = @_;
593 7         24 my ($rows,$cols) = $matrix->dim();
594              
595 7 50       25 croak "Math::MatrixReal::cofactor(): Matrix is not quadratic"
596             unless ($rows == $cols);
597              
598             # black magic ahead
599             my $cofactor = $matrix->each(
600             sub {
601 133     133   154 my($v,$i,$j) = @_;
602 133 100       446 ($i+$j) % 2 == 0 ? $matrix->minor($i,$j)->det() : -1*$matrix->minor($i,$j)->det();
603 7         67 });
604 7         85 return ($cofactor);
605             }
606              
607             sub adjoint {
608 4     4 0 17 my ($matrix) = @_;
609 4         14 return ~($matrix->cofactor);
610             }
611              
612             sub row
613             {
614 19 50   19 1 70 croak "Usage: \$row_vector = \$matrix->row(\$row);"
615             if (@_ != 2);
616              
617 19         26 my($matrix,$row) = @_;
618 19         39 my($rows,$cols) = ($matrix->[1],$matrix->[2]);
619 19         20 my($temp);
620              
621 19 50 33     96 croak "Math::MatrixReal::row(): row index out of range" if ($row < 1 || $row > $rows);
622              
623 19         22 $row--;
624 19         33 $temp = $matrix->new(1,$cols);
625 19         51 for ( my $j = 0; $j < $cols; $j++ )
626             {
627 107         241 $temp->[0][0][$j] = $matrix->[0][$row][$j];
628             }
629 19         60 return($temp);
630             }
631 4     4 0 9 sub col{ return (shift)->column(shift) }
632             sub column
633             {
634 39 50   39 1 125 croak "Usage: \$column_vector = \$matrix->column(\$column);" if (@_ != 2);
635              
636 39         43 my($matrix,$col) = @_;
637 39         50 my($rows,$cols) = ($matrix->[1],$matrix->[2]);
638             #my($temp);
639             #my($i);
640 39         30 my $col_vector;
641              
642 39 50 33     136 croak "Math::MatrixReal::column(): column index out of range" if ($col < 1 || $col > $cols);
643              
644 39         35 $col--;
645 39         63 $col_vector = $matrix->new($rows,1);
646              
647 39         67 map { $col_vector->[0][$_][0] = $matrix->[0][$_][$col] } (0 .. $rows-1);
  138         268  
648              
649 39         96 return $col_vector;
650             }
651              
652             sub as_list
653             {
654 1 50   1 1 7 croak "Usage: \$matrix->as_list();" if (@_ != 1);
655              
656 1         1 my($self) = @_;
657 1         2 my($rows,$cols) = ($self->[1], $self->[2]);
658 1         1 my @list;
659 1         3 for(my $i = 0; $i < $rows; $i++ ){
660 2         4 for(my $j = 0; $j < $rows; $j++){
661 4         10 push @list, $self->[0][$i][$j];
662             }
663             }
664 1         3 return @list;
665             }
666              
667             sub _undo_LR
668             {
669 3299 50   3299   4891 croak "Usage: \$matrix->_undo_LR();"
670             if (@_ != 1);
671              
672 3299         2656 my($self) = @_;
673              
674 3299         3133 undef $self->[3];
675 3299         2755 undef $self->[4];
676 3299         3500 undef $self->[5];
677             }
678             # brrr
679             sub zero
680             {
681 66 50   66 1 240 croak "Usage: \$matrix->zero();" if (@_ != 1);
682              
683 66         105 my ($self) = @_;
684 66         158 my ($rows,$cols) = ($self->[1],$self->[2]);
685              
686 66         217 $self->_undo_LR();
687              
688             # zero out first row
689 66         263 map { $self->[0][0][$_] = 0.0 } (0 .. $cols-1);
  1058         1412  
690            
691             # copy that to the other rows
692 66         202 map { @{$self->[0][$_]} = @{$self->[0][0]} } (0 .. $rows-1);
  1058         743  
  1058         7206  
  1058         1238  
693              
694 66         122 return $self;
695             }
696              
697             sub one
698             {
699 24 50   24 1 114 croak "Usage: \$matrix->one();" if (@_ != 1);
700              
701 24         40 my ($self) = @_;
702 24         197 my ($rows,$cols) = ($self->[1],$self->[2]);
703              
704 24         80 $self->zero(); # We rely on zero() efficiency
705              
706 24         54 map { $self->[0][$_][$_] = 1.0 } (0 .. $rows-1);
  225         279  
707              
708 24         75 return $self;
709             }
710              
711             sub assign
712             {
713 2788 50   2788 1 3869 croak "Usage: \$matrix->assign(\$row,\$column,\$value);" if (@_ != 4);
714              
715 2788         2396 my($self,$row,$col,$value) = @_;
716 2788         2639 my($rows,$cols) = ($self->[1],$self->[2]);
717              
718 2788 50 33     7935 croak "Math::MatrixReal::assign(): row index out of range" if (($row < 1) || ($row > $rows));
719 2788 50 33     7528 croak "Math::MatrixReal::assign(): column index out of range" if (($col < 1) || ($col > $cols));
720              
721 2788         3561 $self->_undo_LR();
722 2788         6378 $self->[0][--$row][--$col] = $value;
723             }
724              
725             sub element
726             {
727 3243 50   3243 1 5388 croak "Usage: \$value = \$matrix->element(\$row,\$column);" if (@_ != 3);
728              
729 3243         2786 my($self,$row,$col) = @_;
730 3243         3146 my($rows,$cols) = ($self->[1],$self->[2]);
731              
732 3243 50 33     9608 croak "Math::MatrixReal::element(): row index out of range" if (($row < 1) || ($row > $rows));
733 3243 50 33     8901 croak "Math::MatrixReal::element(): column index out of range" if (($col < 1) || ($col > $cols));
734              
735 3243         6750 return( $self->[0][--$row][--$col] );
736             }
737              
738             sub dim # returns dimensions of a matrix
739             {
740 868 50   868 0 3281 croak "Usage: (\$rows,\$columns) = \$matrix->dim();" if (@_ != 1);
741              
742 868         789 my($matrix) = @_;
743              
744 868         1603 return( $matrix->[1], $matrix->[2] );
745             }
746              
747             sub norm_one # maximum of sums of each column
748             {
749 191 50   191 0 460 croak "Usage: \$norm_one = \$matrix->norm_one();" if (@_ != 1);
750              
751 191         257 my($self) = @_;
752 191         319 my($rows,$cols) = ($self->[1],$self->[2]);
753              
754 191         252 my $max = 0.0;
755 191         431 for (my $j = 0; $j < $cols; $j++)
756             {
757 1343         1080 my $sum = 0.0;
758 1343         2011 for (my $i = 0; $i < $rows; $i++)
759             {
760 21545         36356 $sum += abs( $self->[0][$i][$j] );
761             }
762 1343 100       3159 $max = $sum if ($sum > $max);
763             }
764 191         1068 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         2 my ($matrix) = @_;
770 1         1 my $norm = 0;
771 1     25   10 $matrix->each( sub { $norm+=abs(shift); } );
  25         51  
772 1         5 return $norm;
773             }
774             sub norm_frobenius {
775 1     1 0 7 my ($m) = @_;
776 1         3 my ($r,$c) = $m->dim;
777 1         1 my $s=0;
778              
779 1     4   6 $m->each( sub { $s+=abs(shift)**2 } );
  4         11  
780 1         6 return sqrt($s);
781             }
782              
783             # Vector Norm
784             sub norm_p {
785 5     5 0 18 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     23 croak "Math::MatrixReal::norm_p: $p must be >= 1"
790             unless ($p =~ m/Inf(inity)?/i || $p >= 1);
791              
792 5 100       13 if( $p =~ m/^(Inf|Infinity)$/i ){
793 1         3 my $max = $v->element(1,1);
794 1 100   3   5 $v->each ( sub { my $x=abs(shift); $max = $x if( $x > $max ); } );
  3         4  
  3         18  
795 1         6 return $max;
796             }
797              
798 4         2 my $s=0;
799 4     12   21 $v->each( sub { $s+= (abs(shift))**$p; } );
  12         39  
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         2 my($rows,$cols) = ($self->[1],$self->[2]);
809              
810 1         1 my $max = 0.0;
811 1         4 for (my $i = 0; $i < $rows; $i++)
812             {
813 5         4 my $sum = 0.0;
814 5         7 for (my $j = 0; $j < $cols; $j++)
815             {
816 25         38 $sum += abs( $self->[0][$i][$j] );
817             }
818 5 100       13 $max = $sum if ($sum > $max);
819             }
820 1         3 return($max);
821             }
822              
823             sub negate
824             {
825 1 50   1 0 5 croak "Usage: \$matrix1->negate(\$matrix2);"
826             if (@_ != 2);
827              
828 1         2 my($matrix1,$matrix2) = @_;
829 1         3 my($rows1,$cols1) = ($matrix1->[1],$matrix1->[2]);
830 1         4 my($rows2,$cols2) = ($matrix2->[1],$matrix2->[2]);
831              
832 1 50 33     8 croak "Math::MatrixReal::negate(): matrix size mismatch"
833             unless (($rows1 == $rows2) && ($cols1 == $cols2));
834              
835 1         4 $matrix1->_undo_LR();
836              
837 1         5 for (my $i = 0; $i < $rows1; $i++ )
838             {
839 10         22 for (my $j = 0; $j < $cols1; $j++ )
840             {
841 100         245 $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 353 croak "Usage: \$new_matrix = \$matrix->each( \&sub );" unless (@_ == 2 );
849 92         147 my($matrix,$function) = @_;
850 92         618 my($rows,$cols) = ($matrix->[1],$matrix->[2]);
851 92         273 my($new_matrix) = $matrix->clone();
852              
853 92 50       296 croak "Math::MatrixReal::each(): argument is not a sub reference" unless ref($function);
854 92         260 $new_matrix->_undo_LR();
855              
856 92         248 for (my $i = 0; $i < $rows; $i++ ) {
857 681         1239 for (my $j = 0; $j < $cols; $j++ ) {
858 59     59   334359 no strict 'refs';
  59         135  
  59         11868  
859             # $i,$j are 1-based as of 1.7
860 12643         14646 $new_matrix->[0][$i][$j] = &{ $function }($matrix->[0][$i][$j],$i+1,$j+1) ;
  12643         14738  
861             }
862             }
863 92         347 return ($new_matrix);
864             }
865             ## each_diag(): same as each() but only diag elements
866             sub each_diag {
867 39 50   39 1 101 croak "Usage: \$new_matrix = \$matrix->each_diag( \&sub );" unless (@_ == 2 );
868 39         52 my($matrix,$function) = @_;
869 39         67 my($rows,$cols) = ($matrix->[1],$matrix->[2]);
870 39         96 my($new_matrix) = $matrix->clone();
871              
872 39 50       109 croak "Math::MatrixReal::each(): argument is not a sub reference" unless ref($function);
873 39 50       79 croak "Matrix is not quadratic" unless ($rows == $cols);
874              
875 39         85 $new_matrix->_undo_LR();
876              
877 39         108 for (my $i = 0; $i < $rows; $i++ ) {
878 166         318 for (my $j = 0; $j < $cols; $j++ ) {
879 972 100       2167 next unless ($i == $j);
880 59     59   328 no strict 'refs';
  59         111  
  59         810939  
881             # $i,$j are 1-based as of 1.7
882 166         255 $new_matrix->[0][$i][$j] = &{ $function }($matrix->[0][$i][$j],$i+1,$j+1) ;
  166         282  
883             }
884             }
885 39         113 return ($new_matrix);
886             }
887              
888             ## Make computing the inverse more user friendly
889             sub inverse {
890 19 50   19 0 844 croak "Usage: \$inverse = \$matrix->inverse();" unless (@_ == 1);
891 19         36 my ($matrix) = @_;
892 19         70 return $matrix->decompose_LR->invert_LR;
893             }
894              
895             sub transpose {
896              
897 69 50   69 0 206 croak "Usage: \$matrix1->transpose(\$matrix2);" if (@_ != 2);
898              
899 69         100 my($matrix1,$matrix2) = @_;
900 69         125 my($rows1,$cols1) = ($matrix1->[1],$matrix1->[2]);
901 69         118 my($rows2,$cols2) = ($matrix2->[1],$matrix2->[2]);
902              
903 69 50 33     371 croak "Math::MatrixReal::transpose(): matrix size mismatch"
904             unless (($rows1 == $cols2) && ($cols1 == $rows2));
905              
906 69         165 $matrix1->_undo_LR();
907              
908 69 100       163 if ($rows1 == $cols1)
909             {
910             # more complicated to make in-place possible!
911              
912 59         185 for (my $i = 0; $i < $rows1; $i++)
913             {
914 597         1008 for (my $j = ($i + 1); $j < $cols1; $j++)
915             {
916 6987         6771 my $swap = $matrix2->[0][$i][$j];
917 6987         7697 $matrix1->[0][$i][$j] = $matrix2->[0][$j][$i];
918 6987         11551 $matrix1->[0][$j][$i] = $swap;
919             }
920 597         1298 $matrix1->[0][$i][$i] = $matrix2->[0][$i][$i];
921             }
922             } else { # ($rows1 != $cols1)
923 10         26 for (my $i = 0; $i < $rows1; $i++)
924             {
925 38         55 for (my $j = 0; $j < $cols1; $j++)
926             {
927 38         129 $matrix1->[0][$i][$j] = $matrix2->[0][$j][$i];
928             }
929             }
930             }
931             }
932              
933             sub add
934             {
935 25 50   25 0 86 croak "Usage: \$matrix1->add(\$matrix2,\$matrix3);" if (@_ != 3);
936              
937 25         56 my($matrix1,$matrix2,$matrix3) = @_;
938 25         62 my($rows1,$cols1) = ($matrix1->[1],$matrix1->[2]);
939 25         58 my($rows2,$cols2) = ($matrix2->[1],$matrix2->[2]);
940 25         59 my($rows3,$cols3) = ($matrix3->[1],$matrix3->[2]);
941              
942 25 50 33     305 croak "Math::MatrixReal::add(): matrix size mismatch"
      33        
      33        
943             unless (($rows1 == $rows2) && ($rows1 == $rows3) &&
944             ($cols1 == $cols2) && ($cols1 == $cols3));
945              
946 25         69 $matrix1->_undo_LR();
947              
948 25         87 for ( my $i = 0; $i < $rows1; $i++ )
949             {
950 378         586 for ( my $j = 0; $j < $cols1; $j++ )
951             {
952 11142         23615 $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 413 croak "Usage: \$matrix1->subtract(\$matrix2,\$matrix3);" if (@_ != 3);
960              
961 171         240 my($matrix1,$matrix2,$matrix3) = @_;
962 171         281 my($rows1,$cols1) = ($matrix1->[1],$matrix1->[2]);
963 171         272 my($rows2,$cols2) = ($matrix2->[1],$matrix2->[2]);
964 171         276 my($rows3,$cols3) = ($matrix3->[1],$matrix3->[2]);
965              
966 171 50 33     1479 croak "Math::MatrixReal::subtract(): matrix size mismatch"
      33        
      33        
967             unless (($rows1 == $rows2) && ($rows1 == $rows3) &&
968             ($cols1 == $cols2) && ($cols1 == $cols3));
969              
970 171         357 $matrix1->_undo_LR();
971              
972 171         415 for ( my $i = 0; $i < $rows1; $i++ )
973             {
974 1537         2222 for ( my $j = 0; $j < $cols1; $j++ )
975             {
976 21703         47923 $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 65 croak "Usage: \$matrix1->multiply_scalar(\$matrix2,\$scalar);"
984             if (@_ != 3);
985              
986 24         35 my($matrix1,$matrix2,$scalar) = @_;
987 24         51 my($rows1,$cols1) = ($matrix1->[1],$matrix1->[2]);
988 24         49 my($rows2,$cols2) = ($matrix2->[1],$matrix2->[2]);
989              
990 24 50 33     124 croak "Math::MatrixReal::multiply_scalar(): matrix size mismatch"
991             unless (($rows1 == $rows2) && ($cols1 == $cols2));
992              
993 24         60 $matrix1->_undo_LR();
994              
995 24         90 for ( my $i = 0; $i < $rows1; $i++ )
996             {
997 249         416 map { $matrix1->[0][$i][$_] = $matrix2->[0][$i][$_] * $scalar } (0 .. $cols1-1);
  4111         6645  
998             }
999             }
1000              
1001             sub multiply
1002             {
1003 45 50   45 0 136 croak "Usage: \$product_matrix = \$matrix1->multiply(\$matrix2);"
1004             if (@_ != 2);
1005              
1006 45         60 my($matrix1,$matrix2) = @_;
1007 45         87 my($rows1,$cols1) = ($matrix1->[1],$matrix1->[2]);
1008 45         83 my($rows2,$cols2) = ($matrix2->[1],$matrix2->[2]);
1009              
1010 45 50       125 croak "Math::MatrixReal::multiply(): matrix size mismatch" unless ($cols1 == $rows2);
1011              
1012 45         104 my $temp = $matrix1->new($rows1,$cols2);
1013 45         169 for (my $i = 0; $i < $rows1; $i++ )
1014             {
1015 417         680 for (my $j = 0; $j < $cols2; $j++ )
1016             {
1017 8427         6168 my $sum = 0.0;
1018 8427         11728 for (my $k = 0; $k < $cols1; $k++ )
1019             {
1020 224889         394311 $sum += ( $matrix1->[0][$i][$k] * $matrix2->[0][$k][$j] );
1021             }
1022 8427         16782 $temp->[0][$i][$j] = $sum;
1023             }
1024             }
1025 45         506 return($temp);
1026             }
1027              
1028             sub exponent {
1029 21 50   21 0 95 croak "Usage: \$matrix_exp = \$matrix1->exponent(\$integer);" if(@_ != 2 );
1030 21         30 my($matrix,$argument) = @_;
1031 21         43 my($rows,$cols) = ($matrix->[1],$matrix->[2]);
1032 21         33 my($name) = "'**'";
1033 21         57 my($temp) = $matrix->clone();
1034              
1035 21 50       53 croak "Matrix is not quadratic" unless ($rows == $cols);
1036 21 50       160 croak "Exponent must be integer" unless ($argument =~ m/^[+-]?\d+$/ );
1037              
1038 21 100       65 return($matrix) if ($argument == 1);
1039              
1040 20         55 $temp->_undo_LR();
1041              
1042             # negative exponent is (A^-1)^n
1043 20 100       78 if( $argument < 0 ){
    100          
1044 5         26 my $LR = $matrix->decompose_LR();
1045 5         26 my $inverse = $LR->invert_LR();
1046 5 50       17 unless (defined $inverse){
1047 0         0 carp "Matrix has no inverse";
1048 0         0 return undef;
1049             }
1050 5         21 $temp = $inverse->clone();
1051 5 50       45 if( $inverse ){
1052 5 100       72 return($inverse) if ($argument == -1);
1053 1         3 for( 2 .. abs($argument) ){
1054 1         3 $temp = multiply($inverse,$temp);
1055             }
1056 1         11 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         13 return ($temp);
1066             }
1067              
1068             # if it is diagonal, just raise diagonal entries to power
1069 12 100       39 if( $matrix->is_diagonal() ){
1070 10     75   86 $temp = $temp->each_diag( sub { (shift)**$argument } );
  75         228  
1071 10         120 return ($temp);
1072            
1073             } else {
1074 2         8 for( 2 .. $argument ){
1075 2         5 $temp = multiply($matrix,$temp);
1076             }
1077 2         10 return ($temp);
1078             }
1079             }
1080              
1081             sub min
1082             {
1083              
1084 3 100   3 0 22 if ( @_ == 1 ) {
1085 2         2 my $matrix = shift;
1086              
1087 2 50       4 croak "Usage: \$minimum = Math::MatrixReal::min(\$number1,\$number2) or $matrix->min" if (@_ > 2);
1088 2 50       5 croak "invalid" unless ref $matrix eq 'Math::MatrixReal';
1089              
1090 2         5 my $min = $matrix->element(1,1);
1091 2 100   500   10 $matrix->each( sub { my ($e,$i,$j) = @_; $min = $e if $e < $min; } );
  500         351  
  500         1223  
1092 2         8 return $min;
1093             }
1094 1 50       11 $_[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       5 croak "Usage: \$maximum = Math::MatrixReal::max(\$number1,\$number2) or $matrix->max" if (@_ > 2);
1103 2 50       5 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         394  
  500         1248  
1107 2         8 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 443 croak "Usage: \$LR_matrix = \$matrix->decompose_LR();"
1198             if (@_ != 1);
1199              
1200 177         246 my($matrix) = @_;
1201 177         291 my($rows,$cols) = ($matrix->[1],$matrix->[2]);
1202 177         734 my($perm_row,$perm_col);
1203 0         0 my($row,$col,$max);
1204 0         0 my($i,$j,$k,$n);
1205 177         223 my($sign) = 1;
1206 177         184 my($swap);
1207             my($temp);
1208              
1209 177 50       315 croak "Math::MatrixReal::decompose_LR(): matrix is not quadratic"
1210             unless ($rows == $cols);
1211              
1212 177         376 $temp = $matrix->new($rows,$cols);
1213 177         454 $temp->copy($matrix);
1214 177         191 $n = $rows;
1215 177         218 $perm_row = [ ];
1216 177         216 $perm_col = [ ];
1217 177         395 for ( $i = 0; $i < $n; $i++ )
1218             {
1219 779         864 $perm_row->[$i] = $i;
1220 779         1398 $perm_col->[$i] = $i;
1221             }
1222             NONZERO:
1223 177         362 for ( $k = 0; $k < $n; $k++ ) # use Gauss's algorithm:
1224             {
1225             # complete pivot-search:
1226              
1227 776         713 $max = 0;
1228 776         1334 for ( $i = $k; $i < $n; $i++ )
1229             {
1230 2265         3525 for ( $j = $k; $j < $n; $j++ )
1231             {
1232 8483 100       23913 if (($swap = abs($temp->[0][$i][$j])) > $max)
1233             {
1234 1768         1484 $max = $swap;
1235 1768         1620 $row = $i;
1236 1768         3537 $col = $j;
1237             }
1238             }
1239             }
1240 776 100       1370 last NONZERO if ($max == 0); # (all remaining elements are zero)
1241 773 100       1236 if ($k != $row) # swap row $k and row $row:
1242             {
1243 373         357 $sign = -$sign;
1244 373         395 $swap = $perm_row->[$k];
1245 373         446 $perm_row->[$k] = $perm_row->[$row];
1246 373         522 $perm_row->[$row] = $swap;
1247 373         695 for ( $j = 0; $j < $n; $j++ )
1248             {
1249             # (must run from 0 since L has to be swapped too!)
1250              
1251 1883         2057 $swap = $temp->[0][$k][$j];
1252 1883         2344 $temp->[0][$k][$j] = $temp->[0][$row][$j];
1253 1883         3927 $temp->[0][$row][$j] = $swap;
1254             }
1255             }
1256 773 100       1204 if ($k != $col) # swap column $k and column $col:
1257             {
1258 396         533 $sign = -$sign;
1259 396         472 $swap = $perm_col->[$k];
1260 396         435 $perm_col->[$k] = $perm_col->[$col];
1261 396         390 $perm_col->[$col] = $swap;
1262 396         748 for ( $i = 0; $i < $n; $i++ )
1263             {
1264 1980         2143 $swap = $temp->[0][$i][$k];
1265 1980         2534 $temp->[0][$i][$k] = $temp->[0][$i][$col];
1266 1980         3656 $temp->[0][$i][$col] = $swap;
1267             }
1268             }
1269 773         1560 for ( $i = ($k + 1); $i < $n; $i++ )
1270             {
1271             # scan the remaining rows, add multiples of row $k to row $i:
1272              
1273 1486         2195 $swap = $temp->[0][$i][$k] / $temp->[0][$k][$k];
1274 1486 100       2565 if ($swap != 0)
1275             {
1276             # calculate a row of matrix R:
1277              
1278 1357         2134 for ( $j = ($k + 1); $j < $n; $j++ )
1279             {
1280 4114         8725 $temp->[0][$i][$j] -= $temp->[0][$k][$j] * $swap;
1281             }
1282              
1283             # store matrix L in same matrix as R:
1284              
1285 1357         3681 $temp->[0][$i][$k] = $swap;
1286             }
1287             }
1288             }
1289 177         405 $temp->[3] = $sign;
1290 177         251 $temp->[4] = $perm_row;
1291 177         245 $temp->[5] = $perm_col;
1292 177         678 return($temp);
1293             }
1294              
1295             sub solve_LR
1296             {
1297 113 50   113 0 356 croak "Usage: (\$dimension,\$x_vector,\$base_matrix) = \$LR_matrix->solve_LR(\$b_vector);"
1298             if (@_ != 2);
1299              
1300 113         168 my($LR_matrix,$b_vector) = @_;
1301 113         164 my($rows,$cols) = ($LR_matrix->[1],$LR_matrix->[2]);
1302 113         122 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     562 croak "Math::MatrixReal::solve_LR(): not an LR decomposition matrix"
1308             unless ((defined $LR_matrix->[3]) && ($rows == $cols));
1309              
1310 113         136 $n = $rows;
1311              
1312 113 50       240 croak "Math::MatrixReal::solve_LR(): vector is not a column vector"
1313             unless ($b_vector->[2] == 1);
1314              
1315 113 50       268 croak "Math::MatrixReal::solve_LR(): matrix and vector size mismatch"
1316             unless ($b_vector->[1] == $n);
1317              
1318 113         137 $perm_row = $LR_matrix->[4];
1319 113         187 $perm_col = $LR_matrix->[5];
1320              
1321 113         239 $x_vector = $b_vector->new($n,1);
1322 113         231 $y_vector = $b_vector->new($n,1);
1323 113         234 $base_matrix = $LR_matrix->new($n,$n);
1324              
1325             # calculate "x" so that LRx = b ==> calculate Ly = b, Rx = y:
1326              
1327 113         318 for ( $i = 0; $i < $n; $i++ ) # calculate $y_vector:
1328             {
1329 665         1139 $sum = $b_vector->[0][($perm_row->[$i])][0];
1330 665         1518 for ( $j = 0; $j < $i; $j++ )
1331             {
1332 1987         4833 $sum -= $LR_matrix->[0][$i][$j] * $y_vector->[0][$j][0];
1333             }
1334 665         2407 $y_vector->[0][$i][0] = $sum;
1335             }
1336              
1337 113         142 $dimension = 0;
1338 113         260 for ( $i = ($n - 1); $i >= 0; $i-- ) # calculate $x_vector:
1339             {
1340 665 50       1209 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         909 $sum = $y_vector->[0][$i][0];
1353 665         1208 for ( $j = ($i + 1); $j < $n; $j++ )
1354             {
1355 1987         5858 $sum -= $LR_matrix->[0][$i][$j] *
1356             $x_vector->[0][($perm_col->[$j])][0];
1357             }
1358 665         2582 $x_vector->[0][($perm_col->[$i])][0] =
1359             $sum / $LR_matrix->[0][$i][$i];
1360             }
1361             }
1362 113 50       218 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         694 return( $dimension, $x_vector, $base_matrix );
1386             }
1387              
1388             sub invert_LR
1389             {
1390 25 50   25 0 109 croak "Usage: \$inverse_matrix = \$LR_matrix->invert_LR();"
1391             if (@_ != 1);
1392              
1393 25         50 my($matrix) = @_;
1394 25         155 my($rows,$cols) = ($matrix->[1],$matrix->[2]);
1395 25         42 my($inv_matrix,$x_vector,$y_vector);
1396 0         0 my($i,$j,$n);
1397              
1398 25 50 33     202 croak "Math::MatrixReal::invert_LR(): not an LR decomposition matrix"
1399             unless ((defined $matrix->[3]) && ($rows == $cols));
1400              
1401 25         126 $n = $rows;
1402             #print Dumper [ $matrix ];
1403 25 50       125 if ($matrix->[0][$n-1][$n-1] != 0)
1404             {
1405 25         87 $inv_matrix = $matrix->new($n,$n);
1406 25         74 $y_vector = $matrix->new($n,1);
1407 25         97 for ( $j = 0; $j < $n; $j++ )
1408             {
1409 112 100       218 if ($j > 0)
1410             {
1411 87         180 $y_vector->[0][$j-1][0] = 0;
1412             }
1413 112         179 $y_vector->[0][$j][0] = 1;
1414 112 50       281 if (($rows,$x_vector,$cols) = $matrix->solve_LR($y_vector))
1415             {
1416 112         271 for ( $i = 0; $i < $n; $i++ )
1417             {
1418 662         2358 $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         254 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 4 croak "Usage: \$condition = \$matrix->condition(\$inverse_matrix);" if (@_ != 2);
1439              
1440 1         2 my($matrix1,$matrix2) = @_;
1441 1         3 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       4 croak "Math::MatrixReal::condition(): 2nd matrix is not quadratic"
1448             unless ($rows2 == $cols2);
1449              
1450 1 50 33     5 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 1 458 croak "Usage: \$determinant = \$matrix->det_LR();" unless (@_ == 1);
1461 172         199 my ($matrix) = @_;
1462 172         305 my ($rows,$cols) = $matrix->dim();
1463 172         202 my $det = 1;
1464              
1465 172 50       309 croak "Math::MatrixReal::det(): Matrix is not quadratic"
1466             unless ($rows == $cols);
1467            
1468             # diagonal will match too
1469 172 100       311 if( $matrix->is_upper_triangular() ){
    100          
1470 24     72   117 $matrix->each_diag( sub { $det*=shift; } );
  72         233  
1471             } elsif ( $matrix->is_lower_triangular() ){
1472 3     9   20 $matrix->each_diag( sub { $det*=shift; } );
  9         32  
1473             } else {
1474 145         267 return $matrix->decompose_LR->det_LR();
1475             }
1476 27         136 return $det;
1477             }
1478              
1479             sub det_LR # determinant of LR decomposition matrix
1480             {
1481 145 50   145 0 290 croak "Usage: \$determinant = \$LR_matrix->det_LR();"
1482             if (@_ != 1);
1483              
1484 145         176 my($matrix) = @_;
1485 145         203 my($rows,$cols) = ($matrix->[1],$matrix->[2]);
1486 145         121 my($k,$det);
1487              
1488 145 50 33     674 croak "Math::MatrixReal::det_LR(): not an LR decomposition matrix"
1489             unless ((defined $matrix->[3]) && ($rows == $cols));
1490              
1491 145         159 $det = $matrix->[3];
1492 145         384 for ( $k = 0; $k < $rows; $k++ )
1493             {
1494 644         1196 $det *= $matrix->[0][$k][$k];
1495             }
1496 145         1188 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 11 croak "Usage: \$order = \$LR_matrix->order_LR();"
1506             if (@_ != 1);
1507              
1508 5         9 my($matrix) = @_;
1509 5         9 my($rows,$cols) = ($matrix->[1],$matrix->[2]);
1510 5         4 my($order);
1511              
1512 5 50 33     23 croak "Math::MatrixReal::order_LR(): not an LR decomposition matrix"
1513             unless ((defined $matrix->[3]) && ($rows == $cols));
1514              
1515             ZERO:
1516 5         11 for ( $order = ($rows - 1); $order >= 0; $order-- )
1517             {
1518 10 100       28 last ZERO if ($matrix->[0][$order][$order] != 0);
1519             }
1520 5         29 return(++$order);
1521             }
1522              
1523             sub scalar_product
1524             {
1525 2 50   2 0 8 croak "Usage: \$scalar_product = \$vector1->scalar_product(\$vector2);"
1526             if (@_ != 2);
1527              
1528 2         4 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       5 croak "Math::MatrixReal::scalar_product(): 1st vector is not a column vector"
1533             unless ($cols1 == 1);
1534              
1535 2 50       4 croak "Math::MatrixReal::scalar_product(): 2nd vector is not a column vector"
1536             unless ($cols2 == 1);
1537              
1538 2 50       5 croak "Math::MatrixReal::scalar_product(): vector size mismatch"
1539             unless ($rows1 == $rows2);
1540              
1541 2         1 my $sum = 0;
1542 2         6 map { $sum += $vector1->[0][$_][0] * $vector2->[0][$_][0] } ( 0 .. $rows1-1);
  6         10  
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         2 my($vector1,$vector2) = @_;
1551 1         2 my($rows1,$cols1) = ($vector1->[1],$vector1->[2]);
1552 1         3 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       2 croak "Math::MatrixReal::vector_product(): vector size mismatch"
1563             unless ($rows1 == $rows2);
1564              
1565 1         2 $n = $rows1;
1566              
1567 1 50       3 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         7 $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         6 $temp->[0][2][0] = $vector1->[0][0][0] * $vector2->[0][1][0] -
1576             $vector1->[0][1][0] * $vector2->[0][0][0];
1577 1         3 return($temp);
1578             }
1579              
1580             sub length
1581             {
1582 2 50   2 0 7 croak "Usage: \$length = \$vector->length();" if (@_ != 1);
1583              
1584 2         2 my($vector) = @_;
1585 2         4 my($rows,$cols) = ($vector->[1],$vector->[2]);
1586 2         2 my($k,$comp,$sum);
1587              
1588 2 50 33     8 croak "Math::MatrixReal::length(): vector is not a row or column vector"
1589             unless ($cols == 1 || $rows ==1 );
1590              
1591 2 50       5 $vector = ~$vector if ($rows == 1 );
1592              
1593 2         2 $sum = 0;
1594 2         4 for ( $k = 0; $k < $rows; $k++ )
1595             {
1596 6         9 $comp = $vector->[0][$k][0];
1597 6         12 $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   50 my ($Q) = @_;
1848 38         95 my ($rows, $cols) = ($Q->[1], $Q->[2]);
1849            
1850             # Creates tridiagonal
1851             # Set up tridiagonal needed elements
1852 38         52 my $d = []; # N Diagonal elements 0...n-1
1853 38         67 my $e = []; # N-1 Off-Diagonal elements 0...n-2
1854            
1855 38         55 my @p = ();
1856 38         128 for (my $i = ($rows-1); $i > 1; $i--)
1857             {
1858 814         678 my $scale = 0.0;
1859             # Computes norm of one column (below diagonal)
1860 814         1284 for (my $k = 0; $k < $i; $k++)
1861             {
1862 11150         18942 $scale += abs($Q->[0][$i][$k]);
1863             }
1864 814 50       1273 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         666 my $h = 0.0;
1871 814         1264 for (my $k = 0; $k < $i; $k++)
1872             { # Used scaled Q for transformation
1873 11150         10179 $Q->[0][$i][$k] /= $scale;
1874             # Form sigma in h
1875 11150         19841 $h += $Q->[0][$i][$k] * $Q->[0][$i][$k];
1876             }
1877 814         1020 my $t1 = $Q->[0][$i][$i-1];
1878 814 100       1374 my $t2 = (($t1 >= 0.0) ? -sqrt($h) : sqrt($h));
1879 814         1145 $e->[$i-1] = $scale * $t2; # Update off-diagonals
1880 814         694 $h -= $t1 * $t2;
1881 814         976 $Q->[0][$i][$i-1] -= $t2;
1882 814         648 my $f = 0.0;
1883 814         1201 for (my $j = 0; $j < $i; $j++)
1884             {
1885 11150         14187 $Q->[0][$j][$i] = $Q->[0][$i][$j] / $h;
1886 11150         8397 my $g = 0.0;
1887 11150         15331 for (my $k = 0; $k <= $j; $k++)
1888             {
1889 108062         186346 $g += $Q->[0][$j][$k] * $Q->[0][$i][$k];
1890             }
1891 11150         16583 for (my $k = $j+1; $k < $i; $k++)
1892             {
1893 96912         171251 $g += $Q->[0][$k][$j] * $Q->[0][$i][$k];
1894             }
1895             # Form elements of P
1896 11150         9006 $p[$j] = $g / $h;
1897 11150         20620 $f += $p[$j] * $Q->[0][$i][$j];
1898             }
1899 814         772 my $hh = $f / ($h + $h);
1900 814         1343 for (my $j = 0; $j < $i; $j++)
1901             {
1902 11150         11046 my $t3 = $Q->[0][$i][$j];
1903 11150         10303 my $t4 = $p[$j] - $hh * $t3;
1904 11150         8454 $p[$j] = $t4;
1905 11150         15367 for (my $k = 0; $k <= $j; $k++)
1906             {
1907 108062         210602 $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         80 $e->[0] = $Q->[0][1][0];
1915 38         112 $d->[0] = $Q->[0][0][0]; # i==0
1916 38         63 $Q->[0][0][0] = 1.0;
1917 38         93 $d->[1] = $Q->[0][1][1]; # i==1
1918 38         77 $Q->[0][1][1] = 1.0;
1919 38         89 $Q->[0][1][0] = $Q->[0][0][1] = 0.0;
1920 38         111 for (my $i = 2; $i < $rows; $i++)
1921             {
1922 814         1296 for (my $j = 0; $j < $i; $j++)
1923             {
1924 11150         8601 my $g = 0.0;
1925 11150         15734 for (my $k = 0; $k < $i; $k++)
1926             {
1927 204974         374755 $g += $Q->[0][$i][$k] * $Q->[0][$k][$j];
1928             }
1929 11150         15730 for (my $k = 0; $k < $i; $k++)
1930             {
1931 204974         364412 $Q->[0][$k][$j] -= $g * $Q->[0][$k][$i];
1932             }
1933             }
1934 814         1576 $d->[$i] = $Q->[0][$i][$i];
1935             # Reset row and column of Q for next iteration
1936 814         871 $Q->[0][$i][$i] = 1.0;
1937 814         1438 for (my $j = 0; $j < $i; $j++)
1938             {
1939 11150         20765 $Q->[0][$i][$j] = $Q->[0][$j][$i] = 0.0;
1940             }
1941             }
1942 38         240 return ($d, $e);
1943             }
1944              
1945             # Computes sqrt(a*a + b*b), but more carefully...
1946             sub _pythag ($$)
1947             {
1948 58779     58779   46733 my ($a, $b) = @_;
1949 58779         45496 my $aa = abs($a);
1950 58779         42754 my $ab = abs($b);
1951 58779 100       67445 if ($aa > $ab)
1952             {
1953             # NB: Not needed!: return 0.0 if ($aa == 0.0);
1954 16738         13569 my $t = $ab / $aa;
1955 16738         24692 return ($aa * sqrt(1.0 + $t*$t));
1956             } else {
1957 42041 50       54394 return 0.0 if ($ab == 0.0);
1958 42041         32228 my $t = $aa / $ab;
1959 42041         58304 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   60 my ($EV, $d, $e) = @_;
1968 38         93 my ($rows, $cols) = ($EV->[1], $EV->[2]);
1969              
1970 38         69 $e->[$rows-1] = 0.0;
1971             # Start real computation
1972 38         105 for (my $l = 0; $l < $rows; $l++)
1973             {
1974 890         774 my $iter = 0;
1975 890         659 my $m;
1976             OUTER:
1977 890         802 do {
1978 2770         4951 for ($m = $l; $m < ($rows - 1); $m++)
1979             {
1980 28412         29803 my $dd = abs($d->[$m]) + abs($d->[$m+1]);
1981 28412 100       63686 last if ((abs($e->[$m]) + $dd) == $dd);
1982             }
1983 2770 100       6364 if ($m != $l)
1984             {
1985             ## why only allow 30 iterations?
1986 1880 50       3560 croak("Too many iterations!") if ($iter++ >= 30);
1987 1880         3155 my $g = ($d->[$l+1] - $d->[$l])
1988             / (2.0 * $e->[$l]);
1989 1880         3134 my $r = _pythag($g, 1.0);
1990 1880 100       4925 $g = $d->[$m] - $d->[$l]
1991             + $e->[$l] / ($g + (($g >= 0.0) ? abs($r) : -abs($r)));
1992 1880         1751 my ($p,$s,$c) = (0.0, 1.0,1.0);
1993 1880         3095 for (my $i = ($m-1); $i >= $l; $i--)
1994             {
1995 26366         23229 my $ii = $i + 1;
1996 26366         24466 my $f = $s * $e->[$i];
1997 26366         34858 my $t = _pythag($f, $g);
1998 26366         26660 $e->[$ii] = $t;
1999 26366 50       39593 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 26366         22861 my $b = $c * $e->[$i];
2006 26366         19287 $s = $f / $t;
2007 26366         19207 $c = $g / $t;
2008 26366         21618 $g = $d->[$ii] - $p;
2009 26366         31714 my $t2 = ($d->[$i] - $g) * $s + 2.0 * $c * $b;
2010 26366         20675 $p = $s * $t2;
2011 26366         23800 $d->[$ii] = $g + $p;
2012 26366         20205 $g = $c * $t2 - $b;
2013 26366         39486 for (my $k = 0; $k < $rows; $k++)
2014             {
2015 735906         694011 my $t1 = $EV->[0][$k][$ii];
2016 735906         666564 my $t2 = $EV->[0][$k][$i];
2017 735906         813927 $EV->[0][$k][$ii] = $s * $t2 + $c * $t1;
2018 735906         1358891 $EV->[0][$k][$i] = $c * $t2 - $s * $t1;
2019             }
2020             }
2021 1880         1978 $d->[$l] -= $p;
2022 1880         1789 $e->[$l] = $g;
2023 1880         4345 $e->[$m] = 0.0;
2024             }
2025             } while ($m != $l);
2026             }
2027 38         87 return;
2028             }
2029              
2030             # Core householder reduction routine (when eigenvector
2031             # are NOT wanted).
2032             sub _householder_values ($)
2033             {
2034 45     45   66 my ($Q) = @_; # NB: Q is destroyed on output...
2035 45         110 my ($rows, $cols) = ($Q->[1], $Q->[2]);
2036            
2037             # Creates tridiagonal
2038             # Set up tridiagonal needed elements
2039 45         61 my $d = []; # N Diagonal elements 0...n-1
2040 45         62 my $e = []; # N-1 Off-Diagonal elements 0...n-2
2041            
2042 45         62 my @p = ();
2043 45         121 for (my $i = ($rows - 1); $i > 1; $i--)
2044             {
2045 849         724 my $scale = 0.0;
2046 849         1335 for (my $k = 0; $k < $i; $k++)
2047             {
2048 12110         20427 $scale += abs($Q->[0][$i][$k]);
2049             }
2050 849 100       1318 if ($scale == 0.0)
2051             { # skip the transformation
2052 15         48 $e->[$i-1] = $Q->[0][$i][$i-1];
2053             }
2054             else
2055             {
2056 834         652 my $h = 0.0;
2057 834         1300 for (my $k = 0; $k < $i; $k++)
2058             { # Used scaled Q for transformation
2059 12065         10191 $Q->[0][$i][$k] /= $scale;
2060             # Form sigma in h
2061 12065         20687 $h += $Q->[0][$i][$k] * $Q->[0][$i][$k];
2062             }
2063 834         1025 my $t = $Q->[0][$i][$i-1];
2064 834 100       1306 my $t2 = (($t >= 0.0) ? -sqrt($h) : sqrt($h));
2065 834         1021 $e->[$i-1] = $scale * $t2; # Updates off-diagonal
2066 834         766 $h -= $t * $t2;
2067 834         902 $Q->[0][$i][$i-1] -= $t2;
2068 834         726 my $f = 0.0;
2069 834         1257 for (my $j = 0; $j < $i; $j++)
2070             {
2071 12065         8412 my $g = 0.0;
2072 12065         15511 for (my $k = 0; $k <= $j; $k++)
2073             {
2074 133392         220447 $g += $Q->[0][$j][$k] * $Q->[0][$i][$k];
2075             }
2076 12065         16715 for (my $k = $j+1; $k < $i; $k++)
2077             {
2078 121327         202936 $g += $Q->[0][$k][$j] * $Q->[0][$i][$k];
2079             }
2080             # Form elements of P
2081 12065         9346 $p[$j] = $g / $h;
2082 12065         21775 $f += $p[$j] * $Q->[0][$i][$j];
2083             }
2084 834         806 my $hh = $f / ($h + $h);
2085 834         1186 for (my $j = 0; $j < $i; $j++)
2086             {
2087 12065         11387 my $t = $Q->[0][$i][$j];
2088 12065         10037 my $g = $p[$j] - $hh * $t;
2089 12065         8483 $p[$j] = $g;
2090 12065         15931 for (my $k = 0; $k <= $j; $k++)
2091             {
2092 133392         255867 $Q->[0][$j][$k] -= $t * $p[$k]
2093             + $g * $Q->[0][$i][$k];
2094             }
2095             }
2096             }
2097             }
2098             # Updates for i==1
2099 45         223 $e->[0] = $Q->[0][1][0];
2100             # Updates diagonal elements
2101 45         158 for (my $i = 0; $i < $rows; $i++)
2102             {
2103 939         1981 $d->[$i] = $Q->[0][$i][$i];
2104             }
2105 45         265 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   87 my ($M, $d, $e) = @_; # NB: M is not touched...
2113 45         95 my ($rows, $cols) = ($M->[1], $M->[2]);
2114              
2115 45         90 $e->[$rows-1] = 0.0;
2116             # Start real computation
2117 45         111 for (my $l = 0; $l < $rows; $l++)
2118             {
2119 939         737 my $iter = 0;
2120 939         627 my $m;
2121             OUTER:
2122 939         695 do {
2123 2865         4218 for ($m = $l; $m < ($rows - 1); $m++)
2124             {
2125 30736         29681 my $dd = abs($d->[$m]) + abs($d->[$m+1]);
2126 30736 100       62890 last if ((abs($e->[$m]) + $dd) == $dd);
2127             }
2128 2865 100       5070 if ($m != $l)
2129             {
2130 1926 50       2745 croak("Too many iterations!") if ($iter++ >= 30);
2131 1926         2378 my $g = ($d->[$l+1] - $d->[$l])
2132             / (2.0 * $e->[$l]);
2133 1926         2201 my $r = _pythag($g, 1.0);
2134 1926 100       3578 $g = $d->[$m] - $d->[$l]
2135             + $e->[$l] / ($g + (($g >= 0.0) ? abs($r) : -abs($r)));
2136 1926         1676 my ($p,$s,$c) = (0.0, 1.0,1.0);
2137 1926         3100 for (my $i = ($m-1); $i >= $l; $i--)
2138             {
2139 28607         20768 my $ii = $i + 1;
2140 28607         22143 my $f = $s * $e->[$i];
2141 28607         28447 my $t = _pythag($f, $g);
2142 28607         24714 $e->[$ii] = $t;
2143 28607 50       37050 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 28607         22082 my $b = $c * $e->[$i];
2150 28607         18762 $s = $f / $t;
2151 28607         17390 $c = $g / $t;
2152 28607         20788 $g = $d->[$ii] - $p;
2153 28607         28860 my $t2 = ($d->[$i] - $g) * $s + 2.0 * $c * $b;
2154 28607         19166 $p = $s * $t2;
2155 28607         22633 $d->[$ii] = $g + $p;
2156 28607         43896 $g = $c * $t2 - $b;
2157             }
2158 1926         1688 $d->[$l] -= $p;
2159 1926         1732 $e->[$l] = $g;
2160 1926         3423 $e->[$m] = 0.0;
2161             }
2162             } while ($m != $l);
2163             }
2164 45         109 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 20844 my ($A) = @_;
2173 33         107 my ($rows, $cols) = ($A->[1], $A->[2]);
2174              
2175 33 50       81 croak "Matrix is not quadratic"
2176             unless ($rows = $cols);
2177 33 50       121 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         130 my $Q = $A->clone();
2182              
2183             # Do the computation of tridiagonal elements and of
2184             # transformation matrix
2185 33         135 my ($diag, $offdiag) = $Q->_householder_vectors();
2186              
2187             # Creates the tridiagonal matrix
2188 33         194 my $T = $A->shadow();
2189 33         140 for (my $i = 0; $i < $rows; $i++)
2190             { # Set diagonal
2191 790         1318 $T->[0][$i][$i] = $diag->[$i];
2192             }
2193 33         127 for (my $i = 0; $i < ($rows-1); $i++)
2194             { # Set off diagonals
2195 757         836 $T->[0][$i+1][$i] = $offdiag->[$i];
2196 757         1319 $T->[0][$i][$i+1] = $offdiag->[$i];
2197             }
2198 33         2959 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 11580 my ($T,$Q) = @_; # Q may be 0 if the original matrix is really tridiagonal
2207              
2208 33         96 my ($rows, $cols) = ($T->[1], $T->[2]);
2209              
2210 33 50       102 croak "Matrix is not quadratic"
2211             unless ($rows = $cols);
2212 33 50       118 croak "Matrix is not tridiagonal"
2213             unless ($T->is_tridiagonal()); # DONE
2214              
2215 33         64 my $EV;
2216             # Obtain/Creates the todo eigenvectors matrix
2217 33 50       207 if ($Q)
2218             {
2219 33         148 $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         62 my $diag = [ ];
2228             # Initializes it with T
2229 33         94 for (my $i = 0; $i < $rows; $i++)
2230             {
2231 790         1542 $diag->[$i] = $T->[0][$i][$i];
2232             }
2233             # Allocate temporary vector for off-diagonal elements
2234 33         49 my $offdiag = [ ];
2235 33         101 for (my $i = 1; $i < $rows; $i++)
2236             {
2237 757         1396 $offdiag->[$i-1] = $T->[0][$i][$i-1];
2238             }
2239              
2240             # Calls the calculus routine
2241 33         133 $EV->_tridiagonal_QLimplicit($diag, $offdiag);
2242              
2243             # Allocate eigenvalues vector
2244 33         235 my $v = Math::MatrixReal->new($rows,1);
2245             # Fills it
2246 33         132 for (my $i = 0; $i < $rows; $i++)
2247             {
2248 790         1396 $v->[0][$i][0] = $diag->[$i];
2249             }
2250 33         3099 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 35458 my ($M) = @_;
2261 5         19 my ($rows, $cols) = ($M->[1], $M->[2]);
2262            
2263 5 50       20 croak "Matrix is not quadratic"
2264             unless ($rows = $cols);
2265 5 50       27 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         25 my $VEC = $M->clone();
2271              
2272             # Do the computation of tridiagonal elements and of
2273             # transformation matrix
2274 5         30 my ($diag, $offdiag) = $VEC->_householder_vectors();
2275             # Calls the calculus routine for diagonalization
2276 5         30 $VEC->_tridiagonal_QLimplicit($diag, $offdiag);
2277              
2278             # Allocate eigenvalues vector
2279 5         46 my $val = Math::MatrixReal->new($rows,1);
2280             # Fills it
2281 5         21 for (my $i = 0; $i < $rows; $i++)
2282             {
2283 100         204 $val->[0][$i][0] = $diag->[$i];
2284             }
2285 5         253 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 15837 my ($A) = @_;
2293 33         177 my ($rows, $cols) = ($A->[1], $A->[2]);
2294              
2295 33 50       97 croak "Matrix is not quadratic"
2296             unless ($rows = $cols);
2297 33 50       157 croak "Matrix is not symmetric"
2298             unless ($A->is_symmetric());
2299              
2300             # Copy given matrix
2301 33         154 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         132 my ($diag, $offdiag) = $Q->_householder_values();
2307              
2308             # Creates the tridiagonal matrix in Q (avoid allocation)
2309 33         70 my $T = $Q;
2310 33         165 $T->zero();
2311 33         110 for (my $i = 0; $i < $rows; $i++)
2312             { # Set diagonal
2313 790         1238 $T->[0][$i][$i] = $diag->[$i];
2314             }
2315 33         132 for (my $i = 0; $i < ($rows-1); $i++)
2316             { # Set off diagonals
2317 757         870 $T->[0][$i+1][$i] = $offdiag->[$i];
2318 757         1297 $T->[0][$i][$i+1] = $offdiag->[$i];
2319             }
2320 33         3243 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 17006 my ($T) = @_;
2329 33         75 my ($rows, $cols) = ($T->[1], $T->[2]);
2330              
2331 33 50       95 croak "Matrix is not quadratic"
2332             unless ($rows = $cols);
2333 33 50       101 croak "Matrix is not tridiagonal"
2334             unless ($T->is_tridiagonal() ); # DONE
2335              
2336             # Allocates diagonal vector
2337 33         84 my $diag = [ ];
2338             # Initializes it with T
2339 33         119 for (my $i = 0; $i < $rows; $i++)
2340             {
2341 790         1518 $diag->[$i] = $T->[0][$i][$i];
2342             }
2343             # Allocate temporary vector for off-diagonal elements
2344 33         53 my $offdiag = [ ];
2345 33         122 for (my $i = 1; $i < $rows; $i++)
2346             {
2347 757         1448 $offdiag->[$i-1] = $T->[0][$i][$i-1];
2348             }
2349              
2350             # Calls the calculus routine (T is not touched)
2351 33         139 $T->_tridiagonal_QLimplicit_values($diag, $offdiag);
2352              
2353             # Allocate eigenvalues vector
2354 33         230 my $v = Math::MatrixReal->new($rows,1);
2355             # Fills it
2356 33         101 for (my $i = 0; $i < $rows; $i++)
2357             {
2358 790         1257 $v->[0][$i][0] = $diag->[$i];
2359             }
2360 33         1703 return $v;
2361             }
2362              
2363             ## more general routine than sym_eigenvalues
2364             sub eigenvalues ($){
2365 19     19 0 65 my ($matrix) = @_;
2366 19         36 my ($rows,$cols) = $matrix->dim();
2367              
2368 19 50       42 croak "Matrix is not quadratic" unless ($rows == $cols);
2369              
2370 19 100 100     38 if($matrix->is_upper_triangular() || $matrix->is_lower_triangular() ){
2371 17         38 my $l = Math::MatrixReal->new($rows,1);
2372 17         29 map { $l->[0][$_][0] = $matrix->[0][$_][$_] } (0 .. $rows-1);
  64         112  
2373 17         40 return $l;
2374             }
2375              
2376 2 50       7 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 36913 my ($M) = @_;
2390 12         31 my ($rows, $cols) = ($M->[1], $M->[2]);
2391            
2392 12 50       48 croak "Matrix is not quadratic" unless ($rows == $cols);
2393 12 50       40 croak "Matrix is not symmetric" unless ($M->is_symmetric);
2394              
2395             # Copy matrix in temporary
2396 12         44 my $A = $M->clone();
2397             # Do the computation of tridiagonal elements and of
2398             # transformation matrix. A is destroyed
2399 12         50 my ($diag, $offdiag) = $A->_householder_values();
2400             # Calls the calculus routine for diagonalization
2401             # (M is not touched)
2402 12         50 $M->_tridiagonal_QLimplicit_values($diag, $offdiag);
2403              
2404             # Allocate eigenvalues vector
2405 12         53 my $val = Math::MatrixReal->new($rows,1);
2406             # Fills it
2407 12         33 map { $val->[0][$_][0] = $diag->[$_] } ( 0 .. $rows-1);
  149         215  
2408              
2409 12         433 return $val;
2410             }
2411             #TODO: docs+test
2412             sub is_positive_definite {
2413 4     4 0 19 my ($matrix) = @_;
2414 4         9 my ($r,$c) = $matrix->dim;
2415              
2416 4 50       10 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       6 return 0 unless $matrix->is_symmetric;
2419              
2420 3         5 my $ev = $matrix->eigenvalues;
2421 3         2 my $pos = 1;
2422 3 100   9   25 $ev->each(sub { my $x = shift; if ($x <= 0){ $pos=0;return; } } );
  9         8  
  9         31  
  2         2  
  2         8  
2423 3         17 return $pos;
2424             }
2425             #TODO: docs+test
2426             sub is_positive_semidefinite {
2427 4     4 0 20 my ($matrix) = @_;
2428 4         14 my ($r,$c) = $matrix->dim;
2429              
2430 4 50       8 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         5 my $ev = $matrix->eigenvalues;
2435 3         3 my $pos = 1;
2436 3 100   9   12 $ev->each(sub { my $x = shift; if ($x < 0){ $pos=0;return; } } );
  9         4  
  9         38  
  1         2  
  1         3  
2437 3         18 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 14 my ($m) = @_;
2444 9         13 my $r = $m->[1];
2445 9 100       34 $r == 1 ? 1 : 0;
2446             }
2447             sub is_col_vector {
2448 11     11 0 22 my ($m) = @_;
2449 11         9 my $c = $m->[2];
2450 11 100       44 $c == 1 ? 1 : 0;
2451             }
2452              
2453             sub is_orthogonal($) {
2454 4     4 0 15 my ($matrix) = @_;
2455              
2456 4 50       11 return 0 unless $matrix->is_quadratic;
2457              
2458 4         8 my $one = $matrix->shadow();
2459 4         7 $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 19 my ($m) = @_;
2467 4         4 my $pos = 1;
2468 4 100   100   20 $m->each( sub { if( (shift) <= 0){ $pos = 0;return;} } );
  100         243  
  68         50  
  68         154  
2469 4         21 return $pos;
2470             }
2471             sub is_negative($) {
2472 4     4 0 7 my ($m) = @_;
2473 4         4 my $neg = 1;
2474 4 100   100   21 $m->each( sub { if( (shift) >= 0){ $neg = 0;return;} } );
  100         194  
  75         53  
  75         170  
2475 4         21 return $neg;
2476             }
2477              
2478              
2479             sub is_periodic($$) {
2480 7     7 0 13 my ($m,$k) = @_;
2481 7 100       25 return 0 unless $m->is_quadratic();
2482 5 50       18 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 216 my ($M) = @_;
2493 108         240 my ($rows, $cols) = ($M->[1], $M->[2]);
2494             # if it is not quadratic it cannot be symmetric...
2495 108 100       290 return 0 unless ($rows == $cols);
2496             # skip when $i=$j?
2497 106         358 for (my $i = 1; $i < $rows; $i++)
2498             {
2499 1805         2527 for (my $j = 0; $j < $i; $j++)
2500             {
2501 23484 100       55741 return 0 unless ($M->[0][$i][$j] == $M->[0][$j][$i]);
2502             }
2503             }
2504 102         338 return 1;
2505             }
2506             # Boolean check to see if matrix is tridiagonal
2507             sub is_tridiagonal ($) {
2508 72     72 0 161 my ($M) = @_;
2509 72         158 my ($rows,$cols) = ($M->[1],$M->[2]);
2510 72         118 my ($i,$j) = (0,0);
2511             # if it is not quadratic it cannot be tridiag
2512 72 100       176 return 0 unless ($rows == $cols);
2513              
2514 71         225 for(;$i < $rows; $i++ ){
2515 1600         2128 for(;$j < $cols; $j++ ){
2516             #print "debug: testing $i,$j = " . $M->[0][$i][$j] . "\n";
2517             # skip diag and diag+-1
2518 39526 100       49754 next if ($i == $j);
2519 37926 100       48519 next if ($i+1 == $j);
2520 36396 100       45398 next if ($i-1 == $j);
2521 34867 100       75652 return 0 if $M->[0][$i][$j];
2522             }
2523 1599         2573 $j = 0;
2524             }
2525 70         315 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 250 my ($M) = @_;
2531 196         282 my ($rows,$cols) = $M->dim();
2532 196         235 my ($i,$j) = (1,0);
2533 196 100       344 return 0 unless ($rows == $cols);
2534            
2535 194         342 for(;$i < $rows; $i++ ){
2536 261         425 for(;$j < $cols;$j++ ){
2537 646 100       1185 next if ($i <= $j);
2538 393 100       1369 return 0 if $M->[0][$i][$j];
2539             }
2540 109         187 $j = 0;
2541             }
2542 42         103 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 186 my ($M) = @_;
2548 156         243 my ($rows,$cols) = $M->dim();
2549 156         184 my ($i,$j) = (0,1);
2550 156 100       341 return 0 unless ($rows == $cols);
2551              
2552 154         293 for(;$i < $rows; $i++ ){
2553 176         366 for(;$j < $cols;$j++ ){
2554 309 100       533 next if ($i >= $j);
2555 219 100       635 return 0 if $M->[0][$i][$j];
2556             }
2557 28         44 $j = 0;
2558             }
2559 6         21 return 1;
2560             }
2561              
2562             # Boolean check to see if matrix is diagonal
2563             sub is_diagonal ($) {
2564 17     17 0 61 my ($M) = @_;
2565 17         38 my ($rows,$cols) = ($M->[1],$M->[2]);
2566 17         22 my ($i,$j) = (0,0);
2567 17 100       46 return 0 unless ($rows == $cols );
2568 16         43 for(;$i < $rows; $i++ ){
2569 91         148 for(;$j < $cols; $j++ ){
2570             # skip diag elements
2571 707 100       1021 next if ($i == $j);
2572 616 100       1488 return 0 if $M->[0][$i][$j];
2573             }
2574 88         136 $j = 0;
2575             }
2576 13         54 return 1;
2577             }
2578             sub is_quadratic ($) {
2579 14 50   14 0 47 croak "Usage: \$matrix->is_quadratic()" unless (@_ == 1);
2580 14         15 my ($matrix) = @_;
2581 14 100       57 $matrix->[1] == $matrix->[2] ? return 1 : return 0;
2582             }
2583              
2584             sub is_square($) {
2585 1 50   1 0 8 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 40 croak "Usage: \$matrix->is_LR()" unless (@_ == 1);
2591 9 100       58 return (shift)->[3] ? 1 : 0;
2592             }
2593              
2594             sub is_normal{
2595 2     2 0 8 my ($matrix,$eps) = @_;
2596 2         4 my ($rows,$cols) = $matrix->dim;
2597            
2598 2   50     7 $eps ||= 1e-8;
2599              
2600 2 100       6 (~$matrix * $matrix - $matrix * ~$matrix < $eps ) ? 1 : 0;
2601              
2602             }
2603              
2604             sub is_skew_symmetric{
2605 4     4 0 21 my ($m) = @_;
2606 4         11 my ($rows, $cols) = $m->dim;
2607             # if it is not quadratic it cannot be skew symmetric...
2608 4 100       19 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       78 return 0 unless ($m->[0][$i][$j] == -$m->[0][$j][$i]);
2612             }
2613             }
2614 3         8 return 1;
2615              
2616             }
2617             ####
2618             sub is_gramian{
2619 6     6 0 32 my ($m) = @_;
2620 6         13 my ($rows,$cols) = $m->dim;
2621 6         10 my $neg=0;
2622             # gramian matrix must be symmetric
2623 6 100       12 return 0 unless $m->is_symmetric;
2624              
2625             # must have all non-negative eigenvalues
2626 4         13 my $ev = $m->eigenvalues;
2627 4 100   15   36 $ev->each(sub { $neg++ if ((shift)<0) } );
  15         85  
2628              
2629 4 100       30 return $neg ? 0 : 1;
2630             }
2631             sub is_binary{
2632 4     4 0 19 my ($m) = @_;
2633 4         9 my ($rows, $cols) = $m->dim;
2634 4         7 for (my $i = 0; $i < $rows; $i++) {
2635 13         19 for (my $j = 0; $j < $cols; $j++) {
2636 62 100 100     247 return 0 unless ($m->[0][$i][$j] == 1 || $m->[0][$i][$j] == 0);
2637             }
2638             }
2639 3         15 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 14 my ($m) = shift;
2648 2         11 my %args = (
2649             format => "%s",
2650             name => "",
2651             semi => 0,
2652             @_);
2653 2         5 my ($row,$col) = $m->dim;
2654 2         4 my $s = "";
2655            
2656 2 100       7 if( $args{name} ){
2657 1         4 $s = "$args{name} = ";
2658             }
2659 2         4 $s .= "[";
2660             $m->each(
2661 12     12   15 sub { my($x,$i,$j) = @_;
2662 12         34 $s .= sprintf(" $args{format}",$x);
2663 12 100 100     77 $s .= ";\n" if( $j == $col && $i != $row);
2664             }
2665 2         45 );
2666 2         9 $s .= "]";
2667 2 50       6 $s .= ";" if $args{semi};
2668 2         15 return $s;
2669             }
2670             #TODO: docs+test
2671             sub as_yacas{
2672 2     2 0 26 my ($m) = shift;
2673 2         11 my %args = (
2674             format => "%s",
2675             name => "",
2676             semi => 0,
2677             @_);
2678 2         6 my ($row,$col) = $m->dim;
2679 2         4 my $s = "";
2680              
2681 2 100       7 if( $args{name} ){
2682 1         2 $s = "$args{name} := ";
2683             }
2684 2         81 $s .= "{";
2685              
2686             $m->each(
2687 12     12   15 sub { my($x,$i,$j) = @_;
2688 12 100       21 $s .= "{" if ($j == 1);
2689 12         38 $s .= sprintf("$args{format}",$x);
2690 12 100       18 $s .= "," if( $j != $col );
2691 12 100 100     74 $s .= "}," if ($j == $col && $i != $row);
2692             }
2693 2         30 );
2694 2         9 $s .= "}}";
2695              
2696 2         10 return $s;
2697             }
2698              
2699             sub as_latex{
2700 2     2 0 16 my ($m) = shift;
2701 2         15 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         2 my $inside;
2711 2         5 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       14 if( $args{align} !~ m/^(c|l|r)$/ ){
2717 0         0 croak "Math::MatrixReal::as_latex(): Invalid alignment '$args{align}'";
2718             }
2719              
2720 2         11 $s =~ s/%COLS%/$args{align} x $col/em;
  2         8  
2721              
2722 2 100       6 if( $args{name} ){
2723 1         4 $s = "$args{name} = $s";
2724             }
2725             $m->each(
2726             sub {
2727 12     12   14 my ($x,$i,$j) = @_;
2728 12         32 $x = sprintf($args{format},$x);
2729              
2730             # last element in each row gets a \\
2731 12 100 100     50 if ($j == $col && $i != $row){
    100 66        
2732 4         21 $inside .= "$x \\\\"."\n";
2733             # the annoying last line has neither
2734             } elsif( $j == $col && $i == $row){
2735 2         9 $inside .= "$x\n";
2736             } else {
2737 6         21 $inside .= "$x&";
2738             }
2739             }
2740 2         18 );
2741 2 50       13 if($args{displaymath}){
2742 0         0 $s = "\\[$s\\]";
2743             } else {
2744 2         5 $s = "\$$s\$";
2745             }
2746 2         9 $s =~ s/%INSIDE%/$inside/gm;
2747 2         12 return $s;
2748             }
2749             ####
2750             sub spectral_radius
2751             {
2752 5     5 0 25 my ($matrix) = @_;
2753 5         11 my ($r,$c) = $matrix->dim;
2754 5         15 my $ev = $matrix->eigenvalues;
2755 5         7 my $radius=0;
2756 5 100   19   31 $ev->each(sub { my $x = shift; $radius = $x if (abs($x) > $radius); } );
  19         18  
  19         114  
2757 5         38 return $radius;
2758             }
2759              
2760             sub maximum {
2761 8     8 1 15 my ($matrix) = @_;
2762 8         17 my ($rows, $columns) = $matrix->dim;
2763              
2764 8         11 my $max = [];
2765 8         9 my $max_p = [];
2766              
2767 8 100       20 if ($rows == 1) {
    100          
2768 2         7 ($max, $max_p) = _max_column($matrix->row(1)->_transpose, $columns);
2769             } elsif ($columns == 1) {
2770 2         6 ($max, $max_p) = _max_column($matrix->column(1), $rows);
2771             } else {
2772 4         10 for my $c (1..$columns) {
2773 12         25 my ($m, $mp) = _max_column($matrix->column($c), $rows);
2774 12         33 push @$max, $m;
2775 12         17 push @$max_p, $mp;
2776             }
2777             }
2778 8 100       63 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         27 my ($m, $mp) = ($column->element(1, 1), 1);
2786 16         32 for my $l (1..$rows) {
2787 52 100       76 if ($column->element($l, 1) > $m) {
2788 28         37 $m = $column->element($l, 1);
2789 28         46 $mp = $l;
2790             }
2791             }
2792 16         29 return ($m, $mp);
2793             }
2794              
2795             sub minimum {
2796 8     8 1 13 my ($matrix) = @_;
2797 8         16 my ($rows, $columns) = $matrix->dim;
2798              
2799 8         12 my $min = [];
2800 8         8 my $min_p = [];
2801              
2802 8 100       25 if ($rows == 1) {
    100          
2803 2         7 ($min, $min_p) = _min_column($matrix->row(1)->_transpose, $columns);
2804             } elsif ($columns == 1) {
2805 2         6 ($min, $min_p) = _min_column($matrix->column(1), $rows);
2806             } else {
2807 4         9 for my $c (1..$columns) {
2808 12         23 my ($m, $mp) = _min_column($matrix->column($c), $rows);
2809 12         27 push @$min, $m;
2810 12         18 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   19 my ($column, $rows) = @_;
2819              
2820 16         32 my ($m, $mp) = ($column->element(1, 1), 1);
2821 16         43 for my $l (1..$rows) {
2822 52 50       68 if ($column->element($l, 1) < $m) {
2823 0         0 $m = $column->element($l, 1);
2824 0         0 $mp = $l;
2825             }
2826             }
2827 16         30 return ($m, $mp);
2828             }
2829              
2830              
2831              
2832             ########################################
2833             # #
2834             # define overloaded operators section: #
2835             # #
2836             ########################################
2837             sub _concat
2838             {
2839 5     5   661 my($object,$argument,$flag) = @_;
2840 5         14 my($orows,$ocols) = ($object->[1],$object->[2]);
2841 5         8 my($name) = "concat";
2842              
2843              
2844 5 100 66     63 if ((defined $argument) && ref($argument) && (ref($argument) !~ /^SCALAR$|^ARRAY$|^HASH$|^CODE$|^REF$/)) {
    50 66        
2845 4         7 my($arows,$acols) = ($argument->[1],$argument->[2]);
2846 4 100       173 croak "Math::MatrixReal: Matrices must have same number of rows in concatenation" unless ($orows == $arows);
2847 3         8 my $result = $object->new($orows,$ocols+$acols);
2848 3         7 for ( my $i = 0; $i < $arows; $i++ ) {
2849 9         17 for ( my $j = 0; $j < $ocols + $acols; $j++ ) {
2850 51 100       137 $result->[0][$i][$j] = ( $j < $ocols ) ? $object->[0][$i][$j] : $argument->[0][$i][$j - $ocols] ;
2851             }
2852             }
2853 3         19 return $result;
2854             } elsif (defined $argument) {
2855 1         3 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   4 my($object) = @_;
2864              
2865 1         7 my $temp = $object->new($object->[1],$object->[2]);
2866 1         5 $temp->negate($object);
2867 1         8 return($temp);
2868             }
2869              
2870             sub _transpose
2871             {
2872 62     62   502 my ($object) = @_;
2873 62         193 my $temp = $object->new($object->[2],$object->[1]);
2874 62         224 $temp->transpose($object);
2875 62         245 return $temp;
2876             }
2877              
2878             sub _boolean
2879             {
2880 217     217   300 my($object) = @_;
2881 217         347 my($rows,$cols) = ($object->[1],$object->[2]);
2882              
2883 217         233 my $result = 0;
2884              
2885             BOOL:
2886 217         495 for ( my $i = 0; $i < $rows; $i++ )
2887             {
2888 290         565 for ( my $j = 0; $j < $cols; $j++ )
2889             {
2890 433 100       1241 if ($object->[0][$i][$j] != 0)
2891             {
2892 167         165 $result = 1;
2893 167         319 last BOOL;
2894             }
2895             }
2896             }
2897 217         447 return($result);
2898             }
2899             #TODO: ugly copy+paste
2900             sub _not_boolean
2901             {
2902 1     1   2 my ($object) = @_;
2903 1         3 my ($rows,$cols) = ($object->[1],$object->[2]);
2904              
2905 1         1 my $result = 1;
2906             NOTBOOL:
2907 1         5 for ( my $i = 0; $i < $rows; $i++ )
2908             {
2909 5         8 for ( my $j = 0; $j < $cols; $j++ )
2910             {
2911 25 50       57 if ($object->[0][$i][$j] != 0)
2912             {
2913 0         0 $result = 0;
2914 0         0 last NOTBOOL;
2915             }
2916             }
2917             }
2918 1         4 return($result);
2919             }
2920              
2921             sub _stringify
2922             {
2923 7     7   32 my ($self) = @_;
2924 7         15 my ($rows,$cols) = ($self->[1],$self->[2]);
2925              
2926 7         8 my $precision = $self->[4];
2927              
2928 7 100       23 my $format = !defined $precision ? '% #-19.12E ' : '% #-19.'.$precision.'f ';
2929 7 100 100     26 $format = '% #-12d' if defined $precision && $precision == 0;
2930              
2931 7         43 my $s = '';
2932 7         22 for ( my $i = 0; $i < $rows; $i++ )
2933             {
2934 26         26 $s .= "[ ";
2935 26         42 for ( my $j = 0; $j < $cols; $j++ )
2936             {
2937 98         349 $s .= sprintf $format , $self->[0][$i][$j];
2938             }
2939 26         44 $s .= "]\n";
2940             }
2941 7         37 return $s;
2942             }
2943              
2944             sub _norm
2945             {
2946 159     159   228 my ($self) = @_;
2947              
2948 159         371 return $self->norm_one() ;
2949             }
2950              
2951             sub _add
2952             {
2953 25     25   67 my($object,$argument,$flag) = @_;
2954 25         60 my($name) = "'+'";
2955              
2956 25 50 33     409 if ((defined $argument) && ref($argument) &&
      33        
2957             (ref($argument) !~ /^SCALAR$|^ARRAY$|^HASH$|^CODE$|^REF$/))
2958             {
2959 25 100       67 if (defined $flag)
2960             {
2961 23         84 my $temp = $object->new($object->[1],$object->[2]);
2962 23         86 $temp->add($object,$argument);
2963 23         164 return($temp);
2964             }
2965             else
2966             {
2967 2         14 $object->add($object,$argument);
2968 2         10 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   1579 my($object,$argument,$flag) = @_;
2980 164         224 my($name) = "'-'";
2981              
2982 164 50 33     2029 if ((defined $argument) && ref($argument) &&
      33        
2983             (ref($argument) !~ /^SCALAR$|^ARRAY$|^HASH$|^CODE$|^REF$/))
2984             {
2985 164 100       292 if (defined $flag)
2986             {
2987 163         472 my $temp = $object->new($object->[1],$object->[2]);
2988 163 50       396 if ($flag) { $temp->subtract($argument,$object); }
  0         0  
2989 163         451 else { $temp->subtract($object,$argument); }
2990 163         641 return $temp;
2991             }
2992             else
2993             {
2994 1         6 $object->subtract($object,$argument);
2995 1         6 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   67 my($matrix, $exp) = @_;
3007 20         47 my($rows,$cols) = ($matrix->[1],$matrix->[2]);
3008              
3009 20         66 return $matrix->exponent( $exp );
3010             }
3011             sub _divide
3012             {
3013 6     6   20 my($matrix,$argument,$flag) = @_;
3014             # TODO: check dimensions of everything!
3015 6         8 my($mrows,$mcols) = ($matrix->[1],$matrix->[2]);
3016 6         8 my($arows,$acols)=(0,0);
3017 6         7 my($name) = "'/'";
3018 6         11 my $temp = $matrix->clone();
3019 6         5 my $arg;
3020 6         5 my ($inv,$m1);
3021              
3022 6 100       16 if( ref($argument) =~ /Math::MatrixReal/ ){
3023 2         3 $arg = $argument->clone();
3024 2         4 ($arows,$acols)=($arg->[1],$arg->[2]);
3025             }
3026             #print "DEBUG: flag= $flag\n";
3027             #print "DEBUG: arg=$arg\n";
3028 6 100       11 if( $flag == 1) {
3029             #print "DEBUG: ref(arg)= " . ref($arg) . "\n";
3030 2 50       5 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       172 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       5 if( $arg->is_col_vector() ){
3053 0         0 print "DEBUG: $arg is a col vector\n";
3054             }
3055 2 50       3 croak "Math::MatrixReal $name: this operation is defined only for square matrices" unless ($arows == $acols);
3056 2         5 $inv = $arg->inverse();
3057 2         8 return $temp->multiply($inv);
3058             } else {
3059 2         8 $temp->multiply_scalar($temp,1/$argument);
3060 2         7 return $temp;
3061             }
3062             }
3063            
3064             }
3065              
3066             sub _multiply
3067             {
3068 53     53   180 my($object,$argument,$flag) = @_;
3069 53         97 my($name) = "'*'";
3070 53         66 my($temp);
3071              
3072 53 100 66     723 if ((defined $argument) && ref($argument) &&
    50 66        
      33        
3073             (ref($argument) !~ /^SCALAR$|^ARRAY$|^HASH$|^CODE$|^REF$/))
3074             {
3075 32 50 33     149 if ((defined $flag) && $flag)
3076             {
3077 0         0 return( multiply($argument,$object) );
3078             }
3079             else
3080             {
3081 32         96 return( multiply($object,$argument) );
3082             }
3083             }
3084             elsif ((defined $argument) && !(ref($argument)))
3085             {
3086 21 100       47 if (defined $flag)
3087             {
3088 20         116 $temp = $object->new($object->[1],$object->[2]);
3089 20         75 $temp->multiply_scalar($object,$argument);
3090 20         244 return($temp);
3091             }
3092             else
3093             {
3094 1         6 $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   5 my($object,$argument) = @_;
3107              
3108 2         9 return( &_add($object,$argument,undef) );
3109             }
3110              
3111             sub _assign_subtract
3112             {
3113 1     1   2 my($object,$argument) = @_;
3114              
3115 1         6 return( &_subtract($object,$argument,undef) );
3116             }
3117              
3118             sub _assign_multiply
3119             {
3120 1     1   2 my($object,$argument) = @_;
3121              
3122 1         4 return( &_multiply($object,$argument,undef) );
3123             }
3124              
3125             sub _assign_exponent {
3126 1     1   5 my($object,$arg) = @_;
3127 1         5 return ( &_exponent($object,$arg,undef) );
3128             }
3129              
3130             sub _equal
3131             {
3132 10     10   315 my($object,$argument,$flag) = @_;
3133 10         14 my($name) = "'=='";
3134 10         20 my($rows,$cols) = ($object->[1],$object->[2]);
3135 10         12 my($i,$j,$result);
3136              
3137 10 100 66     117 if ((defined $argument) && ref($argument) &&
      66        
3138             (ref($argument) !~ /^SCALAR$|^ARRAY$|^HASH$|^CODE$|^REF$/))
3139             {
3140 9         12 $result = 1;
3141             EQUAL:
3142 9         25 for ( $i = 0; $i < $rows; $i++ )
3143             {
3144 65         111 for ( $j = 0; $j < $cols; $j++ )
3145             {
3146 485 50       1140 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         49 return($result);
3154             }
3155             else
3156             {
3157 1         84 croak "Math::MatrixReal $name: wrong argument type";
3158             }
3159             }
3160              
3161             sub _not_equal
3162             {
3163 4     4   8 my($object,$argument,$flag) = @_;
3164 4         6 my($name) = "'!='";
3165 4         8 my($rows,$cols) = ($object->[1],$object->[2]);
3166              
3167 4 100 66     46 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     24 return 1 unless ($r == $rows && $c == $cols );
3172 2         3 my $result = 0;
3173             NOTEQUAL:
3174 2         6 for ( my $i = 0; $i < $rows; $i++ )
3175             {
3176 2         6 for ( my $j = 0; $j < $cols; $j++ )
3177             {
3178 2 50       9 if ($object->[0][$i][$j] != $argument->[0][$i][$j])
3179             {
3180 2         2 $result = 1;
3181 2         6 last NOTEQUAL;
3182             }
3183             }
3184             }
3185 2         17 return $result;
3186             } else {
3187 1         250 croak "Math::MatrixReal $name: wrong argument type";
3188             }
3189             }
3190              
3191             sub _less_than
3192             {
3193 5     5   11 my($object,$argument,$flag) = @_;
3194 5         8 my($name) = "'<'";
3195              
3196 5 100 66     62 if ((defined $argument) && ref($argument) &&
    50 66        
      33        
3197             (ref($argument) !~ /^SCALAR$|^ARRAY$|^HASH$|^CODE$|^REF$/))
3198             {
3199 3 50 33     12 if ((defined $flag) && $flag)
3200             {
3201 0         0 return( $argument->norm_one() < $object->norm_one() );
3202             } else {
3203 3         8 return( $object->norm_one() < $argument->norm_one() );
3204             }
3205             }
3206             elsif ((defined $argument) && !(ref($argument)))
3207             {
3208 2 50 33     11 if ((defined $flag) && $flag)
3209             {
3210 0         0 return( abs($argument) < $object->norm_one() );
3211             } else {
3212 2         6 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   11 my($object,$argument,$flag) = @_;
3222 2         4 my($name) = "'<='";
3223              
3224 2 50 33     33 if ((defined $argument) && ref($argument) &&
    0 33        
      0        
3225             (ref($argument) !~ /^SCALAR$|^ARRAY$|^HASH$|^CODE$|^REF$/))
3226             {
3227 2 50 33     10 if ((defined $flag) && $flag)
3228             {
3229 0         0 return( $argument->norm_one() <= $object->norm_one() );
3230             } else {
3231 2         8 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   6 my($object,$argument,$flag) = @_;
3248 3         4 my($name) = "'>'";
3249              
3250 3 50 33     49 if ((defined $argument) && ref($argument) &&
    0 33        
      0        
3251             (ref($argument) !~ /^SCALAR$|^ARRAY$|^HASH$|^CODE$|^REF$/))
3252             {
3253 3 50 33     12 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   5 my($object,$argument,$flag) = @_;
3274 2         3 my($name) = "'>='";
3275              
3276 2 50 33     22 if ((defined $argument) && ref($argument) &&
    0 33        
      0        
3277             (ref($argument) !~ /^SCALAR$|^ARRAY$|^HASH$|^CODE$|^REF$/))
3278             {
3279 2 50 33     9 if ((defined $flag) && $flag)
3280             {
3281 0         0 return( $argument->norm_one() >= $object->norm_one() );
3282             } else {
3283 2         5 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   14 my($object) = @_;
3300              
3301 4         21 my $temp = $object->new($object->[1],$object->[2]);
3302 4         20 $temp->copy($object);
3303 4         14 $temp->_undo_LR();
3304 4         17 return $temp;
3305             }
3306 59     59   1574 { no warnings; 42 }
  59         113  
  59         4143  
3307             __END__