PageRenderTime 63ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

/MatrixSparse.pm

https://github.com/gitpan/Math-MatrixSparse
Perl | 3600 lines | 2851 code | 658 blank | 91 comment | 316 complexity | d6af8b8fbc5bd482abf7c0722187a6e4 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. package Math::MatrixSparse;
  2. use 5.006;
  3. use strict;
  4. use warnings;
  5. use Carp;
  6. require Exporter;
  7. our @ISA = qw(Exporter);
  8. # Items to export into callers namespace by default. Note: do not export
  9. # names by default without a very good reason. Use EXPORT_OK instead.
  10. # Do not simply export all your public functions/methods/constants.
  11. # This allows declaration use Math::MatrixSparse ':all';
  12. # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
  13. # will save memory.
  14. our %EXPORT_TAGS = ( 'all' => [ qw(
  15. splitkey
  16. makekey
  17. ) ] );
  18. our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} });
  19. our @EXPORT = qw(
  20. );
  21. our $VERSION = '0.01';
  22. use overload
  23. "+" => "add",
  24. "*" => "quickmultiply",
  25. "x" => "kronecker",
  26. "-" => "subtract",
  27. "neg" => "negate",
  28. "~" => "transpose",
  29. "**" => "exponentiate",
  30. "==" => "equals",
  31. '""' => "print",
  32. "&" => "matrixand",
  33. "|" => "matrixor",
  34. "fallback" => undef;
  35. # Preloaded methods go here.
  36. ### CREATION METHODS
  37. sub new {
  38. my ($proto,$name) = @_;
  39. my $this;
  40. $this->{data} = undef;
  41. $this->{name} = $name;
  42. $this->{rows} = 0;
  43. $this->{columns} =0;
  44. $this->{special}->{bandwidth} = 0;
  45. $this->{special}->{structure} = "";
  46. $this->{special}->{shape} = "";
  47. $this->{special}->{sign} = "";
  48. $this->{special}->{pattern} = 0;
  49. $this->{special}->{field} = "real";
  50. bless ($this);
  51. return $this;
  52. }
  53. sub newfromstring {
  54. my ($proto, $string,$name) = @_;
  55. my $this=new Math::MatrixSparse;
  56. my @entries = split(/\n/,$string);
  57. $this->{special}->{structure} = ".";
  58. foreach my $entry (@entries) {
  59. my ($i,$j,$value) = $entry=~m/^\s*(\d+)\s+(\d+)\s+(.+)\s*$/;
  60. $this->assign($i,$j,$value);
  61. }
  62. $this->{name} =$name;
  63. $this->{special}->{field} = "real";
  64. return $this;
  65. }
  66. sub newdiag {
  67. my ($proto, $diag, $name) = @_;
  68. my @data = @{$diag};
  69. my $this = new Math::MatrixSparse;
  70. $this->name($name);
  71. my $i=1;
  72. foreach my $entry (@data){
  73. $this->assign($i,$i,$entry);
  74. $i++;
  75. }
  76. $this->{special}->{structure} = "symmetric";
  77. $this->{special}->{shape} = "diagonal";
  78. $this->{special}->{field} = "real";
  79. $this->{special}->{square} = 1;
  80. return $this;
  81. }
  82. sub newdiagfromstring {
  83. my ($proto, $string, $name) = @_;
  84. my $this=new Math::MatrixSparse;
  85. my @entries = split(/\n/,$string);
  86. foreach my $entry (@entries) {
  87. my ($i,$value) = $entry=~m/^(\d+)\s+(.+)$/;
  88. $this->assign($i,$i,$value);
  89. }
  90. $this->{name} =$name;
  91. $this->{special}->{structure} = "symmetric";
  92. $this->{special}->{field} = "real";
  93. $this->{special}->{shape} = "diagonal";
  94. $this->{special}->{square} = 1;
  95. return $this;
  96. }
  97. sub newidentity {
  98. my ($proto, $n,$m) = @_;
  99. my $this = new Math::MatrixSparse;
  100. $m=$n unless ($m);
  101. $this->{name} = "I$n";
  102. my $min = $m<$n ? $m :$n;
  103. for my $i (1..$min) {
  104. $this->assign($i,$i,1);
  105. }
  106. $this->{rows} = $n;
  107. $this->{columns} = $m;
  108. $this->{special}->{structure} = "symmetric";
  109. $this->{special}->{field} = "real";
  110. $this->{special}->{shape} = "diagonal";
  111. $this->{special}->{sign} = "nonnegative";
  112. $this->{special}->{square} = 1 if $m==$n;
  113. $this->{special}->{pattern} = 0;
  114. return $this;
  115. }
  116. sub newrandom {
  117. my ($proto,$maxrow,$maxcol,$max,$density,$name) = @_;
  118. $maxcol = $maxrow unless defined $maxcol;
  119. $density = 1 unless (defined $density)&&($density>=0) && ($density<=1);
  120. my $stoch = new Math::MatrixSparse($name);
  121. unless (defined $max) {
  122. for my $i (1..$maxrow) {
  123. for my $j (1..$maxcol) {
  124. my $uniform = rand;
  125. next unless $uniform<=$density;
  126. $stoch->assign($i,$j,rand);
  127. }
  128. }
  129. return $stoch;
  130. }
  131. $name = "" unless defined $name;
  132. $stoch->assign($maxrow,$maxcol,0);
  133. my $k = 1;
  134. while ($k<=$max) {
  135. my ($i,$j) = (int($maxrow* rand)+1,int($maxcol* rand)+1);
  136. next if $stoch->element($i,$j);
  137. my $uniform = rand;
  138. if ($uniform>$density) {
  139. carp "Math::MatrixReal::newrandom ignoring element";
  140. }
  141. next unless $uniform <= $density;
  142. $stoch->assign($i,$j,rand);
  143. $k++;
  144. }
  145. $stoch->{special}->{sign}="nonnegative";
  146. return $stoch;
  147. }
  148. ### INPUT-OUTPUT METHODS
  149. sub newharwellboeing {
  150. my ($proto, $filename) = @_;
  151. open(HB,"<$filename" ) || croak "Math::MatrixSparse::newharwellboeing Can't open $filename\n";
  152. my $this = new Math::MatrixSparse;
  153. $_ = <HB>;
  154. chomp();
  155. my ($ident, $key) = $_ =~ m/^(.*)(........)\s*$/;
  156. $key =~ s/\s*$//;
  157. $this->{name} = $key;
  158. $_ = <HB>;
  159. chomp();
  160. my ($lines, $pline, $rline, $nvline, $rhsline) = $_ =~
  161. m/^\s*(\d{1,14})\s*(\d{1,14})\s*(\d{1,14})\s*(\d{1,14})\s*(\d{1,14})\s*$/;
  162. $_ = <HB>;
  163. chomp();
  164. my ($mattype, $rows, $columns, $rvi, $eme) = $_ =~
  165. m/^\s*(...)\s*(\d{1,14})\s*(\d{1,14})\s*(\d{1,14})\s*(\d{1,14})\s*$/;
  166. if ($mattype =~ /^C/) {
  167. croak "Math::MatrixSparse::newharwellboeing Complex types not implemented, exiting\n";
  168. } else {
  169. $this->{special}->{field} = "real";
  170. }
  171. $this->{rows} = $rows;
  172. $this->{columns} = $columns;
  173. my $formatline = <HB>;
  174. chomp($formatline);
  175. my ($colft, $rowft, $valft,$rhsft) = $formatline =~ m/(\([a-zA-Z0-9.+-+]+\))/g;
  176. unless (($colft =~ /I/i)&&($rowft =~ /I/i)) {
  177. carp "Math::MatrixSparse::newharwellboeing non-integer format for rows and columns";
  178. }
  179. $valft =~ s/\d+P//g;
  180. my ($valrep, $valsize) = $valft =~ m/(\d+)[a-zA-Z](\d+)/;
  181. my $valregex = '.' x $valsize;
  182. my $rhsspec;
  183. if ($rhsline) {
  184. $rhsspec = <HB>;
  185. chomp($rhsspec);
  186. }
  187. #now read the column pointer data...
  188. my @colpointers;
  189. for my $i (1..$pline) {
  190. $_ = <HB>;
  191. s/^\s*//;
  192. s/\s*$//;
  193. push @colpointers, split(/\s+/);
  194. }
  195. #...and the row data...
  196. my @rowindex;
  197. for my $i (1..$rline) {
  198. $_ = <HB>;
  199. s/^\s*//;
  200. s/\s*$//;
  201. push @rowindex, split(/\s+/);
  202. }
  203. #...and any value data. If the matrix is a pattern type, fill
  204. # @values with ones.
  205. my @values;
  206. if ($mattype =~ m/^P..$/i) {
  207. @values = (1) x $rvi;
  208. } else {
  209. for my $i (1..$nvline) {
  210. $_ = <HB>;
  211. s/D/e/g;
  212. push @values, map {s/\s//g; $_+0} m/$valregex/g;
  213. }
  214. }
  215. my $curcol = 1;
  216. foreach my $i (0..($#colpointers-1)) {
  217. my $thiscol = $colpointers[$i];
  218. my $nextcol = $colpointers[$i+1];
  219. my @rowslice = @rowindex[$thiscol-1..$nextcol-2];
  220. my @valslice = @values[$thiscol-1..$nextcol-2];
  221. foreach my $j (0..$#rowslice) {
  222. $this->assign($rowslice[$j],$curcol,$valslice[$j]);
  223. }
  224. $curcol++;
  225. }
  226. if ($mattype =~ /^.S.$/) {
  227. $this->_symmetrify();
  228. } elsif ($mattype =~ /^.Z.$/) {
  229. $this->_skewsymmetrify();
  230. }
  231. return $this;
  232. }
  233. sub newmatrixmarket {
  234. my ($proto, $filename) = @_;
  235. my $this = new Math::MatrixSparse;
  236. confess "Math::MatrixSparse::newmatrixmarket undefined filename" unless defined $filename;
  237. open(MM,"<$filename" ) || croak "Math::MatrixSparse::newmatrixmarket Can't open $filename for reading";
  238. $_=<MM>;
  239. unless (/^\%\%MatrixMarket/) {
  240. confess "Math::MatrixSparse::newmatrixmarket Invalid start of file";
  241. }
  242. unless (/coordinate/i) {
  243. confess "Math::MatrixSparse::newmatrixmarket dense format not implemented";
  244. }
  245. if (/complex/i) {
  246. carp "Math::MatrixSparse::newmatrixmarket Complex matrices not implemented, ignoring imaginary part\n";
  247. $this->{special}->{field} = "real";
  248. } else {
  249. $this->{special}->{field} = "real";
  250. }
  251. my $specifications = $_;
  252. my $ispattern;
  253. my $issymmetric;
  254. my $isskewsymmetric;
  255. if ($specifications =~ /pattern/i) {
  256. $ispattern = 1;
  257. $this->{special}->{pattern} = 1;
  258. }
  259. $this->{name} = $filename;
  260. my $startdata=0;
  261. my $entries =0;
  262. my ($rows,$columns);
  263. while (<MM>) {
  264. next if /^\%/;
  265. unless ($startdata) {
  266. s/^\s*//;
  267. s/\s*$//;
  268. ($rows,$columns,$entries) = split(/\s+/);
  269. $this->{rows} = $rows;
  270. $this->{columns} = $columns;
  271. $startdata = 1;
  272. } else {
  273. s/^\s*//;
  274. s/\s*$//;
  275. ($rows,$columns,$entries) = split(/\s+/);
  276. $entries = 1 if $ispattern;
  277. $this->assign($rows,$columns,$entries);
  278. }
  279. }
  280. if ($specifications =~ /\Wsymmetric/i) {
  281. $this->{special}->{structure} = "symmetric";
  282. return $this->symmetrify();
  283. }
  284. if ($specifications =~ /skewsymmetric/i) {
  285. $this->{special}->{structure} = "skewsymmetric";
  286. return $this->skewsymmetrify();
  287. }
  288. # if ($specifications =~ /hermetian/i) {
  289. # $this->{special}->{structure} = "hermetian";
  290. # return $this->symmetrify();
  291. # }
  292. return $this;
  293. }
  294. sub writematrixmarket {
  295. my ($matrix, $filename) = @_;
  296. open(MM,">$filename" ) || croak "Math::MatrixSparse::newmatrixmarket Can't open $filename for writing";
  297. print MM '%%MatrixMarket matrix coordinate ';
  298. print MM $matrix->{special}->{pattern} ? "pattern " : "real ";
  299. if ($matrix->{special}->{structure} =~ m/^symmetric/i) {
  300. $matrix=$matrix->symmetrify();
  301. print MM "symmetric";
  302. } elsif ($matrix->{special}->{structure} =~ m/skewsymmetric/i) {
  303. $matrix=$matrix->skewsymmetrify();
  304. print MM "skewsymmetric";
  305. } else {
  306. print MM "general\n";
  307. }
  308. print MM "$matrix->{rows} $matrix->{columns} ";
  309. print MM $matrix->count() , "\n";
  310. if ($matrix->{special}->{pattern}) {
  311. foreach my $key ($matrix->sortbycolumn()) {
  312. my ($i,$j) = &splitkey($key);
  313. next unless $matrix->element($i,$j);
  314. print MM "$i $j\n";
  315. }
  316. } else {
  317. foreach my $key ($matrix->sortbycolumn()) {
  318. my ($i,$j) = &splitkey($key);
  319. print MM "$i $j ", $matrix->{data}{$key}, "\n";
  320. }
  321. }
  322. return;
  323. }
  324. sub copy {
  325. my ($proto,$name) = @_;
  326. my $this = new Math::MatrixSparse;
  327. return $this unless defined $proto;
  328. if (defined $proto->{data}) {
  329. %{$this->{data}} = %{$proto->{data}};
  330. }
  331. %{$this->{special}} = %{$proto->{special}};
  332. $this->{name} = defined $name ? $name : $proto->{name};
  333. $this->{rows} = $proto->{rows};
  334. $this->{columns} = $proto->{columns};
  335. return $this;
  336. }
  337. sub name {
  338. my ($object,$name) = @_;
  339. $object->{name} = $name;
  340. return $name;
  341. }
  342. ### INSERTION AND LOOKUP METHODS
  343. sub assign {
  344. my ($object, $i,$j,$x)=@_;
  345. return undef unless ((defined $i) && (defined $j)&&(defined $object));
  346. $x = 1 if $object->{special}->{pattern};
  347. return undef unless defined $x;
  348. #update matrix's shape if necessary.
  349. if ((defined $object->{special}) &&
  350. ($object->{special}->{shape} =~ m/diagonal/i) &&
  351. ($i!= $j) ) {
  352. if ($i<$j) {
  353. $object->{special}->{shape} = "upper"
  354. } else {
  355. $object->{special}->{shape} = "lower"
  356. }
  357. } elsif (($object->{special}->{shape} =~ m/strictlower/i)
  358. && ($i<= $j) ) {
  359. if ($i==$j) {
  360. $object->{special}->{shape} = "lower";
  361. } else {
  362. $object->{special}->{shape}="";
  363. }
  364. } elsif (($object->{special}->{shape} =~ m/strictupper/i)
  365. && ($i>= $j) ) {
  366. if ($i==$j) {
  367. $object->{special}->{shape} = "upper";
  368. } else {
  369. $object->{special}->{shape}="";
  370. }
  371. } elsif (($object->{special}->{shape} =~ m/^lower/i) && ($i< $j) ) {
  372. $object->{special}->{shape}="";
  373. } elsif (($object->{special}->{shape} =~ m/^upper/i) && ($i= $j) ) {
  374. $object->{special}->{shape}="";
  375. }
  376. $object->{special}->{pattern} = 0 unless ($x==1);
  377. #update bandwidth
  378. if (abs($i-$j) > $object->{special}->{bandwidth}) {
  379. $object->{special}->{bandwidth} = abs($i-$j);
  380. }
  381. #update symmetric and skew-symmetric structure if necessary
  382. if (
  383. ($i!=$j) && ( defined $object->{special}->{structure}) &&
  384. (
  385. ($object->{special}->{structure} =~ /symmetric/i)
  386. # ||($object->{special}->{structure} =~ /hermetian/i)
  387. )
  388. ) {
  389. $object->{special}->{structure} = "";
  390. } elsif (($i==$j)&&
  391. ($object->{special}->{structure} =~ /skewsymmetric/i)&&
  392. ($x)) {
  393. #skew-symmetric matrices must have zero diagonal
  394. $object->{special}->{structure} = "";
  395. }
  396. #update sign if necessary
  397. if ( ($object->{special}->{sign} =~ /^positive/) && ($x<=0) ) {
  398. if ($x<0) {
  399. $object->{special}->{sign}="";
  400. } else {
  401. $object->{special}->{sign} = "nonnegative";
  402. }
  403. } elsif ( ($object->{special}->{sign} =~ /^negative/) && ($x>=0) ) {
  404. if ($x>0) {
  405. $object->{special}->{sign}="";
  406. } else {
  407. $object->{special}->{sign} = "nonpositive";
  408. }
  409. } elsif ( ($object->{special}->{sign} =~ /nonnegative/i) && ($x<0) ) {
  410. $object->{special}->{sign}="";
  411. } elsif ( ($object->{special}->{sign} =~ /nonpositive/i) && ($x>0) ) {
  412. $object->{special}->{sign}="";
  413. }
  414. my $key = &makekey($i,$j);
  415. delete $object->{sortedrows};
  416. delete $object->{sortedcolumns};
  417. delete $object->{data}->{$key};
  418. $object->{data}->{$key} = $x;
  419. #update size of matrix, and squareness, if necessary
  420. $object->{rows} = $i if ($i>$object->{rows});
  421. $object->{columns} = $j if ($j>$object->{columns});
  422. $object->{special}->{square} = ( $object->{columns}==$object->{rows});
  423. return $x;
  424. }
  425. sub assignspecial {
  426. #as assign, except that it respects special properties of
  427. #the matrix. For example, symmetrical matrices are kept symmetric.
  428. my ($object, $i,$j,$x)=@_;
  429. return undef unless ((defined $i) && (defined $j)&&(defined $object));
  430. $x = 1 if $object->{special}->{pattern};
  431. return undef unless defined $x;
  432. my $key = &makekey($i,$j);
  433. my $revkey = &makekey($j,$i);
  434. if ($object->{special}->{structure} =~ m/^symmetric/i) {
  435. if ($i==$j) {
  436. $object->{data}{$key} = $x;
  437. } else {
  438. $object->{data}{$key} = $x;
  439. $object->{data}{$revkey} = $x;
  440. }
  441. } elsif ($object->{special}->{structure} =~ m/^symmetric/i) {
  442. if (($i==$j)&&($x)) {
  443. croak "Math::MatrixSparse::assignspecial skewsymmetric matrices must have zero diagonal";
  444. } else {
  445. $object->{data}{$key} = $x;
  446. $object->{data}{$revkey} = -$x;
  447. }
  448. } else {
  449. $object->assign($i,$j,$x);
  450. }
  451. return $x;
  452. }
  453. sub assignkey {
  454. my ($object, $key,$x)=@_;
  455. my ($i,$j) = &splitkey($key);
  456. return unless ((defined $i) && (defined $j));
  457. $object->assign($i,$j,$x);
  458. return $x;
  459. }
  460. sub element {
  461. my ($object, $i,$j) = @_;
  462. my $key = &makekey($i,$j);
  463. if (defined $object->{data}{$key}) {
  464. return $object->{data}{$key};
  465. } else {
  466. return 0;
  467. }
  468. }
  469. sub elementkey {
  470. my ($object, $key) = @_;
  471. if (defined $object->{data}{$key}) {
  472. return $object->{data}{$key};
  473. } else {
  474. return 0;
  475. }
  476. }
  477. sub elements {
  478. my ($matrix) = @_;
  479. return keys %{$matrix->{data}};
  480. }
  481. #returns row number $row of matrix as a row matrix
  482. sub row {
  483. my ($matrix, $row,$persist) = @_;
  484. my $matrow = new Math::MatrixSparse;
  485. $matrow->{columns} = $matrix->{columns};
  486. $matrow->{rows} = 1;
  487. $matrow->name($matrix->{name});
  488. reuturn $matrow unless $row;
  489. if ($persist) {
  490. @{$matrix->{sortedrows}} = $matrix->sortbyrow()
  491. unless defined $matrix->{sortedrows};
  492. }
  493. if (defined $matrix->{sortedrows}) {
  494. #binary search for proper values
  495. my @rows = @{$matrix->{sortedrows}};
  496. my ($left,$right) = (0,$#rows);
  497. my $mid = int(($right+$left)/2.0);
  498. while (
  499. ((&splitkey($rows[$mid]))[0] != $row )
  500. && ($right-$left>0)
  501. ) {
  502. if ((&splitkey($rows[$mid]))[0] < $row) {
  503. $left = $mid;
  504. } else {
  505. $right = $mid;
  506. }
  507. $mid = int(($right+$left)/2.0);
  508. }
  509. return $matrow unless (&splitkey($rows[$mid]))[0]==$row;
  510. $right = $mid;
  511. while ( ($right<=$#rows)&&
  512. ((&splitkey($rows[$right]))[0]==$row)
  513. )
  514. {
  515. $matrow->assign(1,
  516. (&splitkey($rows[$right]))[1],
  517. $matrix->elementkey($rows[$right]));
  518. $right++;
  519. }
  520. $left = $mid-1;
  521. while ( ($left) &&
  522. ((&splitkey($rows[$left]))[0]==$row)
  523. ){
  524. $matrow->assign(1,
  525. (&splitkey($rows[$left]))[1],
  526. $matrix->elementkey($rows[$left]));
  527. $left--;
  528. }
  529. return $matrow;
  530. }
  531. foreach my $key (keys %{$matrix->{data}}){
  532. my ($i,$j) = &splitkey($key);
  533. $matrow->assignkey($key, $matrix->{data}{$key}) if ($i==$row);
  534. }
  535. return $matrow;
  536. }
  537. #returns column number $col of matrix as a column matrix
  538. sub column {
  539. my ($matrix, $col,$persist) = @_;
  540. my $matcol = new Math::MatrixSparse;
  541. $matcol->{columns} = $matrix->{columns};
  542. $matcol->{cols} = 1;
  543. $matcol->name($matrix->{name});
  544. if ($persist) {
  545. @{$matrix->{sortedcols}} = $matrix->sortbycolumn() unless defined $matrix->{sortedcols};
  546. }
  547. if (defined $matrix->{sortedcols}) {
  548. #binary search for proper values
  549. my @cols = @{$matrix->{sortedcols}};
  550. my ($left,$right) = (0,$#cols);
  551. my $mid = int(($right+$left)/2.0);
  552. while (
  553. ((&splitkey($cols[$mid]))[1] != $col )
  554. && ($right-$left>0)
  555. ) {
  556. if ((&splitkey($cols[$mid]))[1] < $col) {
  557. $left = $mid;
  558. } else {
  559. $right = $mid;
  560. }
  561. $mid = int(($right+$left)/2.0);
  562. }
  563. return $matcol unless (&splitkey($cols[$mid]))[1]==$col;
  564. $right = $mid;
  565. while ( ($right<=$#cols)&&
  566. ((&splitkey($cols[$right]))[1]==$col)
  567. )
  568. {
  569. $matcol->assign((&splitkey($cols[$right]))[0],1,
  570. $matrix->elementkey($cols[$right]));
  571. $right++;
  572. }
  573. $left = $mid-1;
  574. while ( ($left) &&
  575. ((&splitkey($cols[$left]))[1]==$col)
  576. ){
  577. $matcol->assign((&splitkey($cols[$left]))[0],1,
  578. $matrix->elementkey($cols[$left]));
  579. $left--;
  580. }
  581. return $matcol;
  582. }
  583. foreach my $key (keys %{$matrix->{data}}){
  584. my ($i,$j) = &splitkey($key);
  585. $matcol->assignkey($key, $matrix->{data}{$key}) if ($j==$col);
  586. }
  587. return $matcol;
  588. }
  589. ### PRINT
  590. sub print {
  591. my ($object,$name,$rc) = @_;
  592. return unless defined $object;
  593. my $label = $name ? $name : $object->{name};
  594. my @order ;
  595. $rc = "n" unless defined($rc);
  596. if ($rc =~ /^r/i) {
  597. @order = $object->sortbyrow();
  598. } elsif ($rc =~ /^c/i) {
  599. @order = $object->sortbycolumn();
  600. } else {
  601. @order = keys %{$object->{data}};
  602. }
  603. foreach my $key (@order){
  604. my ($i,$j) = &splitkey($key);
  605. print "$label($i, $j) = ", $object->{data}{$key},"\n";
  606. }
  607. return "";
  608. }
  609. ###ARITHMETIC METHODS
  610. #left+$right, dimensions must be identical
  611. sub add {
  612. my ($left,$right,$switch) = @_;
  613. if (($left->{rows} == $right->{rows})&&($left->{columns} == $right->{columns})) {
  614. my $sum= $left->addfree($right);
  615. $sum->{rows} = $left->{rows};
  616. $sum->{columns} = $left->{columns};
  617. return $sum;
  618. } else {
  619. return undef;
  620. }
  621. }
  622. #as add, but no restrictions on dimensions.
  623. sub addfree {
  624. my ($left, $right,$switch) = @_;
  625. my $sum = new Math::MatrixSparse;
  626. $sum = $left->copy();
  627. if ((defined $left->{name})&&(defined $right->{name})) {
  628. $sum->{name} = $left->{name} . "+" . $right->{name};
  629. }
  630. foreach my $rightkey (keys %{$right->{data}}) {
  631. if (defined $sum->{data}{$rightkey}) {
  632. $sum->{data}{$rightkey}+= $right->{data}{$rightkey};
  633. } else {
  634. $sum->{data}{$rightkey} = $right->{data}{$rightkey};
  635. }
  636. }
  637. $sum->{rows} = $left->{rows} >$right->{rows} ?
  638. $left->{rows} : $right->{rows};
  639. $sum->{columns} = $left->{columns} >$right->{columns} ?
  640. $left->{columns} : $right->{columns};
  641. if ($left->{special}->{structure} eq $right->{special}->{structure}) {
  642. $sum->{special}->{structure} = $left->{special}->{structure};
  643. }
  644. if ($left->{special}->{shape} eq $right->{special}->{shape}) {
  645. $sum->{special}->{shape} = $left->{special}->{shape};
  646. }
  647. return $sum;
  648. }
  649. sub subtract {
  650. my ($left, $right, $switch) = @_;
  651. if ($switch) {
  652. ($left,$right) = ($right, $left);
  653. }
  654. if (
  655. ($left->{rows} == $right->{rows})&&
  656. ($left->{columns} == $right->{columns})
  657. ) {
  658. my $diff= $left->subtractfree($right);
  659. $diff->{rows} = $left->{rows};
  660. $diff->{columns} = $left->{columns};
  661. return $diff;
  662. } else {
  663. return undef;
  664. }
  665. }
  666. #as subtract, but no restrictions on dimensions.
  667. sub subtractfree {
  668. my ($left, $right,$switch) = @_;
  669. my $diff = new Math::MatrixSparse;
  670. $diff = $left->copy();
  671. foreach my $rightkey (keys %{$right->{data}}) {
  672. if (defined $diff->{data}{$rightkey}) {
  673. $diff->{data}{$rightkey}-= $right->{data}{$rightkey};
  674. } else {
  675. $diff->{data}{$rightkey} = -1* $right->{data}{$rightkey};
  676. }
  677. }
  678. if ((defined $left->{name})&&(defined $right->{name})) {
  679. $diff->{name} = $left->{name} . "-" . $right->{name};
  680. }
  681. $diff->{rows} = $left->{rows} >$right->{rows} ?
  682. $left->{rows} : $right->{rows};
  683. $diff->{columns} = $left->{columns} >$right->{columns} ?
  684. $left->{columns} : $right->{columns};
  685. if ($left->{special}->{structure} eq $right->{special}->{structure}) {
  686. $diff->{special}->{structure} = $left->{special}->{structure};
  687. }
  688. if ($left->{special}->{shape} eq $right->{special}->{shape}) {
  689. $diff->{special}->{shape} = $left->{special}->{shape};
  690. }
  691. return $diff;
  692. }
  693. sub negate {
  694. my ($matrix) = @_;
  695. return $matrix->multiplyscalar(-1);
  696. }
  697. sub _negate {
  698. my ($matrix) = @_;
  699. return $matrix->_multiplyscalar(-1);
  700. }
  701. sub multiplyscalar {
  702. my ($matrix, $scalar) = @_;
  703. my $product = $matrix->copy();
  704. $scalar = 0 unless $scalar;
  705. foreach my $key (keys %{$product->{data}}) {
  706. $product->assignkey($key,$matrix->elementkey($key)*$scalar);
  707. }
  708. $product->{name} = "$scalar*".$product->{name} if defined $product->{name};
  709. if ($scalar <0 ) {
  710. if ($matrix->{special}->{sign} =~ m/positive/i) {
  711. $product->{special}->{sign} = "negative";
  712. } elsif ($matrix->{special}->{sign} =~ m/negative/i) {
  713. $product->{special}->{sign} = "positive";
  714. } elsif ($matrix->{special}->{sign} =~ m/nonpositive/i) {
  715. $product->{special}->{sign} = "nonnegative";
  716. } elsif ($matrix->{special}->{sign} =~ m/nonnegative/i) {
  717. $product->{special}->{sign} = "nonpositive";
  718. }
  719. } else {
  720. $product->{special}->{sign} =$matrix->{special}->{sign};
  721. }
  722. $product->{special}->{sign} = "zero" unless $scalar;
  723. return $product;
  724. }
  725. sub _multiplyscalar {
  726. my ($matrix, $scalar) = @_;
  727. $scalar = 0 unless $scalar;
  728. foreach my $key (keys %{$matrix->{data}}) {
  729. $matrix->{data}{$key} = $matrix->{data}{$key}*$scalar;
  730. }
  731. if ($scalar <0 ) {
  732. if ($matrix->{special}->{sign} =~ m/positive/i) {
  733. $matrix->{special}->{sign} = "negative";
  734. } elsif ($matrix->{special}->{sign} =~ m/negative/i) {
  735. $matrix->{special}->{sign} = "positive";
  736. } elsif ($matrix->{special}->{sign} =~ m/nonpositive/i) {
  737. $matrix->{special}->{sign} = "nonnegative";
  738. } elsif ($matrix->{special}->{sign} =~ m/nonnegative/i) {
  739. $matrix->{special}->{sign} = "nonpositive";
  740. }
  741. }
  742. $matrix->{special}->{sign} = "zero" unless $scalar;
  743. return $matrix;
  744. }
  745. #finds $left*$right, if compatible
  746. sub multiply {
  747. my ($left,$right,$switch) = @_;
  748. unless (ref($right)) {
  749. return $left->multiplyscalar($right);
  750. }
  751. return undef if ($left->{columns} != $right->{rows});
  752. my $product = new Math::MatrixSparse;
  753. $product->{rows} = $left->{rows};
  754. $product->{columns} = $right->{columns};
  755. if ((defined $left->{name})&&(defined $right->{name})) {
  756. $product->{name} = $left->{name} . "*" . $right->{name};
  757. }
  758. foreach my $leftkey (keys %{$left->{data}}) {
  759. my ($li,$lj) = &splitkey($leftkey);
  760. foreach my $rightkey (keys %{$right->{data}}) {
  761. my ($ri,$rj) = &splitkey($rightkey);
  762. next unless ($lj==$ri);
  763. my $thiskey = &makekey($li, $rj);
  764. my $prod = $left->{data}{$leftkey}*$right->{data}{$rightkey};
  765. if (defined $product->{data}{$thiskey}) {
  766. $product->{data}{$thiskey} += $prod;
  767. } else {
  768. $product->{data}{$thiskey} = $prod;
  769. }
  770. }
  771. }
  772. if (
  773. ($left->{special}->{sign} =~ /zero/i)||
  774. ($right->{special}->{sign} =~ /zero/i)) {
  775. $product->{special}->{sign} = "zero";
  776. return $product;
  777. }
  778. if ($left->{special}->{sign} =~ /^positive/i) {
  779. $product->{special}->{sign} = $right->{special}->{sign};
  780. } elsif ($left->{special}->{sign} =~ /nonpositive/i) {
  781. if ($right->{special}->{sign} =~ /^positive/i) {
  782. $product->{special}->{sign} = "nonpositive";
  783. } elsif ($right->{special}->{sign} =~ /nonpositive/i) {
  784. $product->{special}->{sign} = "nonnegative";
  785. } elsif ($right->{special}->{sign} =~ /^negative/i) {
  786. $product->{special}->{sign} = "nonnegative";
  787. } elsif ($right->{special}->{sign} =~ /nonnegative/i) {
  788. $product->{special}->{sign} = "nonpositive";
  789. }
  790. } elsif ($left->{special}->{sign} =~ /^negative/i) {
  791. if ($right->{special}->{sign} =~ /^positive/i) {
  792. $product->{special}->{sign} = "negative";
  793. } elsif ($right->{special}->{sign} =~ /nonpositive/i) {
  794. $product->{special}->{sign} = "nonnegative";
  795. } elsif ($right->{special}->{sign} =~ /^negative/i) {
  796. $product->{special}->{sign} = "positive";
  797. } elsif ($right->{special}->{sign} =~ /nonnegative/i) {
  798. $product->{special}->{sign} = "nonpositive";
  799. }
  800. } elsif ($left->{special}->{sign} =~ /nonnegative/i) {
  801. if ($right->{special}->{sign} =~ /^positive/i) {
  802. $product->{special}->{sign} = "nonnegative";
  803. } elsif ($right->{special}->{sign} =~ /nonpositive/i) {
  804. $product->{special}->{sign} = "nonpositive";
  805. } elsif ($right->{special}->{sign} =~ /^negative/i) {
  806. $product->{special}->{sign} = "nonpositive";
  807. } elsif ($right->{special}->{sign} =~ /nonnegative/i) {
  808. $product->{special}->{sign} = "nonnegative";
  809. }
  810. }
  811. return $product;
  812. }
  813. sub quickmultiply {
  814. my ($left,$right,$switch) = @_;
  815. unless (ref($right)) {
  816. return $left->multiplyscalar($right);
  817. }
  818. return undef if ($left->{columns} != $right->{rows});
  819. my $product = new Math::MatrixSparse;
  820. $product->{special}->{structure} = "";
  821. $product->{special}->{pattern}=0;
  822. $product->{special}->{shape} = "";
  823. $product->{rows} = $left->{rows};
  824. $product->{columns} = $right->{columns};
  825. my @leftcols = $left->sortbycolumn();
  826. my @rightrows = $right->sortbyrow();
  827. if ((defined $left->{name})&&(defined $right->{name})) {
  828. $product->{name} = $left->{name} . "*" . $right->{name};
  829. }
  830. my $lastrow = 0;
  831. foreach my $leftkey (@leftcols) {
  832. my ($li,$lj) = &splitkey($leftkey);
  833. my $i = 0;
  834. my $thiskey;
  835. if ($lj >$lastrow ) {
  836. $lastrow = $lj;
  837. #remove elements that won't be used again in multiplication
  838. while (defined ($thiskey = $rightrows[0])){
  839. my ($ri,$rj) = &splitkey($thiskey);
  840. last if $ri>=$lj;
  841. shift @rightrows;
  842. }
  843. }
  844. foreach my $rightkey (@rightrows) {
  845. my ($ri,$rj) = &splitkey($rightkey);
  846. last if ($ri>$lj);
  847. next if ($ri<$lj);
  848. my $thiskey = &makekey($li, $rj);
  849. my $prod = $left->{data}{$leftkey}*$right->{data}{$rightkey};
  850. if (defined $product->{data}{$thiskey}) {
  851. $product->{data}{$thiskey} += $prod;
  852. } else {
  853. $product->{data}{$thiskey} = $prod;
  854. }
  855. }
  856. }
  857. if (
  858. ($left->{special}->{sign} =~ /zero/i)||
  859. ($right->{special}->{sign} =~ /zero/i)) {
  860. $product->{special}->{sign} = "zero";
  861. return $product;
  862. }
  863. if ($left->{special}->{sign} =~ /^positive/i) {
  864. $product->{special}->{sign} = $right->{special}->{sign};
  865. } elsif ($left->{special}->{sign} =~ /nonpositive/i) {
  866. if ($right->{special}->{sign} =~ /^positive/i) {
  867. $product->{special}->{sign} = "nonpositive";
  868. } elsif ($right->{special}->{sign} =~ /nonpositive/i) {
  869. $product->{special}->{sign} = "nonnegative";
  870. } elsif ($right->{special}->{sign} =~ /^negative/i) {
  871. $product->{special}->{sign} = "nonnegative";
  872. } elsif ($right->{special}->{sign} =~ /nonnegative/i) {
  873. $product->{special}->{sign} = "nonpositive";
  874. }
  875. } elsif ($left->{special}->{sign} =~ /^negative/i) {
  876. if ($right->{special}->{sign} =~ /^positive/i) {
  877. $product->{special}->{sign} = "negative";
  878. } elsif ($right->{special}->{sign} =~ /nonpositive/i) {
  879. $product->{special}->{sign} = "nonnegative";
  880. } elsif ($right->{special}->{sign} =~ /^negative/i) {
  881. $product->{special}->{sign} = "positive";
  882. } elsif ($right->{special}->{sign} =~ /nonnegative/i) {
  883. $product->{special}->{sign} = "nonpositive";
  884. }
  885. } elsif ($left->{special}->{sign} =~ /nonnegative/i) {
  886. if ($right->{special}->{sign} =~ /^positive/i) {
  887. $product->{special}->{sign} = "nonnegative";
  888. } elsif ($right->{special}->{sign} =~ /nonpositive/i) {
  889. $product->{special}->{sign} = "nonpositive";
  890. } elsif ($right->{special}->{sign} =~ /^negative/i) {
  891. $product->{special}->{sign} = "nonpositive";
  892. } elsif ($right->{special}->{sign} =~ /nonnegative/i) {
  893. $product->{special}->{sign} = "nonnegative";
  894. }
  895. }
  896. return $product;
  897. }
  898. sub quickmultiplyfree {
  899. my ($left,$right,$switch) = @_;
  900. unless (ref($right)) {
  901. return $left->multiplyscalar($right);
  902. }
  903. my $product = new Math::MatrixSparse;
  904. $product->{rows} = $left->{rows};
  905. $product->{columns} = $right->{columns};
  906. my @leftcols = $left->sortbycolumn();
  907. my @rightrows = $right->sortbyrow();
  908. if ((defined $left->{name})&&(defined $right->{name})) {
  909. $product->{name} = $left->{name} . "*" . $right->{name};
  910. }
  911. my $lastrow = 0;
  912. foreach my $leftkey (@leftcols) {
  913. my ($li,$lj) = &splitkey($leftkey);
  914. my $i = 0;
  915. my $thiskey;
  916. if ($lj >$lastrow ) {
  917. $lastrow = $lj;
  918. #remove elements that won't be used again in multiplication
  919. while (defined ($thiskey = $rightrows[0])){
  920. my ($ri,$rj) = &splitkey($thiskey);
  921. last if $ri>=$lj;
  922. shift @rightrows;
  923. }
  924. }
  925. foreach my $rightkey (@rightrows) {
  926. my ($ri,$rj) = &splitkey($rightkey);
  927. last if ($ri>$lj);
  928. next if ($ri<$lj);
  929. my $thiskey = &makekey($li , $rj);
  930. my $prod = $left->{data}{$leftkey}*$right->{data}{$rightkey};
  931. if (defined $product->{data}{$thiskey}) {
  932. $product->{data}{$thiskey} += $prod;
  933. } else {
  934. $product->{data}{$thiskey} = $prod;
  935. }
  936. }
  937. }
  938. if (
  939. ($left->{special}->{sign} =~ /zero/i)||
  940. ($right->{special}->{sign} =~ /zero/i)) {
  941. $product->{special}->{sign} = "zero";
  942. return $product;
  943. }
  944. if ($left->{special}->{sign} =~ /^positive/i) {
  945. $product->{special}->{sign} = $right->{special}->{sign};
  946. } elsif ($left->{special}->{sign} =~ /nonpositive/i) {
  947. if ($right->{special}->{sign} =~ /^positive/i) {
  948. $product->{special}->{sign} = "nonpositive";
  949. } elsif ($right->{special}->{sign} =~ /nonpositive/i) {
  950. $product->{special}->{sign} = "nonnegative";
  951. } elsif ($right->{special}->{sign} =~ /^negative/i) {
  952. $product->{special}->{sign} = "nonnegative";
  953. } elsif ($right->{special}->{sign} =~ /nonnegative/i) {
  954. $product->{special}->{sign} = "nonpositive";
  955. }
  956. } elsif ($left->{special}->{sign} =~ /^negative/i) {
  957. if ($right->{special}->{sign} =~ /^positive/i) {
  958. $product->{special}->{sign} = "negative";
  959. } elsif ($right->{special}->{sign} =~ /nonpositive/i) {
  960. $product->{special}->{sign} = "nonnegative";
  961. } elsif ($right->{special}->{sign} =~ /^negative/i) {
  962. $product->{special}->{sign} = "positive";
  963. } elsif ($right->{special}->{sign} =~ /nonnegative/i) {
  964. $product->{special}->{sign} = "nonpositive";
  965. }
  966. } elsif ($left->{special}->{sign} =~ /nonnegative/i) {
  967. if ($right->{special}->{sign} =~ /^positive/i) {
  968. $product->{special}->{sign} = "nonnegative";
  969. } elsif ($right->{special}->{sign} =~ /nonpositive/i) {
  970. $product->{special}->{sign} = "nonpositive";
  971. } elsif ($right->{special}->{sign} =~ /^negative/i) {
  972. $product->{special}->{sign} = "nonpositive";
  973. } elsif ($right->{special}->{sign} =~ /nonnegative/i) {
  974. $product->{special}->{sign} = "nonnegative";
  975. }
  976. }
  977. return $product;
  978. }
  979. #as multiply, but no restrictions on dimensions.
  980. sub multiplyfree {
  981. my ($left,$right,$switch) = @_;
  982. my $product = new Math::MatrixSparse;
  983. $product->{rows} = $left->{rows};
  984. $product->{columns} = $right->{columns};
  985. if ((defined $left->{name})&&(defined $right->{name})) {
  986. $product->{name} = $left->{name} . "*" . $right->{name};
  987. }
  988. foreach my $leftkey (keys %{$left->{data}}) {
  989. my ($li,$lj) = &splitkey($leftkey);
  990. foreach my $rightkey (keys %{$right->{data}}) {
  991. my ($ri,$rj) = &splitkey($rightkey);
  992. next unless ($lj==$ri);
  993. my $thiskey = &makekey($li, $rj);
  994. my $prod = $left->{data}{$leftkey}*$right->{data}{$rightkey};
  995. if (defined $product->{data}{$thiskey}) {
  996. $product->{data}{$thiskey} += $prod;
  997. } else {
  998. $product->{data}{$thiskey} = $prod;
  999. }
  1000. }
  1001. }
  1002. if (
  1003. ($left->{special}->{sign} =~ /zero/i)||
  1004. ($right->{special}->{sign} =~ /zero/i)) {
  1005. $product->{special}->{sign} = "zero";
  1006. return $product;
  1007. }
  1008. if ($left->{special}->{sign} =~ /^positive/i) {
  1009. $product->{special}->{sign} = $right->{special}->{sign};
  1010. } elsif ($left->{special}->{sign} =~ /nonpositive/i) {
  1011. if ($right->{special}->{sign} =~ /^positive/i) {
  1012. $product->{special}->{sign} = "nonpositive";
  1013. } elsif ($right->{special}->{sign} =~ /nonpositive/i) {
  1014. $product->{special}->{sign} = "nonnegative";
  1015. } elsif ($right->{special}->{sign} =~ /^negative/i) {
  1016. $product->{special}->{sign} = "nonnegative";
  1017. } elsif ($right->{special}->{sign} =~ /nonnegative/i) {
  1018. $product->{special}->{sign} = "nonpositive";
  1019. }
  1020. } elsif ($left->{special}->{sign} =~ /^negative/i) {
  1021. if ($right->{special}->{sign} =~ /^positive/i) {
  1022. $product->{special}->{sign} = "negative";
  1023. } elsif ($right->{special}->{sign} =~ /nonpositive/i) {
  1024. $product->{special}->{sign} = "nonnegative";
  1025. } elsif ($right->{special}->{sign} =~ /^negative/i) {
  1026. $product->{special}->{sign} = "positive";
  1027. } elsif ($right->{special}->{sign} =~ /nonnegative/i) {
  1028. $product->{special}->{sign} = "nonpositive";
  1029. }
  1030. } elsif ($left->{special}->{sign} =~ /nonnegative/i) {
  1031. if ($right->{special}->{sign} =~ /^positive/i) {
  1032. $product->{special}->{sign} = "nonnegative";
  1033. } elsif ($right->{special}->{sign} =~ /nonpositive/i) {
  1034. $product->{special}->{sign} = "nonpositive";
  1035. } elsif ($right->{special}->{sign} =~ /^negative/i) {
  1036. $product->{special}->{sign} = "nonpositive";
  1037. } elsif ($right->{special}->{sign} =~ /nonnegative/i) {
  1038. $product->{special}->{sign} = "nonnegative";
  1039. }
  1040. }
  1041. return $product;
  1042. }
  1043. sub kronecker {
  1044. my ($left,$right);
  1045. my ($rr,$rc) = $right->size();
  1046. return undef unless ($rr&&$rc);
  1047. my $kroprod = new Math::MatrixSparse;
  1048. foreach my $key (keys %{$left->{data}}) {
  1049. my ($i,$j) = &splitkey($key);
  1050. $kroprod->_insert($i*$rr,$j*$rc,$right*$left->elementkey($key));
  1051. }
  1052. if ((defined $left->{name})&&(defined $right->{name})) {
  1053. $kroprod->{name} = $left->{name} . "x" . $right->{name};
  1054. }
  1055. return $kroprod;
  1056. }
  1057. sub termmult {
  1058. my ($left,$right) = @_;
  1059. my $termprod = $left->copy();
  1060. foreach my $key (keys %{$termprod->{data}}) {
  1061. $termprod->assignkey($key,$termprod->elementkey($key)* $right->elementkey($key));
  1062. }
  1063. return $termprod;
  1064. }
  1065. sub exponentiate {
  1066. my ($matrix,$power) = @_;
  1067. unless ($matrix->is_square()) {
  1068. carp "Math::MatrixSparse::exponentiate matrix must be square";
  1069. return undef ;
  1070. }
  1071. return Math::MatrixSparse->newidentity($matrix->{rows}) unless $power;
  1072. unless ($power>0) {
  1073. carp "Math::MatrixSparse::exponentiate exponent must be positive";
  1074. return undef ;
  1075. }
  1076. unless ($power =~ /^[+]?\d+/) {
  1077. carp "Math::MatrixSparse::exponentiate exponent must be an integer";
  1078. return undef ;
  1079. }
  1080. my $product = $matrix->copy();
  1081. # $product->clearspecials();
  1082. for my $i (2..$power) {
  1083. $product = $product->quickmultiply($matrix);
  1084. }
  1085. $product->{name} = $matrix->{name} ."^$power" if $matrix->{name};
  1086. return $product;
  1087. }
  1088. sub largeexponentiate {
  1089. #find matrix^power using the square-and-multiply method
  1090. my ($matrix,$power) = @_;
  1091. unless ($matrix->is_square()) {
  1092. carp "Math::MatrixSparse::exponentiate matrix must be square";
  1093. return undef ;
  1094. }
  1095. return Math::MatrixSparse->newidentity($matrix->{rows}) unless $power;
  1096. unless ($power>0) {
  1097. carp "Math::MatrixSparse::exponentiate exponent must be positive";
  1098. return undef ;
  1099. }
  1100. unless ($power =~ /^[+]?\d+/) {
  1101. carp "Math::MatrixSparse::exponentiate exponent must be an integer";
  1102. return undef ;
  1103. }
  1104. #get a representation of $exponent in binary
  1105. my $bitstr = unpack('B32',pack('N',$power));
  1106. $bitstr =~s/^0*//;
  1107. my @bitstr=split(//,$bitstr);
  1108. my $z = Math::MatrixSparse->newidentity($matrix->{rows});
  1109. foreach my $bit (@bitstr){
  1110. $z = ($z*$z);
  1111. if ($bit){
  1112. $z = ($z*$matrix);
  1113. }
  1114. }
  1115. $z->{name} = "";
  1116. $z->{name} = $matrix->{name} . "^$power" if $matrix->{name};
  1117. return $z;
  1118. }
  1119. sub transpose {
  1120. my ($matrix) = @_;
  1121. my $this= new Math::MatrixSparse;
  1122. $this->{rows} = $matrix->{columns};
  1123. $this->{columns} = $matrix->{rows};
  1124. foreach my $key (keys %{$matrix->{data}}) {
  1125. my ($i,$j) = &splitkey($key);
  1126. $this->assign($j,$i,$matrix->{data}{$key});
  1127. }
  1128. $this->{name} = $matrix->{name} . "'" if $matrix->{name};
  1129. $this->{special}->{structure} = $matrix->{special}->{structure};
  1130. $this->{special}->{sign} = $matrix->{special}->{sign};
  1131. $this->{special}->{pattern} = $matrix->{special}->{pattern};
  1132. $this->{special}->{square} = $matrix->{special}->{square};
  1133. if ($matrix->{special}->{shape} =~ /diagonal/i) {
  1134. $this->{special}->{shape} = "diagonal";
  1135. } elsif ($matrix->{special}->{shape} =~ /^lower/i) {
  1136. $this->{special}->{shape} = "upper";
  1137. } elsif ($matrix->{special}->{shape} =~ /^upper/i) {
  1138. $this->{special}->{shape} = "lower";
  1139. } elsif ($matrix->{special}->{shape} =~ /strictupper/i) {
  1140. $this->{special}->{shape} = "strictlower";
  1141. } elsif ($matrix->{special}->{shape} =~ /strictlower/i) {
  1142. $this->{special}->{shape} = "strictlower";
  1143. } else {
  1144. $this->{special}->{shape} = "";
  1145. }
  1146. return $this;
  1147. }
  1148. sub terminvert {
  1149. my ($matrix) = @_;
  1150. my $this = $matrix->copy();
  1151. foreach my $key (keys %{$this->{data}}) {
  1152. next unless $this->{data}{$key};
  1153. $this->{data}{$key} = 1.0/($this->{data}{$key});
  1154. }
  1155. return $this;
  1156. }
  1157. sub _terminvert {
  1158. my ($matrix) = @_;
  1159. foreach my $key (keys %{$matrix->{data}}) {
  1160. next unless $matrix->{data}{$key};
  1161. $matrix->{data}{$key} = 1.0/($matrix->{data}{$key});
  1162. }
  1163. return $matrix;
  1164. }
  1165. ### DISSECTION METHODS
  1166. sub diagpart {
  1167. my ($matrix,$offset) = @_;
  1168. my $diag = new Math::MatrixSparse;
  1169. $offset = 0 unless defined $offset;
  1170. foreach my $key (keys %{$matrix->{data}}){
  1171. my ($i,$j) = &splitkey($key);
  1172. next unless ($i == ($j+$offset));
  1173. $diag->assign($i,$j,$matrix->{data}{$key});
  1174. }
  1175. $diag->{rows} = $matrix->{rows};
  1176. $diag->{columns} = $matrix->{columns};
  1177. $diag->{special}->{shape} = "diagonal";
  1178. return $diag;
  1179. }
  1180. sub nondiagpart {
  1181. my ($matrix,$offset) = @_;
  1182. my $diag = new Math::MatrixSparse;
  1183. $offset = 0 unless defined $offset;
  1184. foreach my $key (keys %{$matrix->{data}}){
  1185. my ($i,$j) = &splitkey($key);
  1186. next if ($i == ($j+$offset));
  1187. $diag->assign($i,$j,$matrix->{data}{$key});
  1188. }
  1189. $diag->{rows} = $matrix->{rows};
  1190. $diag->{columns} = $matrix->{columns};
  1191. return $diag;
  1192. }
  1193. sub lowerpart {
  1194. my ($matrix) = @_;
  1195. my $lower = new Math::MatrixSparse;
  1196. foreach my $key (keys %{$matrix->{data}}){
  1197. my ($i,$j) = &splitkey($key);
  1198. next unless ($i > $j);
  1199. $lower->assign($i,$j,$matrix->{data}{$key});
  1200. }
  1201. $lower->{rows} = $matrix->{rows};
  1202. $lower->{columns} = $matrix->{columns};
  1203. $lower->{special}->{shape} = "strictlower";
  1204. return $lower;
  1205. }
  1206. sub nonlowerpart {
  1207. my ($matrix) = @_;
  1208. my $lower = new Math::MatrixSparse;
  1209. foreach my $key (keys %{$matrix->{data}}){
  1210. my ($i,$j) = &splitkey($key);
  1211. next if ($i > $j);
  1212. $lower->assign($i,$j,$matrix->{data}{$key});
  1213. }
  1214. $lower->{rows} = $matrix->{rows};
  1215. $lower->{columns} = $matrix->{columns};
  1216. $lower->{special}->{shape} = "upper";
  1217. return $lower;
  1218. }
  1219. sub upperpart {
  1220. my ($matrix) = @_;
  1221. my $upper = new Math::MatrixSparse;
  1222. foreach my $key (keys %{$matrix->{data}}){
  1223. my ($i,$j) = &splitkey($key);
  1224. next unless ($i < $j);
  1225. $upper->assign($i,$j,$matrix->{data}{$key});
  1226. }
  1227. $upper->{rows} = $matrix->{rows};
  1228. $upper->{columns} = $matrix->{columns};
  1229. $upper->{special}->{shape} = "strictupper";
  1230. return $upper;
  1231. }
  1232. sub nonupperpart {
  1233. my ($matrix) = @_;
  1234. my $upper = new Math::MatrixSparse;
  1235. foreach my $key (keys %{$matrix->{data}}){
  1236. my ($i,$j) = &splitkey($key);
  1237. next if ($i < $j);
  1238. $upper->assign($i,$j,$matrix->{data}{$key});
  1239. }
  1240. $upper->{rows} = $matrix->{rows};
  1241. $upper->{columns} = $matrix->{columns};
  1242. $upper->{special}->{shape} = "lower";
  1243. return $upper;
  1244. }
  1245. sub _diagpart {
  1246. my ($matrix,$offset) = @_;
  1247. $offset = 0 unless defined $offset;
  1248. foreach my $key (keys %{$matrix->{data}}){
  1249. my ($i,$j) = &splitkey($key);
  1250. $matrix->delete($i,$j) unless ($i == ($j+$offset));
  1251. }
  1252. $matrix->{special}->{shape} = "diagonal";
  1253. return $matrix;
  1254. }
  1255. sub _nondiagpart {
  1256. my ($matrix,$offset) = @_;
  1257. $offset = 0 unless defined $offset;
  1258. foreach my $key (keys %{$matrix->{data}}){
  1259. my ($i,$j) = &splitkey($key);
  1260. $matrix->delete($i,$j) if ($i == ($j+$offset));
  1261. }
  1262. $matrix->{special}->{shape}="" if
  1263. $matrix->{special}->{shape} =~ m/diagonal/i;
  1264. return $matrix;
  1265. }
  1266. sub _lowerpart {
  1267. my ($matrix) = @_;
  1268. foreach my $key (keys %{$matrix->{data}}){
  1269. my ($i,$j) = &splitkey($key);
  1270. $matrix->delete($i,$j) unless ($i > $j);
  1271. }
  1272. $matrix->{special}->{shape} = "strictlower";
  1273. return $matrix;
  1274. }
  1275. sub _nonlowerpart {
  1276. my ($matrix) = @_;
  1277. foreach my $key (keys %{$matrix->{data}}){
  1278. my ($i,$j) = &splitkey($key);
  1279. $matrix->delete($i,$j) if ($i > $j);
  1280. }
  1281. $matrix->{special}->{shape} = "upper";
  1282. return $matrix;
  1283. }
  1284. sub _upperpart {
  1285. my ($matrix) = @_;
  1286. foreach my $key (keys %{$matrix->{data}}){
  1287. my ($i,$j) = &splitkey($key);
  1288. $matrix->delete($i,$j) unless ($i < $j);
  1289. }
  1290. $matrix->{special}->{shape} = "strictupper";
  1291. return $matrix;
  1292. }
  1293. sub _nonupperpart {
  1294. my ($matrix) = @_;
  1295. foreach my $key (keys %{$matrix->{data}}){
  1296. my ($i,$j) = &splitkey($key);
  1297. $matrix->delete($i,$j) if ($i < $j);
  1298. }
  1299. $matrix->{special}->{shape} = "lower";
  1300. return $matrix;
  1301. }
  1302. sub symmetricpart {
  1303. #.5*( A+A' )
  1304. my ($matrix) = @_;
  1305. my $sp =($matrix+$matrix->transpose());
  1306. $sp = 0.5*$sp;
  1307. $sp->{special}->{structure} = "symmetric";
  1308. return $sp;
  1309. }
  1310. sub _symmetricpart {
  1311. #.5*( A+A' )
  1312. my ($matrix) = @_;
  1313. my $sp =($matrix+$matrix->transpose());
  1314. $sp = 0.5*$sp;
  1315. $sp->{special}->{structure} = "symmetric";
  1316. return $matrix=$sp->copy();
  1317. }
  1318. sub skewsymmetricpart {
  1319. #.5*( A-A' )
  1320. my ($matrix) = @_;
  1321. my $ssp= 0.5*($matrix-$matrix->transpose());
  1322. $ssp->{special}->{structure} = "skewsymmetric";
  1323. return $ssp;
  1324. }
  1325. sub _skewsymmetricpart {
  1326. #.5*( A-A' )
  1327. my ($matrix) = @_;
  1328. my $ssp= 0.5*($matrix-$matrix->transpose());
  1329. $ssp->{special}->{structure} = "skewsymmetric";
  1330. return $matrix=$ssp->copy();
  1331. }
  1332. sub positivepart {
  1333. my ($matrix) = @_;
  1334. my $pos = new Math::MatrixSparse;
  1335. foreach my $key (keys %{$matrix->{data}}){
  1336. next unless $matrix->elementkey($key) >0;
  1337. $pos->assignkey($key,$matrix->{data}{$key});
  1338. }
  1339. $pos->{rows} = $matrix->{rows};
  1340. $pos->{columns} = $matrix->{columns};
  1341. return $pos;
  1342. }
  1343. sub negativepart {
  1344. my ($matrix) = @_;
  1345. my $neg = new Math::MatrixSparse;
  1346. foreach my $key (keys %{$matrix->{data}}){
  1347. next unless $matrix->elementkey($key) <0;
  1348. $neg->assignkey($key,$matrix->{data}{$key});
  1349. }
  1350. $neg->{rows} = $matrix->{rows};
  1351. $neg->{columns} = $matrix->{columns};
  1352. return $neg;
  1353. }
  1354. sub _positivepart {
  1355. my ($matrix) = @_;
  1356. foreach my $key (keys %{$matrix->{data}}){
  1357. next if $matrix->elementkey($key) >0;
  1358. $matrix->deletekey($key,$matrix->{data}{$key});
  1359. }
  1360. return $matrix;
  1361. }
  1362. sub _negativepart {
  1363. my ($matrix) = @_;
  1364. foreach my $key (keys %{$matrix->{data}}){
  1365. next if $matrix->elementkey($key) <0;
  1366. $matrix->deletekey($key,$matrix->{data}{$key});
  1367. }
  1368. return $matrix;
  1369. }
  1370. sub mask{
  1371. my ($matrix, $i1,$i2,$j1,$j2) = @_;
  1372. return undef unless (($i1<=$i2)&&($j1<=$j2));
  1373. my $mask = new Math::MatrixSparse;
  1374. foreach my $key (keys %{$matrix->{data}}) {
  1375. my ($i,$j) = &splitkey($key);
  1376. next unless (($i>=$i1)&&($i<=$i2));
  1377. next unless (($j>=$j1)&&($j<=$j2));
  1378. $mask->assignkey($key,$matrix->{data}{$key});
  1379. }
  1380. return $mask;
  1381. }
  1382. sub _mask{
  1383. my ($matrix, $i1,$i2,$j1,$j2) = @_;
  1384. return undef unless (($i1<=$i2)&&($j1<=$j2));
  1385. foreach my $key (keys %{$matrix->{data}}) {
  1386. my ($i,$j) = &splitkey($key);
  1387. next if (($i>=$i1)&&($i<=$i2));
  1388. next if (($j>=$j1)&&($j<=$j2));
  1389. $matrix->assignkey($key,0);
  1390. }
  1391. return $matrix;
  1392. }
  1393. sub submatrix{
  1394. my ($matrix, $i1,$i2,$j1,$j2) = @_;
  1395. return undef unless (($i1<=$i2)&&($j1<=$j2));
  1396. my $subm = new Math::MatrixSparse;
  1397. foreach my $key (keys %{$matrix->{data}}) {
  1398. my ($i,$j) = &splitkey($key);
  1399. next unless (($i>=$i1)&&($i<=$i2));
  1400. next unless (($j>=$j1)&&($j<=$j2));
  1401. $subm->assign($i-$i1,$j-$j1,$matrix->{data}{$key});
  1402. }
  1403. return $subm;
  1404. }
  1405. sub insert {
  1406. my ($big, $i0,$j0,$small) = @_;
  1407. my $insert = $big->copy();
  1408. foreach my $key (keys %{$small->{data}}){
  1409. my ($i,$j) = &splitkey($key);
  1410. $insert->assignkey($i+$i0,$j+$j0,$small->elementkey($key));
  1411. }
  1412. return $insert;
  1413. }
  1414. sub _insert {
  1415. my ($big, $i0,$j0,$small) = @_;
  1416. foreach my $key (keys %{$small->{data}}){
  1417. my ($i,$j) = &splitkey($key);
  1418. $big->assignkey($i+$i0,$j+$j0,$small->elementkey($key));
  1419. }
  1420. return $big;
  1421. }
  1422. sub shift {
  1423. my ($matrix, $i1,$j1) = @_;
  1424. my $this = new Math::MatrixSparse;
  1425. $this->{name}=$matrix->{name};
  1426. foreach my $key (keys %{$matrix->{data}}) {
  1427. my ($i,$j) = &splitkey($key);
  1428. $this->assign($i+$i1,$j+$j1,$matrix->{data}{$key});
  1429. }
  1430. return $this;
  1431. }
  1432. sub each {
  1433. my ($matrix,$coderef) = @_;
  1434. my $this = $matrix->copy();
  1435. foreach my $key (keys %{$this->{data}}) {
  1436. my ($i,$j) = &splitkey($key);
  1437. $this->assign($i,$j,&$coderef($this->{data}{$key},$i,$j));
  1438. }
  1439. return $this;
  1440. }
  1441. sub _each {
  1442. my ($matrix,$coderef) = @_;
  1443. foreach my $key (keys %{$matrix->{data}}) {
  1444. my ($i,$j) = &splitkey($key);
  1445. $matrix->assign($i,$j,&$coderef($matrix->{data}{$key},$i,$j));
  1446. }
  1447. return $matrix;
  1448. }
  1449. ### INFORMATIONAL AND STATISTICAL METHODS
  1450. sub printstats {
  1451. my ($matrix,$name) = @_;
  1452. return unless defined $matrix;
  1453. $name = $name || $matrix->{name} || "unnamed matrix";
  1454. print "Statistics for $name :\n";
  1455. print "rows: $matrix->{rows}\t";
  1456. print "columns: $matrix->{columns}\tdefined elements: ";
  1457. print $matrix->count(), "\n";
  1458. my ($width) = $matrix->width();
  1459. print "Bandwidth: $width\t";
  1460. print "Symmetric " if $matrix->is_symmetric();
  1461. print "Skew-Symmetric " if $matrix->is_skewsymmetric();
  1462. # print "Hermetian " if $matrix->{special}->{structure} =~m/^hermetian/i;
  1463. print "Real " if $matrix->{special}->{field} =~ m/real/i;
  1464. # print "Complex " if $matrix->{special}->{field} =~ m/complex/i;
  1465. print "Strictly " if $matrix->{special}->{shape} =~ m/strict/;
  1466. print "Upper Triangular " if $matrix->{special}->{shape} =~ m/upper/;
  1467. print "Lower Triangular " if $matrix->{special}->{shape} =~ m/lower/;
  1468. print "Diagonal" if $matrix->is_diagonal();
  1469. print "Pattern " if $matrix->is_pattern();
  1470. print "Square " if $matrix->is_square();
  1471. print "\n";
  1472. }
  1473. sub dim {
  1474. my ($matrix) = @_;
  1475. return ($matrix->{rows},$matrix->{columns});
  1476. }
  1477. sub exactdim {
  1478. my ($matrix) = @_;
  1479. my $rows=0;
  1480. my $columns = 0;
  1481. foreach my $key (keys %{$matrix->{data}}) {
  1482. my ($i,$j) = &splitkey($key);
  1483. $rows = $i if $i>$rows;
  1484. $columns = $j if $j > $rows;
  1485. }
  1486. return ($rows,$columns);
  1487. }
  1488. sub count {
  1489. my ($matrix) = @_;
  1490. return scalar keys %{$matrix->{data}};
  1491. }
  1492. sub width {
  1493. my ($matrix) = @_;
  1494. return $matrix->{special}->{bandwidth} if $matrix->{special}->{bandwidth};
  1495. my $width = 0;
  1496. foreach my $key (keys %{$matrix->{data}}) {
  1497. my ($i,$j) = &splitkey($key);
  1498. $width = abs($i- $j) if abs($i-$j) > $width;
  1499. }
  1500. return ($width);
  1501. }
  1502. sub sum {
  1503. my ($matrix) = @_;
  1504. my $sum = 0;
  1505. foreach my $key (keys %{$matrix->{data}}) {
  1506. $sum += $matrix->elementkey($key);
  1507. }
  1508. return $sum;
  1509. }
  1510. sub sumeach {
  1511. my ($matrix,$coderef) = @_;
  1512. my $sum = 0;
  1513. foreach my $key (keys %{$matrix->{data}}) {
  1514. my ($i,$j) = &splitkey( $key);
  1515. $sum += &$coderef($matrix->elementkey($key),$i,$j);
  1516. }
  1517. return $sum;
  1518. }
  1519. sub abssum {
  1520. my ($matrix) = @_;
  1521. my $sum = 0;
  1522. foreach my $key (keys %{$matrix->{data}}) {
  1523. $sum += abs($matrix->elementkey($key));
  1524. }
  1525. return $sum;
  1526. }
  1527. sub rownorm {
  1528. my ($matrix) = @_;
  1529. my %rowsums;
  1530. return 0 unless defined $matrix;
  1531. foreach my $key (keys %{$matrix->{data}}) {
  1532. my ($i,$j) = &splitkey($key);
  1533. $rowsums{$i}+= abs($matrix->elementkey($key));
  1534. }
  1535. return (sort {$a <=> $b} values %rowsums)[0];
  1536. }
  1537. sub columnnorm {
  1538. my ($matrix) = @_;
  1539. my %columnsums;
  1540. return 0 unless defined $matrix;
  1541. foreach my $key (keys %{$matrix->{data}}) {
  1542. my ($i,$j) = &splitkey($key);
  1543. $columnsums{$i}+= $matrix->elementkey($key);
  1544. }
  1545. return (sort {$a <=> $b} values %columnsums)[0];
  1546. }
  1547. sub norm_max {
  1548. return $_[0]->rownorm();
  1549. }
  1550. sub norm_one {
  1551. return $_[0]->columnnorm();
  1552. }
  1553. sub norm_frobenius {
  1554. return sqrt($_[0]->sumeach(sub {$_[0]*$_[0]}));
  1555. }
  1556. sub trace {
  1557. my ($matrix) = @_;
  1558. return $matrix->diagpart()->sum();
  1559. }
  1560. ### BOOLEAN METHODS
  1561. sub equals {
  1562. my ($left,$right) = @_;
  1563. return 1 unless ( defined $left|| defined $right);
  1564. return 0 unless defined $left;
  1565. return 0 unless defined $right;
  1566. my $truth = 1;
  1567. foreach my $key (keys %{$left->{data}}, keys %{$right->{data}}) {
  1568. $truth *= ($left->elementkey($key) == $right->elementkey($key));
  1569. }
  1570. return $truth;
  1571. }
  1572. sub is_square {
  1573. my ($matrix) = @_;
  1574. return 0 unless defined $matrix;
  1575. return $matrix->{rows} == $matrix->{columns};
  1576. }
  1577. sub is_quadratic {
  1578. my ($matrix) = @_;
  1579. return 0 unless defined $matrix;
  1580. return $matrix->{rows} == $matrix->{columns};
  1581. }
  1582. sub is_symmetric {
  1583. my ($matrix) = @_;
  1584. return 0 unless defined $matrix;
  1585. return 1 if $matrix->{special}->{structure} =~ m/^symmetric/;
  1586. return 0 if $matrix->{special}->{structure} =~ m/skewsymmetric/;
  1587. # return 0 if $matrix->{special}->{structure} =~ m/hermetian/;
  1588. my $truth = 1;
  1589. foreach my $key (keys %{$matrix->{data}}) {
  1590. my ($

Large files files are truncated, but you can click here to view the full file