Parent Directory
|
Revision Log
Copied PG modules over from old system. -sam
1 # Copyright (c) 1996, 1997 by Steffen Beyer. All rights reserved. 2 # Copyright (c) 1999 by Rodolphe Ortalo. All rights reserved. 3 # This package is free software; you can redistribute it and/or 4 # modify it under the same terms as Perl itself. 5 6 # slightly modified for use in WeBWorK 7 # modifications by Michael E Gage -- added a reference to options in the object array ($this) 8 # a better approach would be to rewrite this package so that $this is a hash rather than an array 9 # grep for MEG to see changes. 10 11 # Changed package name to MatrixReal1 throughout. 12 package MatrixReal1; 13 14 use strict; 15 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION); 16 17 require Exporter; 18 19 @ISA = qw(Exporter); 20 21 @EXPORT = qw(); 22 23 @EXPORT_OK = qw(min max); 24 25 %EXPORT_TAGS = (all => [@EXPORT_OK]); 26 27 $VERSION = '1.3a5'; 28 29 use Carp; 30 31 use overload 32 'neg' => '_negate', 33 '~' => '_transpose', 34 'bool' => '_boolean', 35 '!' => '_not_boolean', 36 '""' => '_stringify', 37 'abs' => '_norm', 38 '+' => '_add', 39 '-' => '_subtract', 40 '*' => '_multiply', 41 '+=' => '_assign_add', 42 '-=' => '_assign_subtract', 43 '*=' => '_assign_multiply', 44 '==' => '_equal', 45 '!=' => '_not_equal', 46 '<' => '_less_than', 47 '<=' => '_less_than_or_equal', 48 '>' => '_greater_than', 49 '>=' => '_greater_than_or_equal', 50 'eq' => '_equal', 51 'ne' => '_not_equal', 52 'lt' => '_less_than', 53 'le' => '_less_than_or_equal', 54 'gt' => '_greater_than', 55 'ge' => '_greater_than_or_equal', 56 '=' => '_clone', 57 'fallback' => undef; 58 59 sub new 60 { 61 croak "Usage: \$new_matrix = MatrixReal1->new(\$rows,\$columns);" 62 if (@_ != 3); 63 64 my $proto = shift; 65 my $class = ref($proto) || $proto || 'MatrixReal1'; 66 my $rows = shift; 67 my $cols = shift; 68 my($i,$j); 69 my($this); 70 71 croak "MatrixReal1::new(): number of rows must be > 0" 72 if ($rows <= 0); 73 74 croak "MatrixReal1::new(): number of columns must be > 0" 75 if ($cols <= 0); 76 77 # $this = [ [ ], $rows, $cols ]; 78 $this = [ [ ], $rows, $cols,{} ]; # added a holder for options MEG 79 # see also modifications to LR decomposition 80 81 # Creates first empty row 82 my $empty = [ ]; 83 $#$empty = $cols - 1; # Lengthens the array 84 for (my $j = 0; $j < $cols; $j++) 85 { 86 $empty->[$j] = 0.0; 87 } 88 $this->[0][0] = $empty; 89 # Creates other rows (by copying) 90 for (my $i = 1; $i < $rows; $i++) 91 { 92 my $arow = [ ]; 93 @$arow = @$empty; 94 $this->[0][$i] = $arow; 95 } 96 bless($this, $class); 97 return($this); 98 } 99 100 sub new_from_string 101 { 102 croak "Usage: \$new_matrix = MatrixReal1->new_from_string(\$string);" 103 if (@_ != 2); 104 105 my $proto = shift; 106 my $class = ref($proto) || $proto || 'MatrixReal1'; 107 my $string = shift; 108 my($line,$values); 109 my($rows,$cols); 110 my($row,$col); 111 my($warn); 112 my($this); 113 114 $warn = 0; 115 $rows = 0; 116 $cols = 0; 117 $values = [ ]; 118 while ($string =~ m!^\s* 119 \[ \s+ ( (?: [+-]? \d+ (?: \. \d* )? (?: E [+-]? \d+ )? \s+ )+ ) \] \s*? \n 120 !x) 121 { 122 $line = $1; 123 $string = $'; 124 $values->[$rows] = [ ]; 125 @{$values->[$rows]} = split(' ', $line); 126 $col = @{$values->[$rows]}; 127 if ($col != $cols) 128 { 129 unless ($cols == 0) { $warn = 1; } 130 if ($col > $cols) { $cols = $col; } 131 } 132 $rows++; 133 } 134 if ($string !~ m!^\s*$!) 135 { 136 croak "MatrixReal1::new_from_string(): syntax error in input string"; 137 } 138 if ($rows == 0) 139 { 140 croak "MatrixReal1::new_from_string(): empty input string"; 141 } 142 if ($warn) 143 { 144 warn "MatrixReal1::new_from_string(): missing elements will be set to zero!\n"; 145 } 146 $this = MatrixReal1::new($class,$rows,$cols); 147 for ( $row = 0; $row < $rows; $row++ ) 148 { 149 for ( $col = 0; $col < @{$values->[$row]}; $col++ ) 150 { 151 $this->[0][$row][$col] = $values->[$row][$col]; 152 } 153 } 154 return($this); 155 } 156 157 sub shadow 158 { 159 croak "Usage: \$new_matrix = \$some_matrix->shadow();" 160 if (@_ != 1); 161 162 my($matrix) = @_; 163 my($temp); 164 165 $temp = $matrix->new($matrix->[1],$matrix->[2]); 166 return($temp); 167 } 168 169 170 sub copy 171 { 172 croak "Usage: \$matrix1->copy(\$matrix2);" 173 if (@_ != 2); 174 175 my($matrix1,$matrix2) = @_; 176 my($rows1,$cols1) = ($matrix1->[1],$matrix1->[2]); 177 my($rows2,$cols2) = ($matrix2->[1],$matrix2->[2]); 178 my($i,$j); 179 180 croak "MatrixReal1::copy(): matrix size mismatch" 181 unless (($rows1 == $rows2) && ($cols1 == $cols2)); 182 183 for ( $i = 0; $i < $rows1; $i++ ) 184 { 185 my $r1 = []; # New array ref 186 my $r2 = $matrix2->[0][$i]; 187 @$r1 = @$r2; # Copy whole array directly 188 $matrix1->[0][$i] = $r1; 189 } 190 $matrix1->[3] = $matrix2->[3]; # sign or option 191 if (defined $matrix2->[4]) # is an LR decomposition matrix! 192 { 193 # $matrix1->[3] = $matrix2->[3]; # $sign 194 $matrix1->[4] = $matrix2->[4]; # $perm_row 195 $matrix1->[5] = $matrix2->[5]; # $perm_col 196 $matrix1->[6] = $matrix2->[6]; # $option 197 } 198 } 199 200 sub clone 201 { 202 croak "Usage: \$twin_matrix = \$some_matrix->clone();" 203 if (@_ != 1); 204 205 my($matrix) = @_; 206 my($temp); 207 208 $temp = $matrix->new($matrix->[1],$matrix->[2]); 209 $temp->copy($matrix); 210 return($temp); 211 } 212 213 sub row 214 { 215 croak "Usage: \$row_vector = \$matrix->row(\$row);" 216 if (@_ != 2); 217 218 my($matrix,$row) = @_; 219 my($rows,$cols) = ($matrix->[1],$matrix->[2]); 220 my($temp); 221 my($j); 222 223 croak "MatrixReal1::row(): row index out of range" 224 if (($row < 1) || ($row > $rows)); 225 226 $row--; 227 $temp = $matrix->new(1,$cols); 228 for ( $j = 0; $j < $cols; $j++ ) 229 { 230 $temp->[0][0][$j] = $matrix->[0][$row][$j]; 231 } 232 return($temp); 233 } 234 235 sub column 236 { 237 croak "Usage: \$column_vector = \$matrix->column(\$column);" 238 if (@_ != 2); 239 240 my($matrix,$col) = @_; 241 my($rows,$cols) = ($matrix->[1],$matrix->[2]); 242 my($temp); 243 my($i); 244 245 croak "MatrixReal1::column(): column index out of range" 246 if (($col < 1) || ($col > $cols)); 247 248 $col--; 249 $temp = $matrix->new($rows,1); 250 for ( $i = 0; $i < $rows; $i++ ) 251 { 252 $temp->[0][$i][0] = $matrix->[0][$i][$col]; 253 } 254 return($temp); 255 } 256 257 sub _undo_LR 258 { 259 croak "Usage: \$matrix->_undo_LR();" 260 if (@_ != 1); 261 262 my($this) = @_; 263 my $rh_options = $this->[6]; 264 undef $this->[3]; 265 undef $this->[4]; 266 undef $this->[5]; 267 undef $this->[6]; 268 $this->[3] = $rh_options; 269 } 270 271 sub zero 272 { 273 croak "Usage: \$matrix->zero();" 274 if (@_ != 1); 275 276 my($this) = @_; 277 my($rows,$cols) = ($this->[1],$this->[2]); 278 my($i,$j); 279 280 $this->_undo_LR(); 281 282 # Zero first row 283 for (my $j = 0; $j < $cols; $j++ ) 284 { 285 $this->[0][0][$j] = 0.0; 286 } 287 # Then propagate to other rows 288 for (my $i = 0; $i < $rows; $i++) 289 { 290 @{$this->[0][$i]} = @{$this->[0][0]}; 291 } 292 } 293 294 sub one 295 { 296 croak "Usage: \$matrix->one();" 297 if (@_ != 1); 298 299 my($this) = @_; 300 my($rows,$cols) = ($this->[1],$this->[2]); 301 my($i,$j); 302 303 # No need for this: done by the 'zero()' 304 # $this->_undo_LR(); 305 $this->zero(); # We rely on zero() efficiency 306 for (my $i = 0; $i < $rows; $i++ ) 307 { 308 $this->[0][$i][$i] = 1.0; 309 } 310 } 311 312 sub assign 313 { 314 croak "Usage: \$matrix->assign(\$row,\$column,\$value);" 315 if (@_ != 4); 316 317 my($this,$row,$col,$value) = @_; 318 my($rows,$cols) = ($this->[1],$this->[2]); 319 320 croak "MatrixReal1::assign(): row index out of range" 321 if (($row < 1) || ($row > $rows)); 322 323 croak "MatrixReal1::assign(): column index out of range" 324 if (($col < 1) || ($col > $cols)); 325 326 $this->_undo_LR(); 327 328 $this->[0][--$row][--$col] = $value; 329 } 330 331 sub element 332 { 333 croak "Usage: \$value = \$matrix->element(\$row,\$column);" 334 if (@_ != 3); 335 336 my($this,$row,$col) = @_; 337 my($rows,$cols) = ($this->[1],$this->[2]); 338 339 croak "MatrixReal1::element(): row index out of range" 340 if (($row < 1) || ($row > $rows)); 341 342 croak "MatrixReal1::element(): column index out of range" 343 if (($col < 1) || ($col > $cols)); 344 345 return( $this->[0][--$row][--$col] ); 346 } 347 348 sub dim # returns dimensions of a matrix 349 { 350 croak "Usage: (\$rows,\$columns) = \$matrix->dim();" 351 if (@_ != 1); 352 353 my($matrix) = @_; 354 355 return( $matrix->[1], $matrix->[2] ); 356 } 357 358 sub norm_one # maximum of sums of each column 359 { 360 croak "Usage: \$norm_one = \$matrix->norm_one();" 361 if (@_ != 1); 362 363 my($this) = @_; 364 my($rows,$cols) = ($this->[1],$this->[2]); 365 366 my $max = 0.0; 367 for (my $j = 0; $j < $cols; $j++) 368 { 369 my $sum = 0.0; 370 for (my $i = 0; $i < $rows; $i++) 371 { 372 $sum += abs( $this->[0][$i][$j] ); 373 } 374 $max = $sum if ($sum > $max); 375 } 376 return($max); 377 } 378 379 sub norm_max # maximum of sums of each row 380 { 381 croak "Usage: \$norm_max = \$matrix->norm_max();" 382 if (@_ != 1); 383 384 my($this) = @_; 385 my($rows,$cols) = ($this->[1],$this->[2]); 386 387 my $max = 0.0; 388 for (my $i = 0; $i < $rows; $i++) 389 { 390 my $sum = 0.0; 391 for (my $j = 0; $j < $cols; $j++) 392 { 393 $sum += abs( $this->[0][$i][$j] ); 394 } 395 $max = $sum if ($sum > $max); 396 } 397 return($max); 398 } 399 400 sub negate 401 { 402 croak "Usage: \$matrix1->negate(\$matrix2);" 403 if (@_ != 2); 404 405 my($matrix1,$matrix2) = @_; 406 my($rows1,$cols1) = ($matrix1->[1],$matrix1->[2]); 407 my($rows2,$cols2) = ($matrix2->[1],$matrix2->[2]); 408 409 croak "MatrixReal1::negate(): matrix size mismatch" 410 unless (($rows1 == $rows2) && ($cols1 == $cols2)); 411 412 $matrix1->_undo_LR(); 413 414 for (my $i = 0; $i < $rows1; $i++ ) 415 { 416 for (my $j = 0; $j < $cols1; $j++ ) 417 { 418 $matrix1->[0][$i][$j] = -($matrix2->[0][$i][$j]); 419 } 420 } 421 } 422 423 sub transpose 424 { 425 croak "Usage: \$matrix1->transpose(\$matrix2);" 426 if (@_ != 2); 427 428 my($matrix1,$matrix2) = @_; 429 my($rows1,$cols1) = ($matrix1->[1],$matrix1->[2]); 430 my($rows2,$cols2) = ($matrix2->[1],$matrix2->[2]); 431 432 croak "MatrixReal1::transpose(): matrix size mismatch" 433 unless (($rows1 == $cols2) && ($cols1 == $rows2)); 434 435 $matrix1->_undo_LR(); 436 437 if ($rows1 == $cols1) 438 { 439 # more complicated to make in-place possible! 440 441 for (my $i = 0; $i < $rows1; $i++) 442 { 443 for (my $j = ($i + 1); $j < $cols1; $j++) 444 { 445 my $swap = $matrix2->[0][$i][$j]; 446 $matrix1->[0][$i][$j] = $matrix2->[0][$j][$i]; 447 $matrix1->[0][$j][$i] = $swap; 448 } 449 $matrix1->[0][$i][$i] = $matrix2->[0][$i][$i]; 450 } 451 } 452 else # ($rows1 != $cols1) 453 { 454 for (my $i = 0; $i < $rows1; $i++) 455 { 456 for (my $j = 0; $j < $cols1; $j++) 457 { 458 $matrix1->[0][$i][$j] = $matrix2->[0][$j][$i]; 459 } 460 } 461 } 462 } 463 464 sub add 465 { 466 croak "Usage: \$matrix1->add(\$matrix2,\$matrix3);" 467 if (@_ != 3); 468 469 my($matrix1,$matrix2,$matrix3) = @_; 470 my($rows1,$cols1) = ($matrix1->[1],$matrix1->[2]); 471 my($rows2,$cols2) = ($matrix2->[1],$matrix2->[2]); 472 my($rows3,$cols3) = ($matrix3->[1],$matrix3->[2]); 473 my($i,$j); 474 475 croak "MatrixReal1::add(): matrix size mismatch" 476 unless (($rows1 == $rows2) && ($rows1 == $rows3) && 477 ($cols1 == $cols2) && ($cols1 == $cols3)); 478 479 $matrix1->_undo_LR(); 480 481 for ( $i = 0; $i < $rows1; $i++ ) 482 { 483 for ( $j = 0; $j < $cols1; $j++ ) 484 { 485 $matrix1->[0][$i][$j] = 486 $matrix2->[0][$i][$j] + $matrix3->[0][$i][$j]; 487 } 488 } 489 } 490 491 sub subtract 492 { 493 croak "Usage: \$matrix1->subtract(\$matrix2,\$matrix3);" 494 if (@_ != 3); 495 496 my($matrix1,$matrix2,$matrix3) = @_; 497 my($rows1,$cols1) = ($matrix1->[1],$matrix1->[2]); 498 my($rows2,$cols2) = ($matrix2->[1],$matrix2->[2]); 499 my($rows3,$cols3) = ($matrix3->[1],$matrix3->[2]); 500 my($i,$j); 501 502 croak "MatrixReal1::subtract(): matrix size mismatch" 503 unless (($rows1 == $rows2) && ($rows1 == $rows3) && 504 ($cols1 == $cols2) && ($cols1 == $cols3)); 505 506 $matrix1->_undo_LR(); 507 508 for ( $i = 0; $i < $rows1; $i++ ) 509 { 510 for ( $j = 0; $j < $cols1; $j++ ) 511 { 512 $matrix1->[0][$i][$j] = 513 $matrix2->[0][$i][$j] - $matrix3->[0][$i][$j]; 514 } 515 } 516 } 517 518 sub multiply_scalar 519 { 520 croak "Usage: \$matrix1->multiply_scalar(\$matrix2,\$scalar);" 521 if (@_ != 3); 522 523 my($matrix1,$matrix2,$scalar) = @_; 524 my($rows1,$cols1) = ($matrix1->[1],$matrix1->[2]); 525 my($rows2,$cols2) = ($matrix2->[1],$matrix2->[2]); 526 my($i,$j); 527 528 croak "MatrixReal1::multiply_scalar(): matrix size mismatch" 529 unless (($rows1 == $rows2) && ($cols1 == $cols2)); 530 531 $matrix1->_undo_LR(); 532 533 for ( $i = 0; $i < $rows1; $i++ ) 534 { 535 for ( $j = 0; $j < $cols1; $j++ ) 536 { 537 $matrix1->[0][$i][$j] = $matrix2->[0][$i][$j] * $scalar; 538 } 539 } 540 } 541 542 sub multiply 543 { 544 croak "Usage: \$product_matrix = \$matrix1->multiply(\$matrix2);" 545 if (@_ != 2); 546 547 my($matrix1,$matrix2) = @_; 548 my($rows1,$cols1) = ($matrix1->[1],$matrix1->[2]); 549 my($rows2,$cols2) = ($matrix2->[1],$matrix2->[2]); 550 my($temp); 551 552 croak "MatrixReal1::multiply(): matrix size mismatch" 553 unless ($cols1 == $rows2); 554 555 $temp = $matrix1->new($rows1,$cols2); 556 for (my $i = 0; $i < $rows1; $i++ ) 557 { 558 for (my $j = 0; $j < $cols2; $j++ ) 559 { 560 my $sum = 0.0; 561 for (my $k = 0; $k < $cols1; $k++ ) 562 { 563 $sum += ( $matrix1->[0][$i][$k] * $matrix2->[0][$k][$j] ); 564 } 565 $temp->[0][$i][$j] = $sum; 566 } 567 } 568 return($temp); 569 } 570 571 sub min 572 { 573 croak "Usage: \$minimum = MatrixReal1::min(\$number1,\$number2);" 574 if (@_ != 2); 575 576 return( $_[0] < $_[1] ? $_[0] : $_[1] ); 577 } 578 579 sub max 580 { 581 croak "Usage: \$maximum = MatrixReal1::max(\$number1,\$number2);" 582 if (@_ != 2); 583 584 return( $_[0] > $_[1] ? $_[0] : $_[1] ); 585 } 586 587 sub kleene 588 { 589 croak "Usage: \$minimal_cost_matrix = \$cost_matrix->kleene();" 590 if (@_ != 1); 591 592 my($matrix) = @_; 593 my($rows,$cols) = ($matrix->[1],$matrix->[2]); 594 my($i,$j,$k,$n); 595 my($temp); 596 597 croak "MatrixReal1::kleene(): matrix is not quadratic" 598 unless ($rows == $cols); 599 600 $temp = $matrix->new($rows,$cols); 601 $temp->copy($matrix); 602 $temp->_undo_LR(); 603 $n = $rows; 604 for ( $i = 0; $i < $n; $i++ ) 605 { 606 $temp->[0][$i][$i] = min( $temp->[0][$i][$i] , 0 ); 607 } 608 for ( $k = 0; $k < $n; $k++ ) 609 { 610 for ( $i = 0; $i < $n; $i++ ) 611 { 612 for ( $j = 0; $j < $n; $j++ ) 613 { 614 $temp->[0][$i][$j] = min( $temp->[0][$i][$j] , 615 ( $temp->[0][$i][$k] + 616 $temp->[0][$k][$j] ) ); 617 } 618 } 619 } 620 return($temp); 621 } 622 623 sub normalize 624 { 625 croak "Usage: (\$norm_matrix,\$norm_vector) = \$matrix->normalize(\$vector);" 626 if (@_ != 2); 627 628 my($matrix,$vector) = @_; 629 my($rows,$cols) = ($matrix->[1],$matrix->[2]); 630 my($norm_matrix,$norm_vector); 631 my($max,$val); 632 my($i,$j,$n); 633 634 croak "MatrixReal1::normalize(): matrix is not quadratic" 635 unless ($rows == $cols); 636 637 $n = $rows; 638 639 croak "MatrixReal1::normalize(): vector is not a column vector" 640 unless ($vector->[2] == 1); 641 642 croak "MatrixReal1::normalize(): matrix and vector size mismatch" 643 unless ($vector->[1] == $n); 644 645 $norm_matrix = $matrix->new($n,$n); 646 $norm_vector = $vector->new($n,1); 647 648 $norm_matrix->copy($matrix); 649 $norm_vector->copy($vector); 650 651 $norm_matrix->_undo_LR(); 652 653 for ( $i = 0; $i < $n; $i++ ) 654 { 655 $max = abs($norm_vector->[0][$i][0]); 656 for ( $j = 0; $j < $n; $j++ ) 657 { 658 $val = abs($norm_matrix->[0][$i][$j]); 659 if ($val > $max) { $max = $val; } 660 } 661 if ($max != 0) 662 { 663 $norm_vector->[0][$i][0] /= $max; 664 for ( $j = 0; $j < $n; $j++ ) 665 { 666 $norm_matrix->[0][$i][$j] /= $max; 667 } 668 } 669 } 670 return($norm_matrix,$norm_vector); 671 } 672 673 sub decompose_LR 674 { 675 croak "Usage: \$LR_matrix = \$matrix->decompose_LR();" 676 if (@_ != 1); 677 678 my($matrix) = @_; 679 my($rows,$cols) = ($matrix->[1],$matrix->[2]); 680 my($perm_row,$perm_col); 681 my($row,$col,$max); 682 my($i,$j,$k,$n); 683 my($sign) = 1; 684 my($swap); 685 my($temp); 686 687 croak "MatrixReal1::decompose_LR(): matrix is not quadratic" 688 unless ($rows == $cols); 689 690 $temp = $matrix->new($rows,$cols); 691 $temp->copy($matrix); 692 $n = $rows; 693 $perm_row = [ ]; 694 $perm_col = [ ]; 695 for ( $i = 0; $i < $n; $i++ ) 696 { 697 $perm_row->[$i] = $i; 698 $perm_col->[$i] = $i; 699 } 700 NONZERO: 701 for ( $k = 0; $k < $n; $k++ ) # use Gauss's algorithm: 702 { 703 # complete pivot-search: 704 705 $max = 0; 706 for ( $i = $k; $i < $n; $i++ ) 707 { 708 for ( $j = $k; $j < $n; $j++ ) 709 { 710 if (($swap = abs($temp->[0][$i][$j])) > $max) 711 { 712 $max = $swap; 713 $row = $i; 714 $col = $j; 715 } 716 } 717 } 718 last NONZERO if ($max == 0); # (all remaining elements are zero) 719 if ($k != $row) # swap row $k and row $row: 720 { 721 $sign = -$sign; 722 $swap = $perm_row->[$k]; 723 $perm_row->[$k] = $perm_row->[$row]; 724 $perm_row->[$row] = $swap; 725 for ( $j = 0; $j < $n; $j++ ) 726 { 727 # (must run from 0 since L has to be swapped too!) 728 729 $swap = $temp->[0][$k][$j]; 730 $temp->[0][$k][$j] = $temp->[0][$row][$j]; 731 $temp->[0][$row][$j] = $swap; 732 } 733 } 734 if ($k != $col) # swap column $k and column $col: 735 { 736 $sign = -$sign; 737 $swap = $perm_col->[$k]; 738 $perm_col->[$k] = $perm_col->[$col]; 739 $perm_col->[$col] = $swap; 740 for ( $i = 0; $i < $n; $i++ ) 741 { 742 $swap = $temp->[0][$i][$k]; 743 $temp->[0][$i][$k] = $temp->[0][$i][$col]; 744 $temp->[0][$i][$col] = $swap; 745 } 746 } 747 for ( $i = ($k + 1); $i < $n; $i++ ) 748 { 749 # scan the remaining rows, add multiples of row $k to row $i: 750 751 $swap = $temp->[0][$i][$k] / $temp->[0][$k][$k]; 752 if ($swap != 0) 753 { 754 # calculate a row of matrix R: 755 756 for ( $j = ($k + 1); $j < $n; $j++ ) 757 { 758 $temp->[0][$i][$j] -= $temp->[0][$k][$j] * $swap; 759 } 760 761 # store matrix L in same matrix as R: 762 763 $temp->[0][$i][$k] = $swap; 764 } 765 } 766 } 767 my $rh_options = $temp->[3]; 768 $temp->[3] = $sign; 769 $temp->[4] = $perm_row; 770 $temp->[5] = $perm_col; 771 $temp->[6] = $temp->[3]; 772 return($temp); 773 } 774 775 sub solve_LR 776 { 777 croak "Usage: (\$dimension,\$x_vector,\$base_matrix) = \$LR_matrix->solve_LR(\$b_vector);" 778 if (@_ != 2); 779 780 my($LR_matrix,$b_vector) = @_; 781 my($rows,$cols) = ($LR_matrix->[1],$LR_matrix->[2]); 782 my($dimension,$x_vector,$base_matrix); 783 my($perm_row,$perm_col); 784 my($y_vector,$sum); 785 my($i,$j,$k,$n); 786 787 croak "MatrixReal1::solve_LR(): not an LR decomposition matrix" 788 unless ((defined $LR_matrix->[4]) && ($rows == $cols)); 789 790 $n = $rows; 791 792 croak "MatrixReal1::solve_LR(): vector is not a column vector" 793 unless ($b_vector->[2] == 1); 794 795 croak "MatrixReal1::solve_LR(): matrix and vector size mismatch" 796 unless ($b_vector->[1] == $n); 797 798 $perm_row = $LR_matrix->[4]; 799 $perm_col = $LR_matrix->[5]; 800 801 $x_vector = $b_vector->new($n,1); 802 $y_vector = $b_vector->new($n,1); 803 $base_matrix = $LR_matrix->new($n,$n); 804 805 # calculate "x" so that LRx = b ==> calculate Ly = b, Rx = y: 806 807 for ( $i = 0; $i < $n; $i++ ) # calculate $y_vector: 808 { 809 $sum = $b_vector->[0][($perm_row->[$i])][0]; 810 for ( $j = 0; $j < $i; $j++ ) 811 { 812 $sum -= $LR_matrix->[0][$i][$j] * $y_vector->[0][$j][0]; 813 } 814 $y_vector->[0][$i][0] = $sum; 815 } 816 817 $dimension = 0; 818 for ( $i = ($n - 1); $i >= 0; $i-- ) # calculate $x_vector: 819 { 820 if ($LR_matrix->[0][$i][$i] == 0) 821 { 822 if ($y_vector->[0][$i][0] != 0) 823 { 824 return(); # a solution does not exist! 825 } 826 else 827 { 828 $dimension++; 829 $x_vector->[0][($perm_col->[$i])][0] = 0; 830 } 831 } 832 else 833 { 834 $sum = $y_vector->[0][$i][0]; 835 for ( $j = ($i + 1); $j < $n; $j++ ) 836 { 837 $sum -= $LR_matrix->[0][$i][$j] * 838 $x_vector->[0][($perm_col->[$j])][0]; 839 } 840 $x_vector->[0][($perm_col->[$i])][0] = 841 $sum / $LR_matrix->[0][$i][$i]; 842 } 843 } 844 if ($dimension) 845 { 846 if ($dimension == $n) 847 { 848 $base_matrix->one(); 849 } 850 else 851 { 852 for ( $k = 0; $k < $dimension; $k++ ) 853 { 854 $base_matrix->[0][($perm_col->[($n-$k-1)])][$k] = 1; 855 for ( $i = ($n-$dimension-1); $i >= 0; $i-- ) 856 { 857 $sum = 0; 858 for ( $j = ($i + 1); $j < $n; $j++ ) 859 { 860 $sum -= $LR_matrix->[0][$i][$j] * 861 $base_matrix->[0][($perm_col->[$j])][$k]; 862 } 863 $base_matrix->[0][($perm_col->[$i])][$k] = 864 $sum / $LR_matrix->[0][$i][$i]; 865 } 866 } 867 } 868 } 869 return( $dimension, $x_vector, $base_matrix ); 870 } 871 872 sub invert_LR 873 { 874 croak "Usage: \$inverse_matrix = \$LR_matrix->invert_LR();" 875 if (@_ != 1); 876 877 my($matrix) = @_; 878 my($rows,$cols) = ($matrix->[1],$matrix->[2]); 879 my($inv_matrix,$x_vector,$y_vector); 880 my($i,$j,$n); 881 882 croak "MatrixReal1::invert_LR(): not an LR decomposition matrix" 883 unless ((defined $matrix->[4]) && ($rows == $cols)); 884 885 $n = $rows; 886 if ($matrix->[0][$n-1][$n-1] != 0) 887 { 888 $inv_matrix = $matrix->new($n,$n); 889 $y_vector = $matrix->new($n,1); 890 for ( $j = 0; $j < $n; $j++ ) 891 { 892 if ($j > 0) 893 { 894 $y_vector->[0][$j-1][0] = 0; 895 } 896 $y_vector->[0][$j][0] = 1; 897 if (($rows,$x_vector,$cols) = $matrix->solve_LR($y_vector)) 898 { 899 for ( $i = 0; $i < $n; $i++ ) 900 { 901 $inv_matrix->[0][$i][$j] = $x_vector->[0][$i][0]; 902 } 903 } 904 else 905 { 906 die "MatrixReal1::invert_LR(): unexpected error - please inform author!\n"; 907 } 908 } 909 return($inv_matrix); 910 } 911 else { return(); } # matrix is not invertible! 912 } 913 914 sub condition 915 { 916 # 1st matrix MUST be the inverse of 2nd matrix (or vice-versa) 917 # for a meaningful result! 918 919 croak "Usage: \$condition = \$matrix->condition(\$inverse_matrix);" 920 if (@_ != 2); 921 922 my($matrix1,$matrix2) = @_; 923 my($rows1,$cols1) = ($matrix1->[1],$matrix1->[2]); 924 my($rows2,$cols2) = ($matrix2->[1],$matrix2->[2]); 925 926 croak "MatrixReal1::condition(): 1st matrix is not quadratic" 927 unless ($rows1 == $cols1); 928 929 croak "MatrixReal1::condition(): 2nd matrix is not quadratic" 930 unless ($rows2 == $cols2); 931 932 croak "MatrixReal1::condition(): matrix size mismatch" 933 unless (($rows1 == $rows2) && ($cols1 == $cols2)); 934 935 return( $matrix1->norm_one() * $matrix2->norm_one() ); 936 } 937 938 sub det_LR # determinant of LR decomposition matrix 939 { 940 croak "Usage: \$determinant = \$LR_matrix->det_LR();" 941 if (@_ != 1); 942 943 my($matrix) = @_; 944 my($rows,$cols) = ($matrix->[1],$matrix->[2]); 945 my($k,$det); 946 947 croak "MatrixReal1::det_LR(): not an LR decomposition matrix" 948 # unless ((defined $matrix->[3]) && ($rows == $cols)); 949 unless ((defined $matrix->[4]) && ($rows == $cols)); #options might be in [3] position-- MEG 950 951 $det = $matrix->[3]; # grab the sign from permutation shifts 952 for ( $k = 0; $k < $rows; $k++ ) 953 { 954 $det *= $matrix->[0][$k][$k]; 955 } 956 return($det); 957 } 958 959 sub order_LR # order of LR decomposition matrix (number of non-zero equations) 960 { 961 croak "Usage: \$order = \$LR_matrix->order_LR();" 962 if (@_ != 1); 963 964 my($matrix) = @_; 965 my($rows,$cols) = ($matrix->[1],$matrix->[2]); 966 my($order); 967 968 croak "MatrixReal1::order_LR(): not an LR decomposition matrix" 969 unless ((defined $matrix->[4]) && ($rows == $cols)); 970 971 ZERO: 972 for ( $order = ($rows - 1); $order >= 0; $order-- ) 973 { 974 last ZERO if ($matrix->[0][$order][$order] != 0); 975 } 976 return(++$order); 977 } 978 979 sub scalar_product 980 { 981 croak "Usage: \$scalar_product = \$vector1->scalar_product(\$vector2);" 982 if (@_ != 2); 983 984 my($vector1,$vector2) = @_; 985 my($rows1,$cols1) = ($vector1->[1],$vector1->[2]); 986 my($rows2,$cols2) = ($vector2->[1],$vector2->[2]); 987 my($k,$sum); 988 989 croak "MatrixReal1::scalar_product(): 1st vector is not a column vector" 990 unless ($cols1 == 1); 991 992 croak "MatrixReal1::scalar_product(): 2nd vector is not a column vector" 993 unless ($cols2 == 1); 994 995 croak "MatrixReal1::scalar_product(): vector size mismatch" 996 unless ($rows1 == $rows2); 997 998 $sum = 0; 999 for ( $k = 0; $k < $rows1; $k++ ) 1000 { 1001 $sum += $vector1->[0][$k][0] * $vector2->[0][$k][0]; 1002 } 1003 return($sum); 1004 } 1005 1006 sub vector_product 1007 { 1008 croak "Usage: \$vector_product = \$vector1->vector_product(\$vector2);" 1009 if (@_ != 2); 1010 1011 my($vector1,$vector2) = @_; 1012 my($rows1,$cols1) = ($vector1->[1],$vector1->[2]); 1013 my($rows2,$cols2) = ($vector2->[1],$vector2->[2]); 1014 my($temp); 1015 my($n); 1016 1017 croak "MatrixReal1::vector_product(): 1st vector is not a column vector" 1018 unless ($cols1 == 1); 1019 1020 croak "MatrixReal1::vector_product(): 2nd vector is not a column vector" 1021 unless ($cols2 == 1); 1022 1023 croak "MatrixReal1::vector_product(): vector size mismatch" 1024 unless ($rows1 == $rows2); 1025 1026 $n = $rows1; 1027 1028 croak "MatrixReal1::vector_product(): only defined for 3 dimensions" 1029 unless ($n == 3); 1030 1031 $temp = $vector1->new($n,1); 1032 $temp->[0][0][0] = $vector1->[0][1][0] * $vector2->[0][2][0] - 1033 $vector1->[0][2][0] * $vector2->[0][1][0]; 1034 $temp->[0][1][0] = $vector1->[0][2][0] * $vector2->[0][0][0] - 1035 $vector1->[0][0][0] * $vector2->[0][2][0]; 1036 $temp->[0][2][0] = $vector1->[0][0][0] * $vector2->[0][1][0] - 1037 $vector1->[0][1][0] * $vector2->[0][0][0]; 1038 return($temp); 1039 } 1040 1041 sub length 1042 { 1043 croak "Usage: \$length = \$vector->length();" 1044 if (@_ != 1); 1045 1046 my($vector) = @_; 1047 my($rows,$cols) = ($vector->[1],$vector->[2]); 1048 my($k,$comp,$sum); 1049 1050 croak "MatrixReal1::length(): vector is not a column vector" 1051 unless ($cols == 1); 1052 1053 $sum = 0; 1054 for ( $k = 0; $k < $rows; $k++ ) 1055 { 1056 $comp = $vector->[0][$k][0]; 1057 $sum += $comp * $comp; 1058 } 1059 return( sqrt( $sum ) ); 1060 } 1061 1062 sub _init_iteration 1063 { 1064 croak "Usage: \$which_norm = \$matrix->_init_iteration();" 1065 if (@_ != 1); 1066 1067 my($matrix) = @_; 1068 my($rows,$cols) = ($matrix->[1],$matrix->[2]); 1069 my($ok,$max,$sum,$norm); 1070 my($i,$j,$n); 1071 1072 croak "MatrixReal1::_init_iteration(): matrix is not quadratic" 1073 unless ($rows == $cols); 1074 1075 $ok = 1; 1076 $n = $rows; 1077 for ( $i = 0; $i < $n; $i++ ) 1078 { 1079 if ($matrix->[0][$i][$i] == 0) { $ok = 0; } 1080 } 1081 if ($ok) 1082 { 1083 $norm = 1; # norm_one 1084 $max = 0; 1085 for ( $j = 0; $j < $n; $j++ ) 1086 { 1087 $sum = 0; 1088 for ( $i = 0; $i < $j; $i++ ) 1089 { 1090 $sum += abs($matrix->[0][$i][$j]); 1091 } 1092 for ( $i = ($j + 1); $i < $n; $i++ ) 1093 { 1094 $sum += abs($matrix->[0][$i][$j]); 1095 } 1096 $sum /= abs($matrix->[0][$j][$j]); 1097 if ($sum > $max) { $max = $sum; } 1098 } 1099 $ok = ($max < 1); 1100 unless ($ok) 1101 { 1102 $norm = -1; # norm_max 1103 $max = 0; 1104 for ( $i = 0; $i < $n; $i++ ) 1105 { 1106 $sum = 0; 1107 for ( $j = 0; $j < $i; $j++ ) 1108 { 1109 $sum += abs($matrix->[0][$i][$j]); 1110 } 1111 for ( $j = ($i + 1); $j < $n; $j++ ) 1112 { 1113 $sum += abs($matrix->[0][$i][$j]); 1114 } 1115 $sum /= abs($matrix->[0][$i][$i]); 1116 if ($sum > $max) { $max = $sum; } 1117 } 1118 $ok = ($max < 1) 1119 } 1120 } 1121 if ($ok) { return($norm); } 1122 else { return(0); } 1123 } 1124 1125 sub solve_GSM # Global Step Method 1126 { 1127 croak "Usage: \$xn_vector = \$matrix->solve_GSM(\$x0_vector,\$b_vector,\$epsilon);" 1128 if (@_ != 4); 1129 1130 my($matrix,$x0_vector,$b_vector,$epsilon) = @_; 1131 my($rows1,$cols1) = ( $matrix->[1], $matrix->[2]); 1132 my($rows2,$cols2) = ($x0_vector->[1],$x0_vector->[2]); 1133 my($rows3,$cols3) = ( $b_vector->[1], $b_vector->[2]); 1134 my($norm,$sum,$diff); 1135 my($xn_vector); 1136 my($i,$j,$n); 1137 1138 croak "MatrixReal1::solve_GSM(): matrix is not quadratic" 1139 unless ($rows1 == $cols1); 1140 1141 $n = $rows1; 1142 1143 croak "MatrixReal1::solve_GSM(): 1st vector is not a column vector" 1144 unless ($cols2 == 1); 1145 1146 croak "MatrixReal1::solve_GSM(): 2nd vector is not a column vector" 1147 unless ($cols3 == 1); 1148 1149 croak "MatrixReal1::solve_GSM(): matrix and vector size mismatch" 1150 unless (($rows2 == $n) && ($rows3 == $n)); 1151 1152 return() unless ($norm = $matrix->_init_iteration()); 1153 1154 $xn_vector = $x0_vector->new($n,1); 1155 1156 $diff = $epsilon + 1; 1157 while ($diff >= $epsilon) 1158 { 1159 for ( $i = 0; $i < $n; $i++ ) 1160 { 1161 $sum = $b_vector->[0][$i][0]; 1162 for ( $j = 0; $j < $i; $j++ ) 1163 { 1164 $sum -= $matrix->[0][$i][$j] * $x0_vector->[0][$j][0]; 1165 } 1166 for ( $j = ($i + 1); $j < $n; $j++ ) 1167 { 1168 $sum -= $matrix->[0][$i][$j] * $x0_vector->[0][$j][0]; 1169 } 1170 $xn_vector->[0][$i][0] = $sum / $matrix->[0][$i][$i]; 1171 } 1172 $x0_vector->subtract($x0_vector,$xn_vector); 1173 if ($norm > 0) { $diff = $x0_vector->norm_one(); } 1174 else { $diff = $x0_vector->norm_max(); } 1175 for ( $i = 0; $i < $n; $i++ ) 1176 { 1177 $x0_vector->[0][$i][0] = $xn_vector->[0][$i][0]; 1178 } 1179 } 1180 return($xn_vector); 1181 } 1182 1183 sub solve_SSM # Single Step Method 1184 { 1185 croak "Usage: \$xn_vector = \$matrix->solve_SSM(\$x0_vector,\$b_vector,\$epsilon);" 1186 if (@_ != 4); 1187 1188 my($matrix,$x0_vector,$b_vector,$epsilon) = @_; 1189 my($rows1,$cols1) = ( $matrix->[1], $matrix->[2]); 1190 my($rows2,$cols2) = ($x0_vector->[1],$x0_vector->[2]); 1191 my($rows3,$cols3) = ( $b_vector->[1], $b_vector->[2]); 1192 my($norm,$sum,$diff); 1193 my($xn_vector); 1194 my($i,$j,$n); 1195 1196 croak "MatrixReal1::solve_SSM(): matrix is not quadratic" 1197 unless ($rows1 == $cols1); 1198 1199 $n = $rows1; 1200 1201 croak "MatrixReal1::solve_SSM(): 1st vector is not a column vector" 1202 unless ($cols2 == 1); 1203 1204 croak "MatrixReal1::solve_SSM(): 2nd vector is not a column vector" 1205 unless ($cols3 == 1); 1206 1207 croak "MatrixReal1::solve_SSM(): matrix and vector size mismatch" 1208 unless (($rows2 == $n) && ($rows3 == $n)); 1209 1210 return() unless ($norm = $matrix->_init_iteration()); 1211 1212 $xn_vector = $x0_vector->new($n,1); 1213 $xn_vector->copy($x0_vector); 1214 1215 $diff = $epsilon + 1; 1216 while ($diff >= $epsilon) 1217 { 1218 for ( $i = 0; $i < $n; $i++ ) 1219 { 1220 $sum = $b_vector->[0][$i][0]; 1221 for ( $j = 0; $j < $i; $j++ ) 1222 { 1223 $sum -= $matrix->[0][$i][$j] * $xn_vector->[0][$j][0]; 1224 } 1225 for ( $j = ($i + 1); $j < $n; $j++ ) 1226 { 1227 $sum -= $matrix->[0][$i][$j] * $xn_vector->[0][$j][0]; 1228 } 1229 $xn_vector->[0][$i][0] = $sum / $matrix->[0][$i][$i]; 1230 } 1231 $x0_vector->subtract($x0_vector,$xn_vector); 1232 if ($norm > 0) { $diff = $x0_vector->norm_one(); } 1233 else { $diff = $x0_vector->norm_max(); } 1234 for ( $i = 0; $i < $n; $i++ ) 1235 { 1236 $x0_vector->[0][$i][0] = $xn_vector->[0][$i][0]; 1237 } 1238 } 1239 return($xn_vector); 1240 } 1241 1242 sub solve_RM # Relaxation Method 1243 { 1244 croak "Usage: \$xn_vector = \$matrix->solve_RM(\$x0_vector,\$b_vector,\$weight,\$epsilon);" 1245 if (@_ != 5); 1246 1247 my($matrix,$x0_vector,$b_vector,$weight,$epsilon) = @_; 1248 my($rows1,$cols1) = ( $matrix->[1], $matrix->[2]); 1249 my($rows2,$cols2) = ($x0_vector->[1],$x0_vector->[2]); 1250 my($rows3,$cols3) = ( $b_vector->[1], $b_vector->[2]); 1251 my($norm,$sum,$diff); 1252 my($xn_vector); 1253 my($i,$j,$n); 1254 1255 croak "MatrixReal1::solve_RM(): matrix is not quadratic" 1256 unless ($rows1 == $cols1); 1257 1258 $n = $rows1; 1259 1260 croak "MatrixReal1::solve_RM(): 1st vector is not a column vector" 1261 unless ($cols2 == 1); 1262 1263 croak "MatrixReal1::solve_RM(): 2nd vector is not a column vector" 1264 unless ($cols3 == 1); 1265 1266 croak "MatrixReal1::solve_RM(): matrix and vector size mismatch" 1267 unless (($rows2 == $n) && ($rows3 == $n)); 1268 1269 return() unless ($norm = $matrix->_init_iteration()); 1270 1271 $xn_vector = $x0_vector->new($n,1); 1272 $xn_vector->copy($x0_vector); 1273 1274 $diff = $epsilon + 1; 1275 while ($diff >= $epsilon) 1276 { 1277 for ( $i = 0; $i < $n; $i++ ) 1278 { 1279 $sum = $b_vector->[0][$i][0]; 1280 for ( $j = 0; $j < $i; $j++ ) 1281 { 1282 $sum -= $matrix->[0][$i][$j] * $xn_vector->[0][$j][0]; 1283 } 1284 for ( $j = ($i + 1); $j < $n; $j++ ) 1285 { 1286 $sum -= $matrix->[0][$i][$j] * $xn_vector->[0][$j][0]; 1287 } 1288 $xn_vector->[0][$i][0] = $weight * ( $sum / $matrix->[0][$i][$i] ) 1289 + (1 - $weight) * $xn_vector->[0][$i][0]; 1290 } 1291 $x0_vector->subtract($x0_vector,$xn_vector); 1292 if ($norm > 0) { $diff = $x0_vector->norm_one(); } 1293 else { $diff = $x0_vector->norm_max(); } 1294 for ( $i = 0; $i < $n; $i++ ) 1295 { 1296 $x0_vector->[0][$i][0] = $xn_vector->[0][$i][0]; 1297 } 1298 } 1299 return($xn_vector); 1300 } 1301 1302 # Core householder reduction routine (when eagenvector 1303 # are wanted). 1304 # Adapted from: Numerical Recipes, 2nd edition. 1305 sub _householder_vectors ($) 1306 { 1307 my ($Q) = @_; 1308 my ($rows, $cols) = ($Q->[1], $Q->[2]); 1309 1310 # Creates tridiagonal 1311 # Set up tridiagonal needed elements 1312 my $d = []; # N Diagonal elements 0...n-1 1313 my $e = []; # N-1 Off-Diagonal elements 0...n-2 1314 1315 my @p = (); 1316 for (my $i = ($rows-1); $i > 1; $i--) 1317 { 1318 my $scale = 0.0; 1319 # Computes norm of one column (below diagonal) 1320 for (my $k = 0; $k < $i; $k++) 1321 { 1322 $scale += abs($Q->[0][$i][$k]); 1323 } 1324 if ($scale == 0.0) 1325 { # skip the transformation 1326 $e->[$i-1] = $Q->[0][$i][$i-1]; 1327 } 1328 else 1329 { 1330 my $h = 0.0; 1331 for (my $k = 0; $k < $i; $k++) 1332 { # Used scaled Q for transformation 1333 $Q->[0][$i][$k] /= $scale; 1334 # Form sigma in h 1335 $h += $Q->[0][$i][$k] * $Q->[0][$i][$k]; 1336 } 1337 my $t1 = $Q->[0][$i][$i-1]; 1338 my $t2 = (($t1 >= 0.0) ? -sqrt($h) : sqrt($h)); 1339 $e->[$i-1] = $scale * $t2; # Update off-diagonals 1340 $h -= $t1 * $t2; 1341 $Q->[0][$i][$i-1] -= $t2; 1342 my $f = 0.0; 1343 for (my $j = 0; $j < $i; $j++) 1344 { 1345 $Q->[0][$j][$i] = $Q->[0][$i][$j] / $h; 1346 my $g = 0.0; 1347 for (my $k = 0; $k <= $j; $k++) 1348 { 1349 $g += $Q->[0][$j][$k] * $Q->[0][$i][$k]; 1350 } 1351 for (my $k = $j+1; $k < $i; $k++) 1352 { 1353 $g += $Q->[0][$k][$j] * $Q->[0][$i][$k]; 1354 } 1355 # Form elements of P 1356 $p[$j] = $g / $h; 1357 $f += $p[$j] * $Q->[0][$i][$j]; 1358 } 1359 my $hh = $f / ($h + $h); 1360 for (my $j = 0; $j < $i; $j++) 1361 { 1362 my $t3 = $Q->[0][$i][$j]; 1363 my $t4 = $p[$j] - $hh * $t3; 1364 $p[$j] = $t4; 1365 for (my $k = 0; $k <= $j; $k++) 1366 { 1367 $Q->[0][$j][$k] -= $t3 * $p[$k] 1368 + $t4 * $Q->[0][$i][$k]; 1369 } 1370 } 1371 } 1372 } 1373 # Updates for i == 0,1 1374 $e->[0] = $Q->[0][1][0]; 1375 $d->[0] = $Q->[0][0][0]; # i==0 1376 $Q->[0][0][0] = 1.0; 1377 $d->[1] = $Q->[0][1][1]; # i==1 1378 $Q->[0][1][1] = 1.0; 1379 $Q->[0][1][0] = $Q->[0][0][1] = 0.0; 1380 for (my $i = 2; $i < $rows; $i++) 1381 { 1382 for (my $j = 0; $j < $i; $j++) 1383 { 1384 my $g = 0.0; 1385 for (my $k = 0; $k < $i; $k++) 1386 { 1387 $g += $Q->[0][$i][$k] * $Q->[0][$k][$j]; 1388 } 1389 for (my $k = 0; $k < $i; $k++) 1390 { 1391 $Q->[0][$k][$j] -= $g * $Q->[0][$k][$i]; 1392 } 1393 } 1394 $d->[$i] = $Q->[0][$i][$i]; 1395 # Reset row and column of Q for next iteration 1396 $Q->[0][$i][$i] = 1.0; 1397 for (my $j = 0; $j < $i; $j++) 1398 { 1399 $Q->[0][$i][$j] = $Q->[0][$j][$i] = 0.0; 1400 } 1401 } 1402 return ($d, $e); 1403 } 1404 1405 # Computes sqrt(a*a + b*b), but more carefully... 1406 sub _pythag ($$) 1407 { 1408 my ($a, $b) = @_; 1409 my $aa = abs($a); 1410 my $ab = abs($b); 1411 if ($aa > $ab) 1412 { 1413 # NB: Not needed!: return 0.0 if ($aa == 0.0); 1414 my $t = $ab / $aa; 1415 return ($aa * sqrt(1.0 + $t*$t)); 1416 } 1417 else 1418 { 1419 return 0.0 if ($ab == 0.0); 1420 my $t = $aa / $ab; 1421 return ($ab * sqrt(1.0 + $t*$t)); 1422 } 1423 } 1424 1425 # QL algorithm with implicit shifts to determine the eigenvalues 1426 # of a tridiagonal matrix. Internal routine. 1427 sub _tridiagonal_QLimplicit 1428 { 1429 my ($EV, $d, $e) = @_; 1430 my ($rows, $cols) = ($EV->[1], $EV->[2]); 1431 1432 $e->[$rows-1] = 0.0; 1433 # Start real computation 1434 for (my $l = 0; $l < $rows; $l++) 1435 { 1436 my $iter = 0; 1437 my $m; 1438 OUTER: 1439 do { 1440 for ($m = $l; $m < ($rows - 1); $m++) 1441 { 1442 my $dd = abs($d->[$m]) + abs($d->[$m+1]); 1443 last if ((abs($e->[$m]) + $dd) == $dd); 1444 } 1445 if ($m != $l) 1446 { 1447 croak("Too many iterations!") if ($iter++ >= 30); 1448 my $g = ($d->[$l+1] - $d->[$l]) 1449 / (2.0 * $e->[$l]); 1450 my $r = _pythag($g, 1.0); 1451 $g = $d->[$m] - $d->[$l] 1452 + $e->[$l] / ($g + (($g >= 0.0) ? abs($r) : -abs($r))); 1453 my ($p,$s,$c) = (0.0, 1.0,1.0); 1454 for (my $i = ($m-1); $i >= $l; $i--) 1455 { 1456 my $ii = $i + 1; 1457 my $f = $s * $e->[$i]; 1458 my $t = _pythag($f, $g); 1459 $e->[$ii] = $t; 1460 if ($t == 0.0) 1461 { 1462 $d->[$ii] -= $p; 1463 $e->[$m] = 0.0; 1464 next OUTER; 1465 } 1466 my $b = $c * $e->[$i]; 1467 $s = $f / $t; 1468 $c = $g / $t; 1469 $g = $d->[$ii] - $p; 1470 my $t2 = ($d->[$i] - $g) * $s + 2.0 * $c * $b; 1471 $p = $s * $t2; 1472 $d->[$ii] = $g + $p; 1473 $g = $c * $t2 - $b; 1474 for (my $k = 0; $k < $rows; $k++) 1475 { 1476 my $t1 = $EV->[0][$k][$ii]; 1477 my $t2 = $EV->[0][$k][$i]; 1478 $EV->[0][$k][$ii] = $s * $t2 + $c * $t1; 1479 $EV->[0][$k][$i] = $c * $t2 - $s * $t1; 1480 } 1481 } 1482 $d->[$l] -= $p; 1483 $e->[$l] = $g; 1484 $e->[$m] = 0.0; 1485 } 1486 } while ($m != $l); 1487 } 1488 return; 1489 } 1490 1491 # Core householder reduction routine (when eagenvector 1492 # are NOT wanted). 1493 sub _householder_values ($) 1494 { 1495 my ($Q) = @_; # NB: Q is destroyed on output... 1496 my ($rows, $cols) = ($Q->[1], $Q->[2]); 1497 1498 # Creates tridiagonal 1499 # Set up tridiagonal needed elements 1500 my $d = []; # N Diagonal elements 0...n-1 1501 my $e = []; # N-1 Off-Diagonal elements 0...n-2 1502 1503 my @p = (); 1504 for (my $i = ($rows - 1); $i > 1; $i--) 1505 { 1506 my $scale = 0.0; 1507 for (my $k = 0; $k < $i; $k++) 1508 { 1509 $scale += abs($Q->[0][$i][$k]); 1510 } 1511 if ($scale == 0.0) 1512 { # skip the transformation 1513 $e->[$i-1] = $Q->[0][$i][$i-1]; 1514 } 1515 else 1516 { 1517 my $h = 0.0; 1518 for (my $k = 0; $k < $i; $k++) 1519 { # Used scaled Q for transformation 1520 $Q->[0][$i][$k] /= $scale; 1521 # Form sigma in h 1522 $h += $Q->[0][$i][$k] * $Q->[0][$i][$k]; 1523 } 1524 my $t = $Q->[0][$i][$i-1]; 1525 my $t2 = (($t >= 0.0) ? -sqrt($h) : sqrt($h)); 1526 $e->[$i-1] = $scale * $t2; # Updates off-diagonal 1527 $h -= $t * $t2; 1528 $Q->[0][$i][$i-1] -= $t2; 1529 my $f = 0.0; 1530 for (my $j = 0; $j < $i; $j++) 1531 { 1532 my $g = 0.0; 1533 for (my $k = 0; $k <= $j; $k++) 1534 { 1535 $g += $Q->[0][$j][$k] * $Q->[0][$i][$k]; 1536 } 1537 for (my $k = $j+1; $k < $i; $k++) 1538 { 1539 $g += $Q->[0][$k][$j] * $Q->[0][$i][$k]; 1540 } 1541 # Form elements of P 1542 $p[$j] = $g / $h; 1543 $f += $p[$j] * $Q->[0][$i][$j]; 1544 } 1545 my $hh = $f / ($h + $h); 1546 for (my $j = 0; $j < $i; $j++) 1547 { 1548 my $t = $Q->[0][$i][$j]; 1549 my $g = $p[$j] - $hh * $t; 1550 $p[$j] = $g; 1551 for (my $k = 0; $k <= $j; $k++) 1552 { 1553 $Q->[0][$j][$k] -= $t * $p[$k] 1554 + $g * $Q->[0][$i][$k]; 1555 } 1556 } 1557 } 1558 } 1559 # Updates for i==1 1560 $e->[0] = $Q->[0][1][0]; 1561 # Updates diagonal elements 1562 for (my $i = 0; $i < $rows; $i++) 1563 { 1564 $d->[$i] = $Q->[0][$i][$i]; 1565 } 1566 return ($d, $e); 1567 } 1568 1569 # QL algorithm with implicit shifts to determine the 1570 # eigenvalues ONLY. This is O(N^2) only... 1571 sub _tridiagonal_QLimplicit_values 1572 { 1573 my ($M, $d, $e) = @_; # NB: M is not touched... 1574 my ($rows, $cols) = ($M->[1], $M->[2]); 1575 1576 $e->[$rows-1] = 0.0; 1577 # Start real computation 1578 for (my $l = 0; $l < $rows; $l++) 1579 { 1580 my $iter = 0; 1581 my $m; 1582 OUTER: 1583 do { 1584 for ($m = $l; $m < ($rows - 1); $m++) 1585 { 1586 my $dd = abs($d->[$m]) + abs($d->[$m+1]); 1587 last if ((abs($e->[$m]) + $dd) == $dd); 1588 } 1589 if ($m != $l) 1590 { 1591 croak("Too many iterations!") if ($iter++ >= 30); 1592 my $g = ($d->[$l+1] - $d->[$l]) 1593 / (2.0 * $e->[$l]); 1594 my $r = _pythag($g, 1.0); 1595 $g = $d->[$m] - $d->[$l] 1596 + $e->[$l] / ($g + (($g >= 0.0) ? abs($r) : -abs($r))); 1597 my ($p,$s,$c) = (0.0, 1.0,1.0); 1598 for (my $i = ($m-1); $i >= $l; $i--) 1599 { 1600 my $ii = $i + 1; 1601 my $f = $s * $e->[$i]; 1602 my $t = _pythag($f, $g); 1603 $e->[$ii] = $t; 1604 if ($t == 0.0) 1605 { 1606 $d->[$ii] -= $p; 1607 $e->[$m] = 0.0; 1608 next OUTER; 1609 } 1610 my $b = $c * $e->[$i]; 1611 $s = $f / $t; 1612 $c = $g / $t; 1613 $g = $d->[$ii] - $p; 1614 my $t2 = ($d->[$i] - $g) * $s + 2.0 * $c * $b; 1615 $p = $s * $t2; 1616 $d->[$ii] = $g + $p; 1617 $g = $c * $t2 - $b; 1618 } 1619 $d->[$l] -= $p; 1620 $e->[$l] = $g; 1621 $e->[$m] = 0.0; 1622 } 1623 } while ($m != $l); 1624 } 1625 return; 1626 } 1627 1628 # Householder reduction of a real, symmetric matrix A. 1629 # Returns a tridiagonal matrix T and the orthogonal matrix 1630 # Q effecting the transformation between A and T. 1631 sub householder ($) 1632 { 1633 my ($A) = @_; 1634 my ($rows, $cols) = ($A->[1], $A->[2]); 1635 1636 croak "Matrix is not quadratic" 1637 unless ($rows = $cols); 1638 croak "Matrix is not symmetric" 1639 unless ($A->is_symmetric()); 1640 1641 # Copy given matrix TODO: study if we should do in-place modification 1642 my $Q = $A->clone(); 1643 1644 # Do the computation of tridiagonal elements and of 1645 # transformation matrix 1646 my ($diag, $offdiag) = $Q->_householder_vectors(); 1647 1648 # Creates the tridiagonal matrix 1649 my $T = $A->shadow(); 1650 for (my $i = 0; $i < $rows; $i++) 1651 { # Set diagonal 1652 $T->[0][$i][$i] = $diag->[$i]; 1653 } 1654 for (my $i = 0; $i < ($rows-1); $i++) 1655 { # Set off diagonals 1656 $T->[0][$i+1][$i] = $offdiag->[$i]; 1657 $T->[0][$i][$i+1] = $offdiag->[$i]; 1658 } 1659 return ($T, $Q); 1660 } 1661 1662 # QL algorithm with implicit shifts to determine the eigenvalues 1663 # and eigenvectors of a real tridiagonal matrix - or of a matrix 1664 # previously reduced to tridiagonal form. 1665 sub tri_diagonalize ($;$) 1666 { 1667 my ($T,$Q) = @_; # Q may be 0 if the original matrix is really tridiagonal 1668 1669 my ($rows, $cols) = ($T->[1], $T->[2]); 1670 1671 croak "Matrix is not quadratic" 1672 unless ($rows = $cols); 1673 croak "Matrix is not tridiagonal" 1674 unless ($T->is_symmetric()); # TODO: Do real tridiag check (not symmetric)! 1675 1676 my $EV; 1677 # Obtain/Creates the todo eigenvectors matrix 1678 if ($Q) 1679 { 1680 $EV = $Q->clone(); 1681 } 1682 else 1683 { 1684 $EV = $T->shadow(); 1685 $EV->one(); 1686 } 1687 # Allocates diagonal vector 1688 my $diag = [ ]; 1689 # Initializes it with T 1690 for (my $i = 0; $i < $rows; $i++) 1691 { 1692 $diag->[$i] = $T->[0][$i][$i]; 1693 } 1694 # Allocate temporary vector for off-diagonal elements 1695 my $offdiag = [ ]; 1696 for (my $i = 1; $i < $rows; $i++) 1697 { 1698 $offdiag->[$i-1] = $T->[0][$i][$i-1]; 1699 } 1700 1701 # Calls the calculus routine 1702 $EV->_tridiagonal_QLimplicit($diag, $offdiag); 1703 1704 # Allocate eigenvalues vector 1705 my $v = MatrixReal1->new($rows,1); 1706 # Fills it 1707 for (my $i = 0; $i < $rows; $i++) 1708 { 1709 $v->[0][$i][0] = $diag->[$i]; 1710 } 1711 return ($v, $EV); 1712 } 1713 1714 # Main routine for diagonalization of a real symmetric 1715 # matrix M. Operates by transforming M into a tridiagonal 1716 # matrix and then obtaining the eigenvalues and eigenvectors 1717 # for that matrix (taking into account the transformation to 1718 # tridiagonal). 1719 sub sym_diagonalize ($) 1720 { 1721 my ($M) = @_; 1722 my ($rows, $cols) = ($M->[1], $M->[2]); 1723 1724 croak "Matrix is not quadratic" 1725 unless ($rows = $cols); 1726 croak "Matrix is not symmetric" 1727 unless ($M->is_symmetric()); 1728 1729 # Copy initial matrix 1730 # TODO: study if we should allow in-place modification 1731 my $VEC = $M->clone(); 1732 1733 # Do the computation of tridiagonal elements and of 1734 # transformation matrix 1735 my ($diag, $offdiag) = $VEC->_householder_vectors(); 1736 # Calls the calculus routine for diagonalization 1737 $VEC->_tridiagonal_QLimplicit($diag, $offdiag); 1738 1739 # Allocate eigenvalues vector 1740 my $val = MatrixReal1->new($rows,1); 1741 # Fills it 1742 for (my $i = 0; $i < $rows; $i++) 1743 { 1744 $val->[0][$i][0] = $diag->[$i]; 1745 } 1746 return ($val, $VEC); 1747 } 1748 1749 # Householder reduction of a real, symmetric matrix A. 1750 # Returns a tridiagonal matrix T equivalent to A. 1751 sub householder_tridiagonal ($) 1752 { 1753 my ($A) = @_; 1754 my ($rows, $cols) = ($A->[1], $A->[2]); 1755 1756 croak "Matrix is not quadratic" 1757 unless ($rows = $cols); 1758 croak "Matrix is not symmetric" 1759 unless ($A->is_symmetric()); 1760 1761 # Copy given matrix 1762 my $Q = $A->clone(); 1763 1764 # Do the computation of tridiagonal elements and of 1765 # transformation matrix 1766 # Q is destroyed after reduction 1767 my ($diag, $offdiag) = $Q->_householder_values(); 1768 1769 # Creates the tridiagonal matrix in Q (avoid allocation) 1770 my $T = $Q; 1771 $T->zero(); 1772 for (my $i = 0; $i < $rows; $i++) 1773 { # Set diagonal 1774 $T->[0][$i][$i] = $diag->[$i]; 1775 } 1776 for (my $i = 0; $i < ($rows-1); $i++) 1777 { # Set off diagonals 1778 $T->[0][$i+1][$i] = $offdiag->[$i]; 1779 $T->[0][$i][$i+1] = $offdiag->[$i]; 1780 } 1781 return $T; 1782 } 1783 1784 # QL algorithm with implicit shifts to determine ONLY 1785 # the eigenvalues a real tridiagonal matrix - or of a 1786 # matrix previously reduced to tridiagonal form. 1787 sub tri_eigenvalues ($;$) 1788 { 1789 my ($T) = @_; 1790 my ($rows, $cols) = ($T->[1], $T->[2]); 1791 1792 croak "Matrix is not quadratic" 1793 unless ($rows = $cols); 1794 croak "Matrix is not tridiagonal" 1795 unless ($T->is_symmetric()); # TODO: Do real tridiag check (not symmetric)! 1796 1797 # Allocates diagonal vector 1798 my $diag = [ ]; 1799 # Initializes it with T 1800 for (my $i = 0; $i < $rows; $i++) 1801 { 1802 $diag->[$i] = $T->[0][$i][$i]; 1803 } 1804 # Allocate temporary vector for off-diagonal elements 1805 my $offdiag = [ ]; 1806 for (my $i = 1; $i < $rows; $i++) 1807 { 1808 $offdiag->[$i-1] = $T->[0][$i][$i-1]; 1809 } 1810 1811 # Calls the calculus routine (T is not touched) 1812 $T->_tridiagonal_QLimplicit_values($diag, $offdiag); 1813 1814 # Allocate eigenvalues vector 1815 my $v = MatrixReal1->new($rows,1); 1816 # Fills it 1817 for (my $i = 0; $i < $rows; $i++) 1818 { 1819 $v->[0][$i][0] = $diag->[$i]; 1820 } 1821 return $v; 1822 } 1823 1824 # Main routine for diagonalization of a real symmetric 1825 # matrix M. Operates by transforming M into a tridiagonal 1826 # matrix and then obtaining the eigenvalues and eigenvectors 1827 # for that matrix (taking into account the transformation to 1828 # tridiagonal). 1829 sub sym_eigenvalues ($) 1830 { 1831 my ($M) = @_; 1832 my ($rows, $cols) = ($M->[1], $M->[2]); 1833 1834 croak "Matrix is not quadratic" 1835 unless ($rows = $cols); 1836 croak "Matrix is not symmetric" 1837 unless ($M->is_symmetric()); 1838 1839 # Copy matrix in temporary 1840 my $A = $M->clone(); 1841 # Do the computation of tridiagonal elements and of 1842 # transformation matrix. A is destroyed 1843 my ($diag, $offdiag) = $A->_householder_values(); 1844 # Calls the calculus routine for diagonalization 1845 # (M is not touched) 1846 $M->_tridiagonal_QLimplicit_values($diag, $offdiag); 1847 1848 # Allocate eigenvalues vector 1849 my $val = MatrixReal1->new($rows,1); 1850 # Fills it 1851 for (my $i = 0; $i < $rows; $i++) 1852 { 1853 $val->[0][$i][0] = $diag->[$i]; 1854 } 1855 return $val; 1856 } 1857 1858 # Boolean check routine to see if a matrix is 1859 # symmetric 1860 sub is_symmetric ($) 1861 { 1862 my ($M) = @_; 1863 my ($rows, $cols) = ($M->[1], $M->[2]); 1864 # if it is not quadratic it cannot be symmetric... 1865 return 0 unless ($rows == $cols); 1866 for (my $i = 1; $i < $rows; $i++) 1867 { 1868 for (my $j = 0; $j < $i; $j++) 1869 { 1870 return 0 unless ($M->[0][$i][$j] == $M->[0][$j][$i]); 1871 } 1872 } 1873 return 1; 1874 } 1875 1876 ######################################## 1877 # # 1878 # define overloaded operators section: # 1879 # # 1880 ######################################## 1881 1882 sub _negate 1883 { 1884 my($object,$argument,$flag) = @_; 1885 # my($name) = "neg"; #&_trace($name,$object,$argument,$flag); 1886 my($temp); 1887 1888 $temp = $object->new($object->[1],$object->[2]); 1889 $temp->negate($object); 1890 return($temp); 1891 } 1892 1893 sub _transpose 1894 { 1895 my($object,$argument,$flag) = @_; 1896 # my($name) = "'~'"; #&_trace($name,$object,$argument,$flag); 1897 my($temp); 1898 1899 $temp = $object->new($object->[2],$object->[1]); 1900 $temp->transpose($object); 1901 return($temp); 1902 } 1903 1904 sub _boolean 1905 { 1906 my($object,$argument,$flag) = @_; 1907 # my($name) = "bool"; #&_trace($name,$object,$argument,$flag); 1908 my($rows,$cols) = ($object->[1],$object->[2]); 1909 my($i,$j,$result); 1910 1911 $result = 0; 1912 BOOL: 1913 for ( $i = 0; $i < $rows; $i++ ) 1914 { 1915 for ( $j = 0; $j < $cols; $j++ ) 1916 { 1917 if ($object->[0][$i][$j] != 0) 1918 { 1919 $result = 1; 1920 last BOOL; 1921 } 1922 } 1923 } 1924 return($result); 1925 } 1926 1927 sub _not_boolean 1928 { 1929 my($object,$argument,$flag) = @_; 1930 # my($name) = "'!'"; #&_trace($name,$object,$argument,$flag); 1931 my($rows,$cols) = ($object->[1],$object->[2]); 1932 my($i,$j,$result); 1933 1934 $result = 1; 1935 NOTBOOL: 1936 for ( $i = 0; $i < $rows; $i++ ) 1937 { 1938 for ( $j = 0; $j < $cols; $j++ ) 1939 { 1940 if ($object->[0][$i][$j] != 0) 1941 { 1942 $result = 0; 1943 last NOTBOOL; 1944 } 1945 } 1946 } 1947 return($result); 1948 } 1949 1950 sub _stringify 1951 { 1952 my($object,$argument,$flag) = @_; 1953 # my($name) = '""'; #&_trace($name,$object,$argument,$flag); 1954 my($rows,$cols) = ($object->[1],$object->[2]); 1955 my($i,$j,$s); 1956 1957 $s = ''; 1958 for ( $i = 0; $i < $rows; $i++ ) 1959 { 1960 $s .= "[ "; 1961 for ( $j = 0; $j < $cols; $j++ ) 1962 { 1963 $s .= sprintf("% #-19.12E ", $object->[0][$i][$j]); 1964 } 1965 $s .= "]\n"; 1966 } 1967 return($s); 1968 } 1969 1970 sub _norm 1971 { 1972 my($object,$argument,$flag) = @_; 1973 # my($name) = "abs"; #&_trace($name,$object,$argument,$flag); 1974 1975 return( $object->norm_one() ); 1976 } 1977 1978 sub _add 1979 { 1980 my($object,$argument,$flag) = @_; 1981 my($name) = "'+'"; #&_trace($name,$object,$argument,$flag); 1982 my($temp); 1983 1984 if ((defined $argument) && ref($argument) && 1985 (ref($argument) !~ /^SCALAR$|^ARRAY$|^HASH$|^CODE$|^REF$/)) 1986 { 1987 if (defined $flag) 1988 { 1989 $temp = $object->new($object->[1],$object->[2]); 1990 $temp->add($object,$argument); 1991 return($temp); 1992 } 1993 else 1994 { 1995 $object->add($object,$argument); 1996 return($object); 1997 } 1998 } 1999 else 2000 { 2001 croak "MatrixReal1 $name: wrong argument type"; 2002 } 2003 } 2004 2005 sub _subtract 2006 { 2007 my($object,$argument,$flag) = @_; 2008 my($name) = "'-'"; #&_trace($name,$object,$argument,$flag); 2009 my($temp); 2010 2011 if ((defined $argument) && ref($argument) && 2012 (ref($argument) !~ /^SCALAR$|^ARRAY$|^HASH$|^CODE$|^REF$/)) 2013 { 2014 if (defined $flag) 2015 { 2016 $temp = $object->new($object->[1],$object->[2]); 2017 if ($flag) { $temp->subtract($argument,$object); } 2018 else { $temp->subtract($object,$argument); } 2019 return($temp); 2020 } 2021 else 2022 { 2023 $object->subtract($object,$argument); 2024 return($object); 2025 } 2026 } 2027 else 2028 { 2029 croak "MatrixReal1 $name: wrong argument type"; 2030 } 2031 } 2032 2033 sub _multiply 2034 { 2035 my($object,$argument,$flag) = @_; 2036 my($name) = "'*'"; #&_trace($name,$object,$argument,$flag); 2037 my($temp); 2038 2039 if ((defined $argument) && ref($argument) && 2040 (ref($argument) !~ /^SCALAR$|^ARRAY$|^HASH$|^CODE$|^REF$/)) 2041 { 2042 if ((defined $flag) && $flag) 2043 { 2044 return( multiply($argument,$object) ); 2045 } 2046 else 2047 { 2048 return( multiply($object,$argument) ); 2049 } 2050 } 2051 elsif ((defined $argument) && !(ref($argument))) 2052 { 2053 if (defined $flag) 2054 { 2055 $temp = $object->new($object->[1],$object->[2]); 2056 $temp->multiply_scalar($object,$argument); 2057 return($temp); 2058 } 2059 else 2060 { 2061 $object->multiply_scalar($object,$argument); 2062 return($object); 2063 } 2064 } 2065 else 2066 { 2067 croak "MatrixReal1 $name: wrong argument type"; 2068 } 2069 } 2070 2071 sub _assign_add 2072 { 2073 my($object,$argument,$flag) = @_; 2074 # my($name) = "'+='"; #&_trace($name,$object,$argument,$flag); 2075 2076 return( &_add($object,$argument,undef) ); 2077 } 2078 2079 sub _assign_subtract 2080 { 2081 my($object,$argument,$flag) = @_; 2082 # my($name) = "'-='"; #&_trace($name,$object,$argument,$flag); 2083 2084 return( &_subtract($object,$argument,undef) ); 2085 } 2086 2087 sub _assign_multiply 2088 { 2089 my($object,$argument,$flag) = @_; 2090 # my($name) = "'*='"; #&_trace($name,$object,$argument,$flag); 2091 2092 return( &_multiply($object,$argument,undef) ); 2093 } 2094 2095 sub _equal 2096 { 2097 my($object,$argument,$flag) = @_; 2098 my($name) = "'=='"; #&_trace($name,$object,$argument,$flag); 2099 my($rows,$cols) = ($object->[1],$object->[2]); 2100 my($i,$j,$result); 2101 2102 if ((defined $argument) && ref($argument) && 2103 (ref($argument) !~ /^SCALAR$|^ARRAY$|^HASH$|^CODE$|^REF$/)) 2104 { 2105 $result = 1; 2106 EQUAL: 2107 for ( $i = 0; $i < $rows; $i++ ) 2108 { 2109 for ( $j = 0; $j < $cols; $j++ ) 2110 { 2111 if ($object->[0][$i][$j] != $argument->[0][$i][$j]) 2112 { 2113 $result = 0; 2114 last EQUAL; 2115 } 2116 } 2117 } 2118 return($result); 2119 } 2120 else 2121 { 2122 croak "MatrixReal1 $name: wrong argument type"; 2123 } 2124 } 2125 2126 sub _not_equal 2127 { 2128 my($object,$argument,$flag) = @_; 2129 my($name) = "'!='"; #&_trace($name,$object,$argument,$flag); 2130 my($rows,$cols) = ($object->[1],$object->[2]); 2131 my($i,$j,$result); 2132 2133 if ((defined $argument) && ref($argument) && 2134 (ref($argument) !~ /^SCALAR$|^ARRAY$|^HASH$|^CODE$|^REF$/)) 2135 { 2136 $result = 0; 2137 NOTEQUAL: 2138 for ( $i = 0; $i < $rows; $i++ ) 2139 { 2140 for ( $j = 0; $j < $cols; $j++ ) 2141 { 2142 if ($object->[0][$i][$j] != $argument->[0][$i][$j]) 2143 { 2144 $result = 1; 2145 last NOTEQUAL; 2146 } 2147 } 2148 } 2149 return($result); 2150 } 2151 else 2152 { 2153 croak "MatrixReal1 $name: wrong argument type"; 2154 } 2155 } 2156 2157 sub _less_than 2158 { 2159 my($object,$argument,$flag) = @_; 2160 my($name) = "'<'"; #&_trace($name,$object,$argument,$flag); 2161 2162 if ((defined $argument) && ref($argument) && 2163 (ref($argument) !~ /^SCALAR$|^ARRAY$|^HASH$|^CODE$|^REF$/)) 2164 { 2165 if ((defined $flag) && $flag) 2166 { 2167 return( $argument->norm_one() < $object->norm_one() ); 2168 } 2169 else 2170 { 2171 return( $object->norm_one() < $argument->norm_one() ); 2172 } 2173 } 2174 elsif ((defined $argument) && !(ref($argument))) 2175 { 2176 if ((defined $flag) && $flag) 2177 { 2178 return( abs($argument) < $object->norm_one() ); 2179 } 2180 else 2181 { 2182 return( $object->norm_one() < abs($argument) ); 2183 } 2184 } 2185 else 2186 { 2187 croak "MatrixReal1 $name: wrong argument type"; 2188 } 2189 } 2190 2191 sub _less_than_or_equal 2192 { 2193 my($object,$argument,$flag) = @_; 2194 my($name) = "'<='"; #&_trace($name,$object,$argument,$flag); 2195 2196 if ((defined $argument) && ref($argument) && 2197 (ref($argument) !~ /^SCALAR$|^ARRAY$|^HASH$|^CODE$|^REF$/)) 2198 { 2199 if ((defined $flag) && $flag) 2200 { 2201 return( $argument->norm_one() <= $object->norm_one() ); 2202 } 2203 else 2204 { 2205 return( $object->norm_one() <= $argument->norm_one() ); 2206 } 2207 } 2208 elsif ((defined $argument) && !(ref($argument))) 2209 { 2210 if ((defined $flag) && $flag) 2211 { 2212 return( abs($argument) <= $object->norm_one() ); 2213 } 2214 else 2215 { 2216 return( $object->norm_one() <= abs($argument) ); 2217 } 2218 } 2219 else 2220 { 2221 croak "MatrixReal1 $name: wrong argument type"; 2222 } 2223 } 2224 2225 sub _greater_than 2226 { 2227 my($object,$argument,$flag) = @_; 2228 my($name) = "'>'"; #&_trace($name,$object,$argument,$flag); 2229 2230 if ((defined $argument) && ref($argument) && 2231 (ref($argument) !~ /^SCALAR$|^ARRAY$|^HASH$|^CODE$|^REF$/)) 2232 { 2233 if ((defined $flag) && $flag) 2234 { 2235 return( $argument->norm_one() > $object->norm_one() ); 2236 } 2237 else 2238 { 2239 return( $object->norm_one() > $argument->norm_one() ); 2240 } 2241 } 2242 elsif ((defined $argument) && !(ref($argument))) 2243 { 2244 if ((defined $flag) && $flag) 2245 { 2246 return( abs($argument) > $object->norm_one() ); 2247 } 2248 else 2249 { 2250 return( $object->norm_one() > abs($argument) ); 2251 } 2252 } 2253 else 2254 { 2255 croak "MatrixReal1 $name: wrong argument type"; 2256 } 2257 } 2258 2259 sub _greater_than_or_equal 2260 { 2261 my($object,$argument,$flag) = @_; 2262 my($name) = "'>='"; #&_trace($name,$object,$argument,$flag); 2263 2264 if ((defined $argument) && ref($argument) && 2265 (ref($argument) !~ /^SCALAR$|^ARRAY$|^HASH$|^CODE$|^REF$/)) 2266 { 2267 if ((defined $flag) && $flag) 2268 { 2269 return( $argument->norm_one() >= $object->norm_one() ); 2270 } 2271 else 2272 { 2273 return( $object->norm_one() >= $argument->norm_one() ); 2274 } 2275 } 2276 elsif ((defined $argument) && !(ref($argument))) 2277 { 2278 if ((defined $flag) && $flag) 2279 { 2280 return( abs($argument) >= $object->norm_one() ); 2281 } 2282 else 2283 { 2284 return( $object->norm_one() >= abs($argument) ); 2285 } 2286 } 2287 else 2288 { 2289 croak "MatrixReal1 $name: wrong argument type"; 2290 } 2291 } 2292 2293 sub _clone 2294 { 2295 my($object,$argument,$flag) = @_; 2296 # my($name) = "'='"; #&_trace($name,$object,$argument,$flag); 2297 my($temp); 2298 2299 $temp = $object->new($object->[1],$object->[2]); 2300 $temp->copy($object); 2301 $temp->_undo_LR(); 2302 return($temp); 2303 } 2304 2305 sub _trace 2306 { 2307 my($text,$object,$argument,$flag) = @_; 2308 2309 unless (defined $object) { $object = 'undef'; }; 2310 unless (defined $argument) { $argument = 'undef'; }; 2311 unless (defined $flag) { $flag = 'undef'; }; 2312 if (ref($object)) { $object = ref($object); } 2313 if (ref($argument)) { $argument = ref($argument); } 2314 print "$text: \$obj='$object' \$arg='$argument' \$flag='$flag'\n"; 2315 } 2316 2317 1; 2318 2319 __END__ 2320 2321 =head1 NAME 2322 2323 MatrixReal1 - Matrix of Reals 2324 2325 Implements the data type "matrix of reals" (and consequently also 2326 "vector of reals") 2327 2328 =head1 DESCRIPTION 2329 2330 Implements the data type "matrix of reals", which can be used almost 2331 like any other basic Perl type thanks to B<OPERATOR OVERLOADING>, i.e., 2332 2333 $product = $matrix1 * $matrix2; 2334 2335 does what you would like it to do (a matrix multiplication). 2336 2337 Also features many important operations and methods: matrix norm, 2338 matrix transposition, matrix inverse, determinant of a matrix, order 2339 and numerical condition of a matrix, scalar product of vectors, vector 2340 product of vectors, vector length, projection of row and column vectors, 2341 a comfortable way for reading in a matrix from a file, the keyboard or 2342 your code, and many more. 2343 2344 Allows to solve linear equation systems using an efficient algorithm 2345 known as "L-R-decomposition" and several approximative (iterative) methods. 2346 2347 Features an implementation of Kleene's algorithm to compute the minimal 2348 costs for all paths in a graph with weighted edges (the "weights" being 2349 the costs associated with each edge). 2350 2351 =head1 SYNOPSIS 2352 2353 =over 2 2354 2355 =item * 2356 2357 C<use MatrixReal1;> 2358 2359 Makes the methods and overloaded operators of this module available 2360 to your program. 2361 2362 =item * 2363 2364 C<use MatrixReal1 qw(min max);> 2365 2366 =item * 2367 2368 C<use MatrixReal1 qw(:all);> 2369 2370 Use one of these two variants to import (all) the functions which the module 2371 offers for export; currently these are "min()" and "max()". 2372 2373 =item * 2374 2375 C<$new_matrix = new MatrixReal1($rows,$columns);> 2376 2377 The matrix object constructor method. 2378 2379 Note that this method is implicitly called by many of the other methods 2380 in this module! 2381 2382 =item * 2383 2384 C<$new_matrix = MatrixReal1-E<gt>>C<new($rows,$columns);> 2385 2386 An alternate way of calling the matrix object constructor method. 2387 2388 =item * 2389 2390 C<$new_matrix = $some_matrix-E<gt>>C<new($rows,$columns);> 2391 2392 Still another way of calling the matrix object constructor method. 2393 2394 Matrix "C<$some_matrix>" is not changed by this in any way. 2395 2396 =item * 2397 2398 C<$new_matrix = MatrixReal1-E<gt>>C<new_from_string($string);> 2399 2400 This method allows you to read in a matrix from a string (for 2401 instance, from the keyboard, from a file or from your code). 2402 2403 The syntax is simple: each row must start with "C<[ >" and end with 2404 "C< ]\n>" ("C<\n>" being the newline character and "C< >" a space or 2405 tab) and contain one or more numbers, all separated from each other 2406 by spaces or tabs. 2407 2408 Additional spaces or tabs can be added at will, but no comments. 2409 2410 Examples: 2411 2412 $string = "[ 1 2 3 ]\n[ 2 2 -1 ]\n[ 1 1 1 ]\n"; 2413 $matrix = MatrixReal1->new_from_string($string); 2414 print "$matrix"; 2415 2416 By the way, this prints 2417 2418 [ 1.000000000000E+00 2.000000000000E+00 3.000000000000E+00 ] 2419 [ 2.000000000000E+00 2.000000000000E+00 -1.000000000000E+00 ] 2420 [ 1.000000000000E+00 1.000000000000E+00 1.000000000000E+00 ] 2421 2422 But you can also do this in a much more comfortable way using the 2423 shell-like "here-document" syntax: 2424 2425 $matrix = MatrixReal1->new_from_string(<<'MATRIX'); 2426 [ 1 0 0 0 0 0 1 ] 2427 [ 0 1 0 0 0 0 0 ] 2428 [ 0 0 1 0 0 0 0 ] 2429 [ 0 0 0 1 0 0 0 ] 2430 [ 0 0 0 0 1 0 0 ] 2431 [ 0 0 0 0 0 1 0 ] 2432 [ 1 0 0 0 0 0 -1 ] 2433 MATRIX 2434 2435 You can even use variables in the matrix: 2436 2437 $c1 = 2 / 3; 2438 $c2 = -2 / 5; 2439 $c3 = 26 / 9; 2440 2441 $matrix = MatrixReal1->new_from_string(<<"MATRIX"); 2442 2443 [ 3 2 0 ] 2444 [ 0 3 2 ] 2445 [ $c1 $c2 $c3 ] 2446 2447 MATRIX 2448 2449 (Remember that you may use spaces and tabs to format the matrix to 2450 your taste) 2451 2452 Note that this method uses exactly the same representation for a 2453 matrix as the "stringify" operator "": this means that you can convert 2454 any matrix into a string with C<$string = "$matrix";> and read it back 2455 in later (for instance from a file!). 2456 2457 Note however that you may suffer a precision loss in this process 2458 because only 13 digits are supported in the mantissa when printed!! 2459 2460 If the string you supply (or someone else supplies) does not obey 2461 the syntax mentioned above, an exception is raised, which can be 2462 caught by "eval" as follows: 2463 2464 print "Please enter your matrix (in one line): "; 2465 $string = <STDIN>; 2466 $string =~ s/\\n/\n/g; 2467 eval { $matrix = MatrixReal1->new_from_string($string); }; 2468 if ($@) 2469 { 2470 print "$@"; 2471 # ... 2472 # (error handling) 2473 } 2474 else 2475 { 2476 # continue... 2477 } 2478 2479 or as follows: 2480 2481 eval { $matrix = MatrixReal1->new_from_string(<<"MATRIX"); }; 2482 [ 3 2 0 ] 2483 [ 0 3 2 ] 2484 [ $c1 $c2 $c3 ] 2485 MATRIX 2486 if ($@) 2487 # ... 2488 2489 Actually, the method shown above for reading a matrix from the keyboard 2490 is a little awkward, since you have to enter a lot of "\n"'s for the 2491 newlines. 2492 2493 A better way is shown in this piece of code: 2494 2495 while (1) 2496 { 2497 print "\nPlease enter your matrix "; 2498 print "(multiple lines, <ctrl-D> = done):\n"; 2499 eval { $new_matrix = 2500 MatrixReal1->new_from_string(join('',<STDIN>)); }; 2501 if ($@) 2502 { 2503 $@ =~ s/\s+at\b.*?$//; 2504 print "${@}Please try again.\n"; 2505 } 2506 else { last; } 2507 } 2508 2509 Possible error messages of the "new_from_string()" method are: 2510 2511 MatrixReal1::new_from_string(): syntax error in input string 2512 MatrixReal1::new_from_string(): empty input string 2513 2514 If the input string has rows with varying numbers of columns, 2515 the following warning will be printed to STDERR: 2516 2517 MatrixReal1::new_from_string(): missing elements will be set to zero! 2518 2519 If everything is okay, the method returns an object reference to the 2520 (newly allocated) matrix containing the elements you specified. 2521 2522 =item * 2523 2524 C<$new_matrix = $some_matrix-E<gt>shadow();> 2525 2526 Returns an object reference to a B<NEW> but B<EMPTY> matrix 2527 (filled with zero's) of the B<SAME SIZE> as matrix "C<$some_matrix>". 2528 2529 Matrix "C<$some_matrix>" is not changed by this in any way. 2530 2531 =item * 2532 2533 C<$matrix1-E<gt>copy($matrix2);> 2534 2535 Copies the contents of matrix "C<$matrix2>" to an B<ALREADY EXISTING> 2536 matrix "C<$matrix1>" (which must have the same size as matrix "C<$matrix2>"!). 2537 2538 Matrix "C<$matrix2>" is not changed by this in any way. 2539 2540 =item * 2541 2542 C<$twin_matrix = $some_matrix-E<gt>clone();> 2543 2544 Returns an object reference to a B<NEW> matrix of the B<SAME SIZE> as 2545 matrix "C<$some_matrix>". The contents of matrix "C<$some_matrix>" have 2546 B<ALREADY BEEN COPIED> to the new matrix "C<$twin_matrix>". 2547 2548 Matrix "C<$some_matrix>" is not changed by this in any way. 2549 2550 =item * 2551 2552 C<$row_vector = $matrix-E<gt>row($row);> 2553 2554 This is a projection method which returns an object reference to 2555 a B<NEW> matrix (which in fact is a (row) vector since it has only 2556 one row) to which row number "C<$row>" of matrix "C<$matrix>" has 2557 already been copied. 2558 2559 Matrix "C<$matrix>" is not changed by this in any way. 2560 2561 =item * 2562 2563 C<$column_vector = $matrix-E<gt>column($column);> 2564 2565 This is a projection method which returns an object reference to 2566 a B<NEW> matrix (which in fact is a (column) vector since it has 2567 only one column) to which column number "C<$column>" of matrix 2568 "C<$matrix>" has already been copied. 2569 2570 Matrix "C<$matrix>" is not changed by this in any way. 2571 2572 =item * 2573 2574 C<$matrix-E<gt>zero();> 2575 2576 Assigns a zero to every element of the matrix "C<$matrix>", i.e., 2577 erases all values previously stored there, thereby effectively 2578 transforming the matrix into a "zero"-matrix or "null"-matrix, 2579 the neutral element of the addition operation in a Ring. 2580 2581 (For instance the (quadratic) matrices with "n" rows and columns 2582 and matrix addition and multiplication form a Ring. Most prominent 2583 characteristic of a Ring is that multiplication is not commutative, 2584 i.e., in general, "C<matrix1 * matrix2>" is not the same as 2585 "C<matrix2 * matrix1>"!) 2586 2587 =item * 2588 2589 C<$matrix-E<gt>one();> 2590 2591 Assigns one's to the elements on the main diagonal (elements (1,1), 2592 (2,2), (3,3) and so on) of matrix "C<$matrix>" and zero's to all others, 2593 thereby erasing all values previously stored there and transforming the 2594 matrix into a "one"-matrix, the neutral element of the multiplication 2595 operation in a Ring. 2596 2597 (If the matrix is quadratic (which this method doesn't require, though), 2598 then multiplying this matrix with itself yields this same matrix again, 2599 and multiplying it with some other matrix leaves that other matrix 2600 unchanged!) 2601 2602 =item * 2603 2604 C<$matrix-E<gt>assign($row,$column,$value);> 2605 2606 Explicitly assigns a value "C<$value>" to a single element of the 2607 matrix "C<$matrix>", located in row "C<$row>" and column "C<$column>", 2608 thereby replacing the value previously stored there. 2609 2610 =item * 2611 2612 C<$value = $matrix-E<gt>>C<element($row,$column);> 2613 2614 Returns the value of a specific element of the matrix "C<$matrix>", 2615 located in row "C<$row>" and column "C<$column>". 2616 2617 =item * 2618 2619 C<($rows,$columns) = $matrix-E<gt>dim();> 2620 2621 Returns a list of two items, representing the number of rows 2622 and columns the given matrix "C<$matrix>" contains. 2623 2624 =item * 2625 2626 C<$norm_one = $matrix-E<gt>norm_one();> 2627 2628 Returns the "one"-norm of the given matrix "C<$matrix>". 2629 2630 The "one"-norm is defined as follows: 2631 2632 For each column, the sum of the absolute values of the elements in the 2633 different rows of that column is calculated. Finally, the maximum 2634 of these sums is returned. 2635 2636 Note that the "one"-norm and the "maximum"-norm are mathematically 2637 equivalent, although for the same matrix they usually yield a different 2638 value. 2639 2640 Therefore, you should only compare values that have been calculated 2641 using the same norm! 2642 2643 Throughout this package, the "one"-norm is (arbitrarily) used 2644 for all comparisons, for the sake of uniformity and comparability, 2645 except for the iterative methods "solve_GSM()", "solve_SSM()" and 2646 "solve_RM()" which use either norm depending on the matrix itself. 2647 2648 =item * 2649 2650 C<$norm_max = $matrix-E<gt>norm_max();> 2651 2652 Returns the "maximum"-norm of the given matrix "C<$matrix>". 2653 2654 The "maximum"-norm is defined as follows: 2655 2656 For each row, the sum of the absolute values of the elements in the 2657 different columns of that row is calculated. Finally, the maximum 2658 of these sums is returned. 2659 2660 Note that the "maximum"-norm and the "one"-norm are mathematically 2661 equivalent, although for the same matrix they usually yield a different 2662 value. 2663 2664 Therefore, you should only compare values that have been calculated 2665 using the same norm! 2666 2667 Throughout this package, the "one"-norm is (arbitrarily) used 2668 for all comparisons, for the sake of uniformity and comparability, 2669 except for the iterative methods "solve_GSM()", "solve_SSM()" and 2670 "solve_RM()" which use either norm depending on the matrix itself. 2671 2672 =item * 2673 2674 C<$matrix1-E<gt>negate($matrix2);> 2675 2676 Calculates the negative of matrix "C<$matrix2>" (i.e., multiplies 2677 all elements with "-1") and stores the result in matrix "C<$matrix1>" 2678 (which must already exist and have the same size as matrix "C<$matrix2>"!). 2679 2680 This operation can also be carried out "in-place", i.e., input and 2681 output matrix may be identical. 2682 2683 =item * 2684 2685 C<$matrix1-E<gt>transpose($matrix2);> 2686 2687 Calculates the transposed matrix of matrix "C<$matrix2>" and stores 2688 the result in matrix "C<$matrix1>" (which must already exist and have 2689 the same size as matrix "C<$matrix2>"!). 2690 2691 This operation can also be carried out "in-place", i.e., input and 2692 output matrix may be identical. 2693 2694 Transposition is a symmetry operation: imagine you rotate the matrix 2695 along the axis of its main diagonal (going through elements (1,1), 2696 (2,2), (3,3) and so on) by 180 degrees. 2697 2698 Another way of looking at it is to say that rows and columns are 2699 swapped. In fact the contents of element C<(i,j)> are swapped 2700 with those of element C<(j,i)>. 2701 2702 Note that (especially for vectors) it makes a big difference if you 2703 have a row vector, like this: 2704 2705 [ -1 0 1 ] 2706 2707 or a column vector, like this: 2708 2709 [ -1 ] 2710 [ 0 ] 2711 [ 1 ] 2712 2713 the one vector being the transposed of the other! 2714 2715 This is especially true for the matrix product of two vectors: 2716 2717 [ -1 ] 2718 [ -1 0 1 ] * [ 0 ] = [ 2 ] , whereas 2719 [ 1 ] 2720 2721 * [ -1 0 1 ] 2722 [ -1 ] [ 1 0 -1 ] 2723 [ 0 ] * [ -1 0 1 ] = [ -1 ] [ 1 0 -1 ] = [ 0 0 0 ] 2724 [ 1 ] [ 0 ] [ 0 0 0 ] [ -1 0 1 ] 2725 [ 1 ] [ -1 0 1 ] 2726 2727 So be careful about what you really mean! 2728 2729 Hint: throughout this module, whenever a vector is explicitly required 2730 for input, a B<COLUMN> vector is expected! 2731 2732 =item * 2733 2734 C<$matrix1-E<gt>add($matrix2,$matrix3);> 2735 2736 Calculates the sum of matrix "C<$matrix2>" and matrix "C<$matrix3>" 2737 and stores the result in matrix "C<$matrix1>" (which must already exist 2738 and have the same size as matrix "C<$matrix2>" and matrix "C<$matrix3>"!). 2739 2740 This operation can also be carried out "in-place", i.e., the output and 2741 one (or both) of the input matrices may be identical. 2742 2743 =item * 2744 2745 C<$matrix1-E<gt>subtract($matrix2,$matrix3);> 2746 2747 Calculates the difference of matrix "C<$matrix2>" minus matrix "C<$matrix3>" 2748 and stores the result in matrix "C<$matrix1>" (which must already exist 2749 and have the same size as matrix "C<$matrix2>" and matrix "C<$matrix3>"!). 2750 2751 This operation can also be carried out "in-place", i.e., the output and 2752 one (or both) of the input matrices may be identical. 2753 2754 Note that this operation is the same as 2755 C<$matrix1-E<gt>add($matrix2,-$matrix3);>, although the latter is 2756 a little less efficient. 2757 2758 =item * 2759 2760 C<$matrix1-E<gt>multiply_scalar($matrix2,$scalar);> 2761 2762 Calculates the product of matrix "C<$matrix2>" and the number "C<$scalar>" 2763 (i.e., multiplies each element of matrix "C<$matrix2>" with the factor 2764 "C<$scalar>") and stores the result in matrix "C<$matrix1>" (which must 2765 already exist and have the same size as matrix "C<$matrix2>"!). 2766 2767 This operation can also be carried out "in-place", i.e., input and 2768 output matrix may be identical. 2769 2770 =item * 2771 2772 C<$product_matrix = $matrix1-E<gt>multiply($matrix2);> 2773 2774 Calculates the product of matrix "C<$matrix1>" and matrix "C<$matrix2>" 2775 and returns an object reference to a new matrix "C<$product_matrix>" in 2776 which the result of this operation has been stored. 2777 2778 Note that the dimensions of the two matrices "C<$matrix1>" and "C<$matrix2>" 2779 (i.e., their numbers of rows and columns) must harmonize in the following 2780 way (example): 2781 2782 [ 2 2 ] 2783 [ 2 2 ] 2784 [ 2 2 ] 2785 2786 [ 1 1 1 ] [ * * ] 2787 [ 1 1 1 ] [ * * ] 2788 [ 1 1 1 ] [ * * ] 2789 [ 1 1 1 ] [ * * ] 2790 2791 I.e., the number of columns of matrix "C<$matrix1>" has to be the same 2792 as the number of rows of matrix "C<$matrix2>". 2793 2794 The number of rows and columns of the resulting matrix "C<$product_matrix>" 2795 is determined by the number of rows of matrix "C<$matrix1>" and the number 2796 of columns of matrix "C<$matrix2>", respectively. 2797 2798 =item * 2799 2800 C<$minimum = MatrixReal1::min($number1,$number2);> 2801 2802 Returns the minimum of the two numbers "C<number1>" and "C<number2>". 2803 2804 =item * 2805 2806 C<$minimum = MatrixReal1::max($number1,$number2);> 2807 2808 Returns the maximum of the two numbers "C<number1>" and "C<number2>". 2809 2810 =item * 2811 2812 C<$minimal_cost_matrix = $cost_matrix-E<gt>kleene();> 2813 2814 Copies the matrix "C<$cost_matrix>" (which has to be quadratic!) to 2815 a new matrix of the same size (i.e., "clones" the input matrix) and 2816 applies Kleene's algorithm to it. 2817 2818 See L<Math::Kleene(3)> for more details about this algorithm! 2819 2820 The method returns an object reference to the new matrix. 2821 2822 Matrix "C<$cost_matrix>" is not changed by this method in any way. 2823 2824 =item * 2825 2826 C<($norm_matrix,$norm_vector) = $matrix-E<gt>normalize($vector);> 2827 2828 This method is used to improve the numerical stability when solving 2829 linear equation systems. 2830 2831 Suppose you have a matrix "A" and a vector "b" and you want to find 2832 out a vector "x" so that C<A * x = b>, i.e., the vector "x" which 2833 solves the equation system represented by the matrix "A" and the 2834 vector "b". 2835 2836 Applying this method to the pair (A,b) yields a pair (A',b') where 2837 each row has been divided by (the absolute value of) the greatest 2838 coefficient appearing in that row. So this coefficient becomes equal 2839 to "1" (or "-1") in the new pair (A',b') (all others become smaller 2840 than one and greater than minus one). 2841 2842 Note that this operation does not change the equation system itself 2843 because the same division is carried out on either side of the equation 2844 sign! 2845 2846 The method requires a quadratic (!) matrix "C<$matrix>" and a vector 2847 "C<$vector>" for input (the vector must be a column vector with the same 2848 number of rows as the input matrix) and returns a list of two items 2849 which are object references to a new matrix and a new vector, in this 2850 order. 2851 2852 The output matrix and vector are clones of the input matrix and vector 2853 to which the operation explained above has been applied. 2854 2855 The input matrix and vector are not changed by this in any way. 2856 2857 Example of how this method can affect the result of the methods to solve 2858 equation systems (explained immediately below following this method): 2859 2860 Consider the following little program: 2861 2862 #!perl -w 2863 2864 use MatrixReal1 qw(new_from_string); 2865 2866 $A = MatrixReal1->new_from_string(<<"MATRIX"); 2867 [ 1 2 3 ] 2868 [ 5 7 11 ] 2869 [ 23 19 13 ] 2870 MATRIX 2871 2872 $b = MatrixReal1->new_from_string(<<"MATRIX"); 2873 [ 0 ] 2874 [ 1 ] 2875 [ 29 ] 2876 MATRIX 2877 2878 $LR = $A->decompose_LR(); 2879 if (($dim,$x,$B) = $LR->solve_LR($b)) 2880 { 2881 $test = $A * $x; 2882 print "x = \n$x"; 2883 print "A * x = \n$test"; 2884 } 2885 2886 ($A_,$b_) = $A->normalize($b); 2887 2888 $LR = $A_->decompose_LR(); 2889 if (($dim,$x,$B) = $LR->solve_LR($b_)) 2890 { 2891 $test = $A * $x; 2892 print "x = \n$x"; 2893 print "A * x = \n$test"; 2894 } 2895 2896 This will print: 2897 2898 x = 2899 [ 1.000000000000E+00 ] 2900 [ 1.000000000000E+00 ] 2901 [ -1.000000000000E+00 ] 2902 A * x = 2903 [ 4.440892098501E-16 ] 2904 [ 1.000000000000E+00 ] 2905 [ 2.900000000000E+01 ] 2906 x = 2907 [ 1.000000000000E+00 ] 2908 [ 1.000000000000E+00 ] 2909 [ -1.000000000000E+00 ] 2910 A * x = 2911 [ 0.000000000000E+00 ] 2912 [ 1.000000000000E+00 ] 2913 [ 2.900000000000E+01 ] 2914 2915 You can see that in the second example (where "normalize()" has been used), 2916 the result is "better", i.e., more accurate! 2917 2918 =item * 2919 2920 C<$LR_matrix = $matrix-E<gt>decompose_LR();> 2921 2922 This method is needed to solve linear equation systems. 2923 2924 Suppose you have a matrix "A" and a vector "b" and you want to find 2925 out a vector "x" so that C<A * x = b>, i.e., the vector "x" which 2926 solves the equation system represented by the matrix "A" and the 2927 vector "b". 2928 2929 You might also have a matrix "A" and a whole bunch of different 2930 vectors "b1".."bk" for which you need to find vectors "x1".."xk" 2931 so that C<A * xi = bi>, for C<i=1..k>. 2932 2933 Using Gaussian transformations (multiplying a row or column with 2934 a factor, swapping two rows or two columns and adding a multiple 2935 of one row or column to another), it is possible to decompose any 2936 matrix "A" into two triangular matrices, called "L" and "R" (for 2937 "Left" and "Right"). 2938 2939 "L" has one's on the main diagonal (the elements (1,1), (2,2), (3,3) 2940 and so so), non-zero values to the left and below of the main diagonal 2941 and all zero's in the upper right half of the matrix. 2942 2943 "R" has non-zero values on the main diagonal as well as to the right 2944 and above of the main diagonal and all zero's in the lower left half 2945 of the matrix, as follows: 2946 2947 [ 1 0 0 0 0 ] [ x x x x x ] 2948 [ x 1 0 0 0 ] [ 0 x x x x ] 2949 L = [ x x 1 0 0 ] R = [ 0 0 x x x ] 2950 [ x x x 1 0 ] [ 0 0 0 x x ] 2951 [ x x x x 1 ] [ 0 0 0 0 x ] 2952 2953 Note that "C<L * R>" is equivalent to matrix "A" in the sense that 2954 C<L * R * x = b E<lt>==E<gt> A * x = b> for all vectors "x", leaving 2955 out of account permutations of the rows and columns (these are taken 2956 care of "magically" by this module!) and numerical errors. 2957 2958 Trick: 2959 2960 Because we know that "L" has one's on its main diagonal, we can 2961 store both matrices together in the same array without information 2962 loss! I.e., 2963 2964 [ R R R R R ] 2965 [ L R R R R ] 2966 LR = [ L L R R R ] 2967 [ L L L R R ] 2968 [ L L L L R ] 2969 2970 Beware, though, that "LR" and "C<L * R>" are not the same!!! 2971 2972 Note also that for the same reason, you cannot apply the method "normalize()" 2973 to an "LR" decomposition matrix. Trying to do so will yield meaningless 2974 rubbish! 2975 2976 (You need to apply "normalize()" to each pair (Ai,bi) B<BEFORE> decomposing 2977 the matrix "Ai'"!) 2978 2979 Now what does all this help us in solving linear equation systems? 2980 2981 It helps us because a triangular matrix is the next best thing 2982 that can happen to us besides a diagonal matrix (a matrix that 2983 has non-zero values only on its main diagonal - in which case 2984 the solution is trivial, simply divide "C<b[i]>" by "C<A[i,i]>" 2985 to get "C<x[i]>"!). 2986 2987 To find the solution to our problem "C<A * x = b>", we divide this 2988 problem in parts: instead of solving C<A * x = b> directly, we first 2989 decompose "A" into "L" and "R" and then solve "C<L * y = b>" and 2990 finally "C<R * x = y>" (motto: divide and rule!). 2991 2992 From the illustration above it is clear that solving "C<L * y = b>" 2993 and "C<R * x = y>" is straightforward: we immediately know that 2994 C<y[1] = b[1]>. We then deduce swiftly that 2995 2996 y[2] = b[2] - L[2,1] * y[1] 2997 2998 (and we know "C<y[1]>" by now!), that 2999 3000 y[3] = b[3] - L[3,1] * y[1] - L[3,2] * y[2] 3001 3002 and so on. 3003 3004 Having effortlessly calculated the vector "y", we now proceed to 3005 calculate the vector "x" in a similar fashion: we see immediately 3006 that C<x[n] = y[n] / R[n,n]>. It follows that 3007 3008 x[n-1] = ( y[n-1] - R[n-1,n] * x[n] ) / R[n-1,n-1] 3009 3010 and 3011 3012 x[n-2] = ( y[n-2] - R[n-2,n-1] * x[n-1] - R[n-2,n] * x[n] ) 3013 / R[n-2,n-2] 3014 3015 and so on. 3016 3017 You can see that - especially when you have many vectors "b1".."bk" 3018 for which you are searching solutions to C<A * xi = bi> - this scheme 3019 is much more efficient than a straightforward, "brute force" approach. 3020 3021 This method requires a quadratic matrix as its input matrix. 3022 3023 If you don't have that many equations, fill up with zero's (i.e., do 3024 nothing to fill the superfluous rows if it's a "fresh" matrix, i.e., 3025 a matrix that has been created with "new()" or "shadow()"). 3026 3027 The method returns an object reference to a new matrix containing the 3028 matrices "L" and "R". 3029 3030 The input matrix is not changed by this method in any way. 3031 3032 Note that you can "copy()" or "clone()" the result of this method without 3033 losing its "magical" properties (for instance concerning the hidden 3034 permutations of its rows and columns). 3035 3036 However, as soon as you are applying any method that alters the contents 3037 of the matrix, its "magical" properties are stripped off, and the matrix 3038 immediately reverts to an "ordinary" matrix (with the values it just happens 3039 to contain at that moment, be they meaningful as an ordinary matrix or not!). 3040 3041 =item * 3042 3043 C<($dimension,$x_vector,$base_matrix) = $LR_matrix>C<-E<gt>>C<solve_LR($b_vector);> 3044 3045 Use this method to actually solve an equation system. 3046 3047 Matrix "C<$LR_matrix>" must be a (quadratic) matrix returned by the 3048 method "decompose_LR()", the LR decomposition matrix of the matrix 3049 "A" of your equation system C<A * x = b>. 3050 3051 The input vector "C<$b_vector>" is the vector "b" in your equation system 3052 C<A * x = b>, which must be a column vector and have the same number of 3053 rows as the input matrix "C<$LR_matrix>". 3054 3055 The method returns a list of three items if a solution exists or an 3056 empty list otherwise (!). 3057 3058 Therefore, you should always use this method like this: 3059 3060 if ( ($dim,$x_vec,$base) = $LR->solve_LR($b_vec) ) 3061 { 3062 # do something with the solution... 3063 } 3064 else 3065 { 3066 # do something with the fact that there is no solution... 3067 } 3068 3069 The three items returned are: the dimension "C<$dimension>" of the solution 3070 space (which is zero if only one solution exists, one if the solution is 3071 a straight line, two if the solution is a plane, and so on), the solution 3072 vector "C<$x_vector>" (which is the vector "x" of your equation system 3073 C<A * x = b>) and a matrix "C<$base_matrix>" representing a base of the 3074 solution space (a set of vectors which put up the solution space like 3075 the spokes of an umbrella). 3076 3077 Only the first "C<$dimension>" columns of this base matrix actually 3078 contain entries, the remaining columns are all zero. 3079 3080 Now what is all this stuff with that "base" good for? 3081 3082 The output vector "x" is B<ALWAYS> a solution of your equation system 3083 C<A * x = b>. 3084 3085 But also any vector "C<$vector>" 3086 3087 $vector = $x_vector->clone(); 3088 3089 $machine_infinity = 1E+99; # or something like that 3090 3091 for ( $i = 1; $i <= $dimension; $i++ ) 3092 { 3093 $vector += rand($machine_infinity) * $base_matrix->column($i); 3094 } 3095 3096 is a solution to your problem C<A * x = b>, i.e., if "C<$A_matrix>" contains 3097 your matrix "A", then 3098 3099 print abs( $A_matrix * $vector - $b_vector ), "\n"; 3100 3101 should print a number around 1E-16 or so! 3102 3103 By the way, note that you can actually calculate those vectors "C<$vector>" 3104 a little more efficient as follows: 3105 3106 $rand_vector = $x_vector->shadow(); 3107 3108 $machine_infinity = 1E+99; # or something like that 3109 3110 for ( $i = 1; $i <= $dimension; $i++ ) 3111 { 3112 $rand_vector->assign($i,1, rand($machine_infinity) ); 3113 } 3114 3115 $vector = $x_vector + ( $base_matrix * $rand_vector ); 3116 3117 Note that the input matrix and vector are not changed by this method 3118 in any way. 3119 3120 =item * 3121 3122 C<$inverse_matrix = $LR_matrix-E<gt>invert_LR();> 3123 3124 Use this method to calculate the inverse of a given matrix "C<$LR_matrix>", 3125 which must be a (quadratic) matrix returned by the method "decompose_LR()". 3126 3127 The method returns an object reference to a new matrix of the same size as 3128 the input matrix containing the inverse of the matrix that you initially 3129 fed into "decompose_LR()" B<IF THE INVERSE EXISTS>, or an empty list 3130 otherwise. 3131 3132 Therefore, you should always use this method in the following way: 3133 3134 if ( $inverse_matrix = $LR->invert_LR() ) 3135 { 3136 # do something with the inverse matrix... 3137 } 3138 else 3139 { 3140 # do something with the fact that there is no inverse matrix... 3141 } 3142 3143 Note that by definition (disregarding numerical errors), the product 3144 of the initial matrix and its inverse (or vice-versa) is always a matrix 3145 containing one's on the main diagonal (elements (1,1), (2,2), (3,3) and 3146 so on) and zero's elsewhere. 3147 3148 The input matrix is not changed by this method in any way. 3149 3150 =item * 3151 3152 C<$condition = $matrix-E<gt>condition($inverse_matrix);> 3153 3154 In fact this method is just a shortcut for 3155 3156 abs($matrix) * abs($inverse_matrix) 3157 3158 Both input matrices must be quadratic and have the same size, and the result 3159 is meaningful only if one of them is the inverse of the other (for instance, 3160 as returned by the method "invert_LR()"). 3161 3162 The number returned is a measure of the "condition" of the given matrix 3163 "C<$matrix>", i.e., a measure of the numerical stability of the matrix. 3164 3165 This number is always positive, and the smaller its value, the better the 3166 condition of the matrix (the better the stability of all subsequent 3167 computations carried out using this matrix). 3168 3169 Numerical stability means for example that if 3170 3171 abs( $vec_correct - $vec_with_error ) < $epsilon 3172 3173 holds, there must be a "C<$delta>" which doesn't depend on the vector 3174 "C<$vec_correct>" (nor "C<$vec_with_error>", by the way) so that 3175 3176 abs( $matrix * $vec_correct - $matrix * $vec_with_error ) < $delta 3177 3178 also holds. 3179 3180 =item * 3181 3182 C<$determinant = $LR_matrix-E<gt>det_LR();> 3183 3184 Calculates the determinant of a matrix, whose LR decomposition matrix 3185 "C<$LR_matrix>" must be given (which must be a (quadratic) matrix 3186 returned by the method "decompose_LR()"). 3187 3188 In fact the determinant is a by-product of the LR decomposition: It is 3189 (in principle, that is, except for the sign) simply the product of the 3190 elements on the main diagonal (elements (1,1), (2,2), (3,3) and so on) 3191 of the LR decomposition matrix. 3192 3193 (The sign is taken care of "magically" by this module) 3194 3195 =item * 3196 3197 C<$order = $LR_matrix-E<gt>order_LR();> 3198 3199 Calculates the order (called "Rang" in German) of a matrix, whose 3200 LR decomposition matrix "C<$LR_matrix>" must be given (which must 3201 be a (quadratic) matrix returned by the method "decompose_LR()"). 3202 3203 This number is a measure of the number of linear independent row 3204 and column vectors (= number of linear independent equations in 3205 the case of a matrix representing an equation system) of the 3206 matrix that was initially fed into "decompose_LR()". 3207 3208 If "n" is the number of rows and columns of the (quadratic!) matrix, 3209 then "n - order" is the dimension of the solution space of the 3210 associated equation system. 3211 3212 =item * 3213 3214 C<$scalar_product = $vector1-E<gt>scalar_product($vector2);> 3215 3216 Returns the scalar product of vector "C<$vector1>" and vector "C<$vector2>". 3217 3218 Both vectors must be column vectors (i.e., a matrix having 3219 several rows but only one column). 3220 3221 This is a (more efficient!) shortcut for 3222 3223 $temp = ~$vector1 * $vector2; 3224 $scalar_product = $temp->element(1,1); 3225 3226 or the sum C<i=1..n> of the products C<vector1[i] * vector2[i]>. 3227 3228 Provided none of the two input vectors is the null vector, then 3229 the two vectors are orthogonal, i.e., have an angle of 90 degrees 3230 between them, exactly when their scalar product is zero, and 3231 vice-versa. 3232 3233 =item * 3234 3235 C<$vector_product = $vector1-E<gt>vector_product($vector2);> 3236 3237 Returns the vector product of vector "C<$vector1>" and vector "C<$vector2>". 3238 3239 Both vectors must be column vectors (i.e., a matrix having several rows 3240 but only one column). 3241 3242 Currently, the vector product is only defined for 3 dimensions (i.e., 3243 vectors with 3 rows); all other vectors trigger an error message. 3244 3245 In 3 dimensions, the vector product of two vectors "x" and "y" 3246 is defined as 3247 3248 | x[1] y[1] e[1] | 3249 determinant | x[2] y[2] e[2] | 3250 | x[3] y[3] e[3] | 3251 3252 where the "C<x[i]>" and "C<y[i]>" are the components of the two vectors 3253 "x" and "y", respectively, and the "C<e[i]>" are unity vectors (i.e., 3254 vectors with a length equal to one) with a one in row "i" and zero's 3255 elsewhere (this means that you have numbers and vectors as elements 3256 in this matrix!). 3257 3258 This determinant evaluates to the rather simple formula 3259 3260 z[1] = x[2] * y[3] - x[3] * y[2] 3261 z[2] = x[3] * y[1] - x[1] * y[3] 3262 z[3] = x[1] * y[2] - x[2] * y[1] 3263 3264 A characteristic property of the vector product is that the resulting 3265 vector is orthogonal to both of the input vectors (if neither of both 3266 is the null vector, otherwise this is trivial), i.e., the scalar product 3267 of each of the input vectors with the resulting vector is always zero. 3268 3269 =item * 3270 3271 C<$length = $vector-E<gt>length();> 3272 3273 This is actually a shortcut for 3274 3275 $length = sqrt( $vector->scalar_product($vector) ); 3276 3277 and returns the length of a given (column!) vector "C<$vector>". 3278 3279 Note that the "length" calculated by this method is in fact the 3280 "two"-norm of a vector "C<$vector>"! 3281 3282 The general definition for norms of vectors is the following: 3283 3284 sub vector_norm 3285 { 3286 croak "Usage: \$norm = \$vector->vector_norm(\$n);" 3287 if (@_ != 2); 3288 3289 my($vector,$n) = @_; 3290 my($rows,$cols) = ($vector->[1],$vector->[2]); 3291 my($k,$comp,$sum); 3292 3293 croak "MatrixReal1::vector_norm(): vector is not a column vector" 3294 unless ($cols == 1); 3295 3296 croak "MatrixReal1::vector_norm(): norm index must be > 0" 3297 unless ($n > 0); 3298 3299 croak "MatrixReal1::vector_norm(): norm index must be integer" 3300 unless ($n == int($n)); 3301 3302 $sum = 0; 3303 for ( $k = 0; $k < $rows; $k++ ) 3304 { 3305 $comp = abs( $vector->[0][$k][0] ); 3306 $sum += $comp ** $n; 3307 } 3308 return( $sum ** (1 / $n) ); 3309 } 3310 3311 Note that the case "n = 1" is the "one"-norm for matrices applied to a 3312 vector, the case "n = 2" is the euclidian norm or length of a vector, 3313 and if "n" goes to infinity, you have the "infinity"- or "maximum"-norm 3314 for matrices applied to a vector! 3315 3316 =item * 3317 3318 C<$xn_vector = $matrix-E<gt>>C<solve_GSM($x0_vector,$b_vector,$epsilon);> 3319 3320 =item * 3321 3322 C<$xn_vector = $matrix-E<gt>>C<solve_SSM($x0_vector,$b_vector,$epsilon);> 3323 3324 =item * 3325 3326 C<$xn_vector = $matrix-E<gt>>C<solve_RM($x0_vector,$b_vector,$weight,$epsilon);> 3327 3328 In some cases it might not be practical or desirable to solve an 3329 equation system "C<A * x = b>" using an analytical algorithm like 3330 the "decompose_LR()" and "solve_LR()" method pair. 3331 3332 In fact in some cases, due to the numerical properties (the "condition") 3333 of the matrix "A", the numerical error of the obtained result can be 3334 greater than by using an approximative (iterative) algorithm like one 3335 of the three implemented here. 3336 3337 All three methods, GSM ("Global Step Method" or "Gesamtschrittverfahren"), 3338 SSM ("Single Step Method" or "Einzelschrittverfahren") and RM ("Relaxation 3339 Method" or "Relaxationsverfahren"), are fix-point iterations, that is, can 3340 be described by an iteration function "C<x(t+1) = Phi( x(t) )>" which has 3341 the property: 3342 3343 Phi(x) = x <==> A * x = b 3344 3345 We can define "C<Phi(x)>" as follows: 3346 3347 Phi(x) := ( En - A ) * x + b 3348 3349 where "En" is a matrix of the same size as "A" ("n" rows and columns) 3350 with one's on its main diagonal and zero's elsewhere. 3351 3352 This function has the required property. 3353 3354 Proof: 3355 3356 A * x = b 3357 3358 <==> -( A * x ) = -b 3359 3360 <==> -( A * x ) + x = -b + x 3361 3362 <==> -( A * x ) + x + b = x 3363 3364 <==> x - ( A * x ) + b = x 3365 3366 <==> ( En - A ) * x + b = x 3367 3368 This last step is true because 3369 3370 x[i] - ( a[i,1] x[1] + ... + a[i,i] x[i] + ... + a[i,n] x[n] ) + b[i] 3371 3372 is the same as 3373 3374 ( -a[i,1] x[1] + ... + (1 - a[i,i]) x[i] + ... + -a[i,n] x[n] ) + b[i] 3375 3376 qed 3377 3378 Note that actually solving the equation system "C<A * x = b>" means 3379 to calculate 3380 3381 a[i,1] x[1] + ... + a[i,i] x[i] + ... + a[i,n] x[n] = b[i] 3382 3383 <==> a[i,i] x[i] = 3384 b[i] 3385 - ( a[i,1] x[1] + ... + a[i,i] x[i] + ... + a[i,n] x[n] ) 3386 + a[i,i] x[i] 3387 3388 <==> x[i] = 3389 ( b[i] 3390 - ( a[i,1] x[1] + ... + a[i,i] x[i] + ... + a[i,n] x[n] ) 3391 + a[i,i] x[i] 3392 ) / a[i,i] 3393 3394 <==> x[i] = 3395 ( b[i] - 3396 ( a[i,1] x[1] + ... + a[i,i-1] x[i-1] + 3397 a[i,i+1] x[i+1] + ... + a[i,n] x[n] ) 3398 ) / a[i,i] 3399 3400 There is one major restriction, though: a fix-point iteration is 3401 guaranteed to converge only if the first derivative of the iteration 3402 function has an absolute value less than one in an area around the 3403 point "C<x(*)>" for which "C<Phi( x(*) ) = x(*)>" is to be true, and 3404 if the start vector "C<x(0)>" lies within that area! 3405 3406 This is best verified grafically, which unfortunately is impossible 3407 to do in this textual documentation! 3408 3409 See literature on Numerical Analysis for details! 3410 3411 In our case, this restriction translates to the following three conditions: 3412 3413 There must exist a norm so that the norm of the matrix of the iteration 3414 function, C<( En - A )>, has a value less than one, the matrix "A" may 3415 not have any zero value on its main diagonal and the initial vector 3416 "C<x(0)>" must be "good enough", i.e., "close enough" to the solution 3417 "C<x(*)>". 3418 3419 (Remember school math: the first derivative of a straight line given by 3420 "C<y = a * x + b>" is "a"!) 3421 3422 The three methods expect a (quadratic!) matrix "C<$matrix>" as their 3423 first argument, a start vector "C<$x0_vector>", a vector "C<$b_vector>" 3424 (which is the vector "b" in your equation system "C<A * x = b>"), in the 3425 case of the "Relaxation Method" ("RM"), a real number "C<$weight>" best 3426 between zero and two, and finally an error limit (real number) "C<$epsilon>". 3427 3428 (Note that the weight "C<$weight>" used by the "Relaxation Method" ("RM") 3429 is B<NOT> checked to lie within any reasonable range!) 3430 3431 The three methods first test the first two conditions of the three 3432 conditions listed above and return an empty list if these conditions 3433 are not fulfilled. 3434 3435 Therefore, you should always test their return value using some 3436 code like: 3437 3438 if ( $xn_vector = $A_matrix->solve_GSM($x0_vector,$b_vector,1E-12) ) 3439 { 3440 # do something with the solution... 3441 } 3442 else 3443 { 3444 # do something with the fact that there is no solution... 3445 } 3446 3447 Otherwise, they iterate until C<abs( Phi(x) - x ) E<lt> epsilon>. 3448 3449 (Beware that theoretically, infinite loops might result if the starting 3450 vector is too far "off" the solution! In practice, this shouldn't be 3451 a problem. Anyway, you can always press <ctrl-C> if you think that the 3452 iteration takes too long!) 3453 3454 The difference between the three methods is the following: 3455 3456 In the "Global Step Method" ("GSM"), the new vector "C<x(t+1)>" 3457 (called "y" here) is calculated from the vector "C<x(t)>" 3458 (called "x" here) according to the formula: 3459 3460 y[i] = 3461 ( b[i] 3462 - ( a[i,1] x[1] + ... + a[i,i-1] x[i-1] + 3463 a[i,i+1] x[i+1] + ... + a[i,n] x[n] ) 3464 ) / a[i,i] 3465 3466 In the "Single Step Method" ("SSM"), the components of the vector 3467 "C<x(t+1)>" which have already been calculated are used to calculate 3468 the remaining components, i.e. 3469 3470 y[i] = 3471 ( b[i] 3472 - ( a[i,1] y[1] + ... + a[i,i-1] y[i-1] + # note the "y[]"! 3473 a[i,i+1] x[i+1] + ... + a[i,n] x[n] ) # note the "x[]"! 3474 ) / a[i,i] 3475 3476 In the "Relaxation method" ("RM"), the components of the vector 3477 "C<x(t+1)>" are calculated by "mixing" old and new value (like 3478 cold and hot water), and the weight "C<$weight>" determines the 3479 "aperture" of both the "hot water tap" as well as of the "cold 3480 water tap", according to the formula: 3481 3482 y[i] = 3483 ( b[i] 3484 - ( a[i,1] y[1] + ... + a[i,i-1] y[i-1] + # note the "y[]"! 3485 a[i,i+1] x[i+1] + ... + a[i,n] x[n] ) # note the "x[]"! 3486 ) / a[i,i] 3487 y[i] = weight * y[i] + (1 - weight) * x[i] 3488 3489 Note that the weight "C<$weight>" should be greater than zero and 3490 less than two (!). 3491 3492 The three methods are supposed to be of different efficiency. 3493 Experiment! 3494 3495 Remember that in most cases, it is probably advantageous to first 3496 "normalize()" your equation system prior to solving it! 3497 3498 =back 3499 3500 =head2 Eigensystems 3501 3502 =over 2 3503 3504 =item * 3505 3506 C<$matrix-E<gt>is_symmetric();> 3507 3508 Returns a boolean value indicating if the given matrix is 3509 symmetric (B<M>[I<i>,I<j>]=B<M>[I<j>,I<i>]). This is equivalent to 3510 C<($matrix == ~$matrix)> but without memory allocation. 3511 3512 =item * 3513 3514 C<($l, $V) = $matrix-E<gt>sym_diagonalize();> 3515 3516 This method performs the diagonalization of the quadratic 3517 I<symmetric> matrix B<M> stored in $matrix. 3518 On output, B<l> is a column vector containing all the eigenvalues 3519 of B<M> and B<V> is an orthogonal matrix which columns are the 3520 corresponding normalized eigenvectors. 3521 The primary property of an eigenvalue I<l> and an eigenvector 3522 B<x> is of course that: B<M> * B<x> = I<l> * B<x>. 3523 3524 The method uses a Householder reduction to tridiagonal form 3525 followed by a QL algoritm with implicit shifts on this 3526 tridiagonal. (The tridiagonal matrix is kept internally 3527 in a compact form in this routine to save memory.) 3528 In fact, this routine wraps the householder() and 3529 tri_diagonalize() methods described below when their 3530 intermediate results are not desired. 3531 The overall algorithmic complexity of this technique 3532 is O(N^3). According to several books, the coefficient 3533 hidden by the 'O' is one of the best possible for general 3534 (symmetric) matrixes. 3535 3536 =item * 3537 3538 C<($T, $Q) = $matrix-E<gt>householder();> 3539 3540 This method performs the Householder algorithm which reduces 3541 the I<n> by I<n> real I<symmetric> matrix B<M> contained 3542 in $matrix to tridiagonal form. 3543 On output, B<T> is a symmetric tridiagonal matrix (only 3544 diagonal and off-diagonal elements are non-zero) and B<Q> 3545 is an I<orthogonal> matrix performing the tranformation 3546 between B<M> and B<T> (C<$M == $Q * $T * ~$Q>). 3547 3548 =item * 3549 3550 C<($l, $V) = $T-E<gt>tri_diagonalize([$Q]);> 3551 3552 This method diagonalizes the symmetric tridiagonal 3553 matrix B<T>. On output, $l and $V are similar to the 3554 output values described for sym_diagonalize(). 3555 3556 The optional argument $Q corresponds to an orthogonal 3557 transformation matrix B<Q> that should be used additionally 3558 during B<V> (eigenvectors) computation. It should be supplied 3559 if the desired eigenvectors correspond to a more general 3560 symmetric matrix B<M> previously reduced by the 3561 householder() method, not a mere tridiagonal. If B<T> is 3562 really a tridiagonal matrix, B<Q> can be omitted (it 3563 will be internally created in fact as an identity matrix). 3564 The method uses a QL algorithm (with implicit shifts). 3565 3566 =item * 3567 3568 C<$l = $matrix-E<gt>sym_eigenvalues();> 3569 3570 This method computes the eigenvalues of the quadratic 3571 I<symmetric> matrix B<M> stored in $matrix. 3572 On output, B<l> is a column vector containing all the eigenvalues 3573 of B<M>. Eigenvectors are not computed (on the contrary of 3574 C<sym_diagonalize()>) and this method is more efficient 3575 (even though it uses a similar algorithm with two phases). 3576 However, understand that the algorithmic complexity of this 3577 technique is still also O(N^3). But the coefficient hidden 3578 by the 'O' is better by a factor of..., well, see your 3579 benchmark, it's wiser. 3580 3581 This routine wraps the householder_tridiagonal() and 3582 tri_eigenvalues() methods described below when the 3583 intermediate tridiagonal matrix is not needed. 3584 3585 =item * 3586 3587 C<$T = $matrix-E<gt>householder_tridiagonal();> 3588 3589 This method performs the Householder algorithm which reduces 3590 the I<n> by I<n> real I<symmetric> matrix B<M> contained 3591 in $matrix to tridiagonal form. 3592 On output, B<T> is the obtained symmetric tridiagonal matrix 3593 (only diagonal and off-diagonal elements are non-zero). The 3594 operation is similar to the householder() method, but potentially 3595 a little more efficient as the transformation matrix is not 3596 computed. 3597 3598 =item * 3599 3600 C<$l = $T-E<gt>tri_eigenvalues();> 3601 3602 This method compute the eigenvalues of the symmetric 3603 tridiagonal matrix B<T>. On output, $l is a vector 3604 containing the eigenvalues (similar to C<sym_eigenvalues()>). 3605 This method is much more efficient than tri_diagonalize() 3606 when eigenvectors are not needed. 3607 3608 =back 3609 3610 =head1 OVERLOADED OPERATORS 3611 3612 =head2 SYNOPSIS 3613 3614 =over 2 3615 3616 =item * 3617 3618 Unary operators: 3619 3620 "C<->", "C<~>", "C<abs>", C<test>, "C<!>", 'C<"">' 3621 3622 =item * 3623 3624 Binary (arithmetic) operators: 3625 3626 "C<+>", "C<->", "C<*>" 3627 3628 =item * 3629 3630 Binary (relational) operators: 3631 3632 "C<==>", "C<!=>", "C<E<lt>>", "C<E<lt>=>", "C<E<gt>>", "C<E<gt>=>" 3633 3634 "C<eq>", "C<ne>", "C<lt>", "C<le>", "C<gt>", "C<ge>" 3635 3636 Note that the latter ("C<eq>", "C<ne>", ... ) are just synonyms 3637 of the former ("C<==>", "C<!=>", ... ), defined for convenience 3638 only. 3639 3640 =back 3641 3642 =head2 DESCRIPTION 3643 3644 =over 5 3645 3646 =item '-' 3647 3648 Unary minus 3649 3650 Returns the negative of the given matrix, i.e., the matrix with 3651 all elements multiplied with the factor "-1". 3652 3653 Example: 3654 3655 $matrix = -$matrix; 3656 3657 =item '~' 3658 3659 Transposition 3660 3661 Returns the transposed of the given matrix. 3662 3663 Examples: 3664 3665 $temp = ~$vector * $vector; 3666 $length = sqrt( $temp->element(1,1) ); 3667 3668 if (~$matrix == $matrix) { # matrix is symmetric ... } 3669 3670 =item abs 3671 3672 Norm 3673 3674 Returns the "one"-Norm of the given matrix. 3675 3676 Example: 3677 3678 $error = abs( $A * $x - $b ); 3679 3680 =item test 3681 3682 Boolean test 3683 3684 Tests wether there is at least one non-zero element in the matrix. 3685 3686 Example: 3687 3688 if ($xn_vector) { # result of iteration is not zero ... } 3689 3690 =item '!' 3691 3692 Negated boolean test 3693 3694 Tests wether the matrix contains only zero's. 3695 3696 Examples: 3697 3698 if (! $b_vector) { # heterogenous equation system ... } 3699 else { # homogenous equation system ... } 3700 3701 unless ($x_vector) { # not the null-vector! } 3702 3703 =item '""""' 3704 3705 "Stringify" operator 3706 3707 Converts the given matrix into a string. 3708 3709 Uses scientific representation to keep precision loss to a minimum in case 3710 you want to read this string back in again later with "new_from_string()". 3711 3712 Uses a 13-digit mantissa and a 20-character field for each element so that 3713 lines will wrap nicely on an 80-column screen. 3714 3715 Examples: 3716 3717 $matrix = MatrixReal1->new_from_string(<<"MATRIX"); 3718 [ 1 0 ] 3719 [ 0 -1 ] 3720 MATRIX 3721 print "$matrix"; 3722 3723 [ 1.000000000000E+00 0.000000000000E+00 ] 3724 [ 0.000000000000E+00 -1.000000000000E+00 ] 3725 3726 $string = "$matrix"; 3727 $test = MatrixReal1->new_from_string($string); 3728 if ($test == $matrix) { print ":-)\n"; } else { print ":-(\n"; } 3729 3730 =item '+' 3731 3732 Addition 3733 3734 Returns the sum of the two given matrices. 3735 3736 Examples: 3737 3738 $matrix_S = $matrix_A + $matrix_B; 3739 3740 $matrix_A += $matrix_B; 3741 3742 =item '-' 3743 3744 Subtraction 3745 3746 Returns the difference of the two given matrices. 3747 3748 Examples: 3749 3750 $matrix_D = $matrix_A - $matrix_B; 3751 3752 $matrix_A -= $matrix_B; 3753 3754 Note that this is the same as: 3755 3756 $matrix_S = $matrix_A + -$matrix_B; 3757 3758 $matrix_A += -$matrix_B; 3759 3760 (The latter are less efficient, though) 3761 3762 =item '*' 3763 3764 Multiplication 3765 3766 Returns the matrix product of the two given matrices or 3767 the product of the given matrix and scalar factor. 3768 3769 Examples: 3770 3771 $matrix_P = $matrix_A * $matrix_B; 3772 3773 $matrix_A *= $matrix_B; 3774 3775 $vector_b = $matrix_A * $vector_x; 3776 3777 $matrix_B = -1 * $matrix_A; 3778 3779 $matrix_B = $matrix_A * -1; 3780 3781 $matrix_A *= -1; 3782 3783 =item '==' 3784 3785 Equality 3786 3787 Tests two matrices for equality. 3788 3789 Example: 3790 3791 if ( $A * $x == $b ) { print "EUREKA!\n"; } 3792 3793 Note that in most cases, due to numerical errors (due to the finite 3794 precision of computer arithmetics), it is a bad idea to compare two 3795 matrices or vectors this way. 3796 3797 Better use the norm of the difference of the two matrices you want 3798 to compare and compare that norm with a small number, like this: 3799 3800 if ( abs( $A * $x - $b ) < 1E-12 ) { print "BINGO!\n"; } 3801 3802 =item '!=' 3803 3804 Inequality 3805 3806 Tests two matrices for inequality. 3807 3808 Example: 3809 3810 while ($x0_vector != $xn_vector) { # proceed with iteration ... } 3811 3812 (Stops when the iteration becomes stationary) 3813 3814 Note that (just like with the '==' operator), it is usually a bad idea 3815 to compare matrices or vectors this way. Compare the norm of the difference 3816 of the two matrices with a small number instead. 3817 3818 =item 'E<lt>' 3819 3820 Less than 3821 3822 Examples: 3823 3824 if ( $matrix1 < $matrix2 ) { # ... } 3825 3826 if ( $vector < $epsilon ) { # ... } 3827 3828 if ( 1E-12 < $vector ) { # ... } 3829 3830 if ( $A * $x - $b < 1E-12 ) { # ... } 3831 3832 These are just shortcuts for saying: 3833 3834 if ( abs($matrix1) < abs($matrix2) ) { # ... } 3835 3836 if ( abs($vector) < abs($epsilon) ) { # ... } 3837 3838 if ( abs(1E-12) < abs($vector) ) { # ... } 3839 3840 if ( abs( $A * $x - $b ) < abs(1E-12) ) { # ... } 3841 3842 Uses the "one"-norm for matrices and Perl's built-in "abs()" for scalars. 3843 3844 =item 'E<lt>=' 3845 3846 Less than or equal 3847 3848 As with the '<' operator, this is just a shortcut for the same expression 3849 with "abs()" around all arguments. 3850 3851 Example: 3852 3853 if ( $A * $x - $b <= 1E-12 ) { # ... } 3854 3855 which in fact is the same as: 3856 3857 if ( abs( $A * $x - $b ) <= abs(1E-12) ) { # ... } 3858 3859 Uses the "one"-norm for matrices and Perl's built-in "abs()" for scalars. 3860 3861 =item 'E<gt>' 3862 3863 Greater than 3864 3865 As with the '<' and '<=' operator, this 3866 3867 if ( $xn - $x0 > 1E-12 ) { # ... } 3868 3869 is just a shortcut for: 3870 3871 if ( abs( $xn - $x0 ) > abs(1E-12) ) { # ... } 3872 3873 Uses the "one"-norm for matrices and Perl's built-in "abs()" for scalars. 3874 3875 =item 'E<gt>=' 3876 3877 Greater than or equal 3878 3879 As with the '<', '<=' and '>' operator, the following 3880 3881 if ( $LR >= $A ) { # ... } 3882 3883 is simply a shortcut for: 3884 3885 if ( abs($LR) >= abs($A) ) { # ... } 3886 3887 Uses the "one"-norm for matrices and Perl's built-in "abs()" for scalars. 3888 3889 =back 3890 3891 =head1 SEE ALSO 3892 3893 Math::MatrixBool(3), DFA::Kleene(3), Math::Kleene(3), 3894 Set::IntegerRange(3), Set::IntegerFast(3). 3895 3896 =head1 VERSION 3897 3898 This man page documents MatrixReal1 version 1.3. 3899 3900 =head1 AUTHORS 3901 3902 Steffen Beyer <sb@sdm.de>, Rodolphe Ortalo <ortalo@laas.fr>. 3903 3904 =head1 CREDITS 3905 3906 Many thanks to Prof. Pahlings for stoking the fire of my enthusiasm for 3907 Algebra and Linear Algebra at the university (RWTH Aachen, Germany), and 3908 to Prof. Esser and his assistant, Mr. Jarausch, for their fascinating 3909 lectures in Numerical Analysis! 3910 3911 =head1 COPYRIGHT 3912 3913 Copyright (c) 1996, 1997, 1999 by Steffen Beyer and Rodolphe Ortalo. 3914 All rights reserved. 3915 3916 =head1 LICENSE AGREEMENT 3917 3918 This package is free software; you can redistribute it and/or 3919 modify it under the same terms as Perl itself. 3920
| aubreyja at gmail dot com | ViewVC Help |
| Powered by ViewVC 1.0.9 |