File Coverage

blib/lib/Bio/Metabolic/MatrixOps.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1              
2             =head1 NAME
3              
4             Bio::Metabolic::MatrixOps - Operations on PDL::Matrix objects
5              
6             =head1 SYNOPSIS
7              
8             use Bio::Metabolic::MatrixOps;
9              
10              
11             =head1 DESCRIPTION
12              
13             This module contains all matrix operations that are needed for calculations
14             involving the stoichiometric matrix
15              
16             =head1 AUTHOR
17              
18             Oliver Ebenhoeh, oliver.ebenhoeh@rz.hu-berlin.de
19              
20             =head1 SEE ALSO
21              
22             Bio::Metabolic
23             Bio::Metabolic::Substrate
24             Bio::Metabolic::Substrate::Cluster
25             Bio::Metabolic::Reaction
26             Bio::Metabolic::Network
27              
28             =cut
29              
30             =head1 METHODS
31              
32             =head2 method xrow
33              
34             $m->xrow($r1,$r2);
35             Exchanges rows $r1 and $r2 in matrix $m.
36              
37             =cut
38              
39 1     1   1743 use PDL;
  0            
  0            
40             use PDL::Matrix;
41             use Math::Fraction;
42             use Math::Cephes qw(:fract);
43              
44             sub PDL::Matrix::xrow { # $m, $row1, $row2
45             my $matrix = shift;
46             my ( $r1, $r2 ) = @_;
47              
48             my $d = $r1 - $r2;
49             ( my $tmp = $matrix->slice("$r1:$r2:$d,:") ) .=
50             $matrix->slice("$r2:$r1:$d,:")->sever
51             if $d != 0;
52             }
53              
54             =head2 method xcol
55              
56             $m->xcol($r1,$r2);
57             Exchanges columns $r1 and $r2 in matrix $m.
58              
59             =cut
60              
61             sub PDL::Matrix::xcol {
62             my $matrix = shift;
63             my ( $r1, $r2 ) = @_;
64              
65             my $d = $r1 - $r2;
66             ( my $tmp = $matrix->slice(":,$r1:$r2:$d") ) .=
67             $matrix->slice(":,$r2:$r1:$d")->sever
68             if $d != 0;
69             }
70              
71             =head2 method addrows
72              
73             $m->addrows($r1,$r2[,$lambda]);
74             Adds $lambda times the $r2-th row to $r1
75              
76             =cut
77              
78             sub PDL::Matrix::addcols { # $matrix->($i,$j[,$lambda])
79             # adds lambda * j-th row to i-th row
80             my $matrix = shift;
81             my $i = shift;
82             my $j = shift;
83             my $lambda = @_ ? shift: 1;
84              
85             ( my $tmp = $matrix->slice(":,($i)") ) +=
86             $lambda * $matrix->slice(":,($j)")->sever;
87             }
88              
89             =head2 method addcols
90              
91             $m->addcols($c1,$c2[,$lambda]);
92             Adds $lambda times the $c2-th column to $c1
93              
94             =cut
95              
96             sub PDL::Matrix::addrows { # $matrix->($i,$j[,$lambda])
97             # adds lambda * j-th row to i-th row
98             my $matrix = shift;
99             my $i = shift;
100             my $j = shift;
101             my $lambda = @_ ? shift: 1;
102              
103             ( my $tmp = $matrix->slice("($i),:") ) +=
104             $lambda * $matrix->slice("($j),:")->sever;
105             }
106              
107             =head2 method delcol
108              
109             $m->delcol($c);
110             Sets all coefficients in the column $c to zero.
111              
112             =cut
113              
114             sub PDL::Matrix::delcol { # set row to zero
115             my ( $matrix, $r ) = @_;
116              
117             ( my $tmp = $matrix->slice(":,$r") ) .= 0;
118             }
119              
120             =head2 method delcols
121              
122             $m->delcols(@c);
123             Sets all coefficients in the columns specified in @c to zero.
124              
125             =cut
126              
127             sub PDL::Matrix::delcols { # set several rows to zero
128             my ( $matrix, @rows ) = @_;
129              
130             my $r;
131             foreach $r (@rows) {
132             $matrix->delcol($r);
133             }
134             }
135              
136             =head2 method delrow
137              
138             $m->delrow($r);
139             Sets all coefficients in the row $r to zero.
140              
141             =cut
142              
143             sub PDL::Matrix::delrow { # set row to zero
144             my ( $matrix, $r ) = @_;
145              
146             ( my $tmp = $matrix->slice("$r,:") ) .= 0;
147             }
148              
149             =head2 method delrows
150              
151             $m->delrows(@r);
152             Sets all coefficients in the rows specified in @r to zero.
153              
154             =cut
155              
156             sub PDL::Matrix::delrows { # set several rows to zero
157             my ( $matrix, @rows ) = @_;
158              
159             my $r;
160             foreach $r (@rows) {
161             $matrix->delrow($r);
162             }
163             }
164              
165             =head2 method det # probably obsolete!!!! Check with PDL::Matrix / PDL::MatrixOps
166              
167             $det = $m->det;
168             Returns the determinant of matrix $m, undef if $m is not square.
169              
170             =cut
171              
172             #sub PDL::Matrix::det {
173             # my $matrix = shift->copy;
174              
175             # my @dims = $matrix->dims;
176              
177             # return undef if ( @dims < 2 );
178             # my $n = $dims[0] < $dims[1] ? $dims[0] : $dims[1];
179              
180             # my ( $nzindex, $tmp, $r2 );
181             # my $sign = 1;
182             # my $r = 0;
183             # my $rank = $n;
184             # while ( $r < $rank ) {
185              
186             # print "Entering loop with r=$r, rank=$rank\n";
187             # my $row = $matrix->slice("($r),:");
188             # if ( $row->where( $row == 0 )->nelem >= $n ) {
189             # if ( $r < $rank - 1 ) {
190             # $matrix->xrow( $r, $rank - 1 )
191             # ; #print("Reihenaustausch, $matrix\n");
192             # $sign = -$sign;
193             # }
194             # $rank--;
195             # }
196             # else {
197             # $nzindex = which( $row != 0 )->at(0);
198             # if ( $nzindex > $r ) {
199             # $matrix->xcol( $r, $nzindex )
200             # ; #print("Spaltenaustausch, $matrix\n");
201             # $sign *= -1;
202             # }
203             # for ( $r2 = $r + 1 ; $r2 < $rank ; $r2++ ) {
204             # $matrix->addrows( $r2, $r,
205             # -$matrix->at( $r2, $r ) / $matrix->at( $r, $r ) );
206              
207             ## ($tmp = $matrix->slice(":,$r2")) .= $matrix->slice(":,$r2") - $matrix->at($r,$r2)/$matrix->at($r,$r) * $matrix->slice(":,$r");
208             # }
209             # $r++;
210             # }
211              
212             # # print "$r,$rank,$matrix\n";
213             # }
214             # my $det = 1;
215             # for ( $r = 0 ; $r < $n ; $r++ ) {
216             # $det *= $matrix->at( $r, $r );
217             # }
218             # return ( $det * $sign );
219             #}
220              
221             =head2 method is_pos_def
222              
223             $m->is_pos_def;
224             Returns true if matrix $m is truely positive definite, false otherwise
225              
226             =cut
227              
228             sub PDL::Matrix::is_pos_def {
229             my $matrix = shift;
230              
231             my ( $n, $m ) = $matrix->dims;
232              
233             for ( my $i = 0 ; $i < $n ; $i++ ) {
234             return (0) unless $matrix->slice("0:$i,0:$i")->det > 0;
235             }
236              
237             return (1);
238             }
239              
240             =head2 method row_echelon_int;
241              
242             $row_echelon_matrix = $m->row_echelon_int;
243             ($row_echelon_matrix, $permutation_vector, $rank) = $m->row_echelon_int;
244              
245             Returns the integer row echelon form of matrix $m.
246             In array context also returns the permutation vector indication how the
247             rows of $m were permuted while calculating the row echelon form and the
248             rank of the matrix $m.
249              
250             =cut
251              
252             sub PDL::Matrix::row_echelon_int {
253             my $matrix = shift->copy;
254              
255             # print "reference of matrix is now: ".ref($matrix)."\n";
256             return ( null, null, 0 ) if ( $matrix->isempty );
257              
258             my ( $n, $m ) = $matrix->dims;
259              
260             my $rank = $m;
261             my $r = 0;
262             my $perm = msequence 1, $n;
263              
264             # bless $perm, ref($matrix);
265              
266             my ( $nzindex, $tmp, $r2, $frac, $p, $q );
267             while ( $r < $rank ) {
268              
269             # print "Entering loop with r=$r, rank=$rank\n";
270             my $row = $matrix->slice("($r),:");
271             if ( $row->where( $row == 0 )->nelem == $n ) {
272              
273             # print "Exchange rows $r and ".eval($rank-1)."...\n";
274             $matrix->xrow( $r, $rank - 1 ); #print $matrix;
275             $rank--;
276             }
277             else {
278             $nzindex = which( $row != 0 )->at(0);
279             if ( $nzindex > $r ) {
280              
281             # print "Exchange columns $r and $nzindex...\n";
282             $matrix->xcol( $r, $nzindex );
283             $perm->xcol( $r, $nzindex );
284             }
285             for ( $r2 = $r + 1 ; $r2 < $rank ; $r2++ ) {
286              
287             # for ($r2=0;$r2<$rank;$r2++) {
288             # if ($r2 != $r) {
289             ( $p, $q ) =
290             frac( $matrix->at( $r2, $r ), $matrix->at( $r, $r ) )->list;
291              
292             # print "Reihe $r2:p=$p,q=$q ".frac($matrix->at($r,$r2),$matrix->at($r,$r))->string."\n";
293             ( $tmp = $matrix->slice("$r2,:") ) .=
294             $q * $matrix->slice("$r2,:") - $p * $matrix->slice("$r,:");
295              
296             # }
297             }
298             $r++;
299             }
300              
301             # print "$r,$rank,$matrix\n";
302             }
303              
304             # return wantarray ? ( $matrix, $perm->slice("(0),:"), $rank ) : $matrix;
305             return wantarray ? ( $matrix, $perm, $rank ) : $matrix;
306             }
307              
308             =head2 method cutrow
309              
310             $mnew = $m->cutrow($r);
311             Returns a matrix without row $r, i.e. the number of rows is
312             reduced by one.
313              
314             =cut
315              
316             sub PDL::Matrix::cutrow {
317             my $matrix = shift->copy;
318             my $r = shift;
319              
320             my ( $x, $y ) = $matrix->mdims;
321              
322             if ( $r < $x - 1 ) {
323             my $slstr1 = "$r:" . eval( $x - 2 ) . ",:";
324             my $slstr2 = eval( $r + 1 ) . ":" . eval( $x - 1 ) . ",:";
325             ( my $tmp = $matrix->slice("$slstr1") ) .=
326             $matrix->slice("$slstr2")->sever;
327             }
328             $x -= 2;
329             return $x < 0 ? null: $matrix->slice("0:$x,:");
330             }
331              
332             =head2 method cutcol
333              
334             $mnew = $m->cutcol($c);
335             Returns a matrix without column $c, i.e. the number of columns is
336             reduced by one.
337              
338             =cut
339              
340             sub PDL::Matrix::cutcol {
341             my $matrix = shift->copy;
342             my $r = shift;
343              
344             my ( $x, $y ) = $matrix->mdims;
345             return undef if ( !defined $y );
346              
347             if ( $r < $y - 1 ) {
348             my $slstr1 = ":,$r:" . eval( $y - 2 );
349             my $slstr2 = ":," . eval( $r + 1 ) . ":" . eval( $y - 1 );
350             ( my $tmp = $matrix->slice("$slstr1") ) .=
351             $matrix->slice("$slstr2")->sever;
352             }
353             $y -= 2;
354             return $y < 0 ? null: $matrix->slice(":,0:$y");
355             }
356              
357             =head2 method cutrows
358              
359             $mnew = $m->cutrows(@r);
360             Returns a matrix without all rows specified in @r, i.e. the number
361             of rows is reduced by the number of elements in @r.
362              
363             =cut
364              
365             sub PDL::Matrix::cutrows {
366             my $matrix = shift->copy;
367             my @rows = sort { $b <=> $a } @_;
368              
369             for ( my $r = 0 ; $r < @rows ; $r++ ) {
370             $matrix = $matrix->cutrow( $rows[$r] );
371             }
372             return $matrix;
373             }
374              
375             =head2 method cutcols
376              
377             $mnew = $m->cutcols(@c);
378             Returns a matrix without all columns specified in @c, i.e. the number
379             of columns is reduced by the number of elements in @c.
380              
381             =cut
382              
383             sub PDL::Matrix::cutcols {
384             my $matrix = shift->copy;
385             my @rows = sort { $b <=> $a } @_;
386              
387             for ( my $r = 0 ; $r < @rows ; $r++ ) {
388             $matrix = $matrix->cutcol( $rows[$r] );
389             }
390             return $matrix;
391             }
392              
393             =head2 method permrows
394              
395             $mnew = $m->permrows($permutation_vector);
396              
397             Returns a matrix with the rows permuted as specified by $permutation_vector.
398             $permutation_vector must be a PDL.
399              
400             EXAMPLE: If $m is a 3x3 matrix, then
401             $p = $m->permrows(pdl [2,0,1]);
402             will return a matrix with the last row of $m as first row, the first row of $m
403             as the second and the second row of $m as the last row.
404              
405             =cut
406              
407             *PDL::Matrix::permrows = \&permrows;
408              
409             sub permrows {
410             my $matrix = shift->copy;
411             my $perm;
412             if ( ref( $_[0] ) eq "PDL::Matrix" ) {
413             $perm = shift;
414             my @pdims = $perm->mdims;
415             $perm = $perm->transpose if $pdims[1] == 1;
416             }
417             elsif ( ref( $_[0] ) eq "PDL" ) {
418             $perm = shift->copy->dummy(1);
419             bless $perm, 'PDL::Matrix';
420             }
421             elsif ( ref( $_[0] ) eq "ARRAY" ) {
422             $perm = pdl( $_[0] );
423             }
424             else {
425             $perm = pdl(@_);
426             }
427              
428             my ( $c, $r ) = $matrix->dims();
429              
430             # print "$c columns, $r rows, perm has ".eval($perm->nelem)." entries\n";
431             return undef if ( $r != $perm->nelem );
432              
433             # print "permrows called\n";
434             my $cnt;
435             for ( $cnt = 0 ; $cnt < $r ; $cnt++ ) {
436             my $xchpos = which( $perm->slice("(0),:") == $cnt )->at(0);
437             if ( $xchpos != $cnt ) {
438              
439             # print "exchange rows $xchpos and $cnt\n";
440             $matrix->xrow( $xchpos, $cnt );
441             $perm->xcol( $xchpos, $cnt );
442             }
443             }
444              
445             return $matrix;
446             }
447              
448             =head2 method kernel
449              
450             $ker = $m->kernel;
451             Returns the kernel of matrix $m, i.e. the matrix with linearly independent
452             column vectors $c satisfying the equation $m x $c = 0.
453              
454             =cut
455              
456             sub PDL::Matrix::kernel {
457             my $matrix = shift->copy;
458              
459             my ( $rem, $perm, $rank ) = $matrix->row_echelon_int();
460              
461             my ( $cols, $rows ) = $rem->dims();
462              
463             my $vec;
464             my @veclist = ();
465              
466             my ( $cnt, $r );
467             for ( $cnt = $rank ; $cnt < $cols ; $cnt++ ) {
468              
469             # print "Solution number ".eval($cnt-$rank+1).":\n";
470             $vec = zeroes($cols);
471             set $vec, $cnt, 1;
472              
473             for ( $r = $rank - 1 ; $r >= 0 ; $r-- ) {
474              
475             # print "Calculating row $r:\n";
476             my ($row) = $rem->slice("($r),:");
477             my $sum = inner( $row, $vec );
478              
479             # ensure only integers remain
480             my ( $gcd, $redsum, $redpivot ) =
481             euclid( $sum, $rem->at( $r, $r ) );
482             $vec *= $redpivot;
483             set $vec, $r, -( $sum * $redpivot / $rem->at( $r, $r ) );
484              
485             # print "$vec\n";
486             }
487              
488             push( @veclist, $vec );
489             }
490              
491             # print "kernel: Rank=$rank,Columns=$cols,vecs:@veclist\n";
492             # my $retmat = cat(@veclist)->xchg(0,1);
493             # $retmat->permrows($perm);
494             # return $retmat;
495             return null unless @veclist;
496             my $retmat = bless cat(@veclist)->xchg( 0, 1 ), ref($matrix);
497             return $retmat->permrows($perm);
498             }
499              
500             =head2 method invert # probably obsolete!!!! Check with PDL::Matrix / PDL::MatrixOps
501              
502             $inv = $m->invert;
503             Returns the inverse of $m, undef if $m is not invertible.
504              
505             =cut
506              
507             sub PDL::Matrix::invert {
508             my $matrix = shift->copy;
509              
510             # print "ref(matrix) is ".ref($matrix)."\n";
511             # my ($pkg,$file,$line)=caller();
512              
513             my @dims = $matrix->dims;
514              
515             if ( $dims[0] != $dims[1] ) {
516             croak("Inverse for non-square matrix not defined!\n");
517             return undef;
518             }
519              
520             my $n = $dims[0]; # n x n matrix
521              
522             # $inverse = 1
523             # print "matrix $matrix";
524             # print "invert called from package $pkg, file $file, line $line (n=$n)\n";
525             # my $inverse = ref($matrix)->new($n,$n);
526             my $inverse = mzeroes( $n, $n );
527              
528             # (my $tmp = $inverse->diagonal(0,1)) .= 1; # some strange error!!!!
529             # for (my $del=0;$del<$n;$del++) {set($inverse,$del,$del,1)};
530             $inverse->diagonal( 0, 1 ) .= 1;
531              
532             for ( my $colnr = 0 ; $colnr < $n ; $colnr++ ) {
533              
534             # find pivot element
535             my $colnz = which( $matrix->slice(":,($colnr)")->sever != 0 );
536             my $ppiv = which( $colnz >= $colnr );
537             if ( $ppiv->nelem == 0 ) {
538             print "Matrix not invertible\n";
539             return undef;
540             }
541             my $pivotrow = $colnz->at( $ppiv->at(0) );
542              
543             if ( $pivotrow != $colnr ) {
544             $matrix->xrow( $colnr, $pivotrow );
545             $inverse->xrow( $colnr, $pivotrow );
546             }
547              
548             my $akk = $matrix->at( $colnr, $colnr ); # this is a_{kk}
549             for ( my $rownr = 0 ; $rownr < $n ; $rownr++ ) {
550             if ( $rownr != $colnr ) {
551              
552             # my $q = - $matrix->at($colnr,$rownr) / $akk;
553             my $q = -$matrix->at( $rownr, $colnr ) / $akk;
554             $matrix->addrows( $rownr, $colnr, $q );
555             $inverse->addrows( $rownr, $colnr, $q );
556             }
557             }
558              
559             # print "Matrix now (column $colnr) : $matrix\n";
560             # print "Inverse now (column $colnr) : $inverse\n";
561              
562             }
563              
564             # my $diag = $matrix->diagonal(0,1);
565             # if (!(which($diag==0)->isempty)) {
566             # print "Matrix non inversible!\n";
567             # return undef;
568             # }
569              
570             for ( my $d = 0 ; $d < $n ; $d++ ) {
571             if ( $matrix->at( $d, $d ) == 0 ) {
572             croak("Matrix non inversible!");
573             return undef;
574             }
575              
576             # ($tmp = $inverse->slice(":,($d)")) /= $diag->at($d);
577             my $tmp;
578             ( $tmp = $inverse->slice("($d),:") ) /= $matrix->at( $d, $d );
579             }
580              
581             # print "inverse: $inverse";
582             return $inverse;
583             }
584              
585             =head2 method char_pol
586              
587             $coefficient_vector = $m->char_pol;
588              
589             Returns a PDL with the coefficients of the characteristic polynomial of $m.
590              
591             EXAMPLE:
592             [1 2 1]
593             The matrix M=[2 0 3] has the characeristic polynomial
594             [1 1 1]
595             p(x) = det(M-x1) = a_3 x^3 + a_2 x^2 + a_1 x + a_0 = -x^3+2x^2+7x+1.
596              
597             $m = mdpl [[1,2,1],[2,0,3],[1,1,1]];
598             $cp = $m->char_pol;
599             This returns [1,7,2,-1]. $cp->at(n) contains the coefficient a_n.
600              
601             =cut
602              
603             sub PDL::Matrix::char_pol {
604             my $matrix = shift;
605              
606             my @dims = $matrix->dims;
607             my $n = $dims[0];
608              
609             # print "Dimension: $n\nMatrix: $matrix\n";
610             my $lvec = @_ ? shift: ones($n);
611              
612             # print "lvec: $lvec\n";
613             # if ($n == 1) {
614             # return $lvec->at(0) == 1 ? [$matrix->at(0,0),-1] : [$matrix->at(0,0),0];
615             # }
616             return pdl [ $matrix->at( 0, 0 ), -$lvec->at(0) ] if $n == 1;
617              
618             my $lambdas = which( $lvec == 1 );
619              
620             # print "Lambdas at $lambdas\n";
621             return pdl [ eval( $matrix->determinant ) ] if ( $lambdas->nelem == 0 );
622              
623             my $lambda = $lambdas->at(0);
624              
625             # print "...choosing lambda at $lambda\n";
626             set( $lvec, $lambda, 0 ); # now recursively calculate...
627              
628             # print "calling char_pol with $lvec\n";
629             # print ">>>>>>>>>>>>>\n";
630             my $coeff1 = $matrix->char_pol( $lvec->copy );
631              
632             # print "<<<<<<<<<<<<< (lvec is $lvec)\n";
633              
634             # print "Coefficients due to leaving out lambda: $coeff1\n";
635              
636             # print "lvec before splicing: $lvec\n";
637             my @tmpl = $lvec->list;
638             splice( @tmpl, $lambda, 1 );
639             $lvec = pdl(@tmpl);
640             $lvec = $lvec->dummy(0) if $lvec->dims == 0;
641              
642             # print "lvec after splicing: $lvec\n";
643              
644             # reduce matrix
645             my $redmat = $matrix->cutrow($lambda)->cutcol($lambda);
646              
647             # $redmat = $redmat->cutcol($lambda);
648              
649             # print "calling char_pol with $lvec and reduced matrix $redmat\n";
650             # print ">>>>>>>>>>>>>\n";
651             my $coeff2 = $redmat->char_pol( $lvec->copy );
652              
653             # print "<<<<<<<<<<<<<\n";
654              
655             # print "Coefficients due to lambda: $coeff2\n";
656              
657             # unshift(@$coeff2,0);
658             $coeff2 = pdl [ 0, $coeff2->list ];
659              
660             # print "after unshifting: ".join(',',@$coeff2)."\n";
661              
662             # my $res = [];
663             # for (my $i=0;$i<@$coeff2;$i++) {
664             # $res->[$i] = $coeff1->[$i] - $coeff2->[$i];
665             # }
666             $coeff1 =
667             pdl [ $coeff1->list, zeroes( $coeff2->nelem - $coeff1->nelem )->list ]
668             if $coeff2->nelem != $coeff1->nelem;
669             my $res = $coeff1 - $coeff2;
670              
671             # print "returning ".join(',',@$res)."\n";
672             # if ($res->[@$res-1] < 0) {
673             # foreach my $r (@$res) {
674             # $r *= -1;
675             # }
676             # }
677             # $res *= -1 if ($res->at($res->nelem-1) < 0);
678             # return bless $res, 'PDL::Mat';
679             return $res;
680             }
681              
682             =head2 method to_Hurwitz
683              
684             $hurwitz_matrix = $m->to_Hurwitz;
685              
686             Returns the Hurwitz matrix.
687             The coefficients of the Hurwitz matrix are defined to be:
688             H_ij = a_{n-2i+j} if 0 < 2i-j <= n, 0 otherwise where a_n are the
689             coefficients of the characteristic polynomial.
690              
691             =cut
692              
693             sub PDL::Matrix::to_Hurwitz {
694             my $matrix = shift;
695              
696             # my @dims = $matrix->dims;
697             my $cp_coeff;
698              
699             # if (ref($matrix) =~ /PDL/) {
700             if ( !$matrix->isempty ) {
701             $cp_coeff = $matrix->char_pol;
702              
703             # print "getting charac. pol. :".join(',',@$cp_coeff)."\n";
704             }
705             else {
706              
707             # $cp_coeff = $matrix;
708             # croak("to_Hurwitz: matrix is not 2-dim!");
709             $cp_coeff = shift;
710             }
711              
712             # my $n = @$cp_coeff-1;
713             my $n = $cp_coeff->nelem - 1;
714              
715             # my $hurwitz = ref($matrix)->new($n,$n);
716             my $hurwitz = mzeroes( $n, $n );
717             for ( my $i = 1 ; $i <= $n ; $i++ ) {
718             for (
719             my $j = 2 * $i - $n > 1 ? 2 * $i - $n : 1 ;
720             $j <= $n && $j <= 2 * $i ;
721             $j++
722             )
723             {
724              
725             # print "Setting ($i,$j) to a_".eval($n-2*$i+$j)."\n";
726             # set($hurwitz,$i-1,$j-1,$cp_coeff->at($n-2*$i+$j));
727             set( $hurwitz, $j - 1, $i - 1, $cp_coeff->at( $n - 2 * $i + $j ) );
728             }
729             }
730              
731             return $hurwitz;
732             }
733              
734             =head2 method Hurwitz_crit
735              
736             if ($m->Hurwitz_crit) { ... }
737              
738             Returns true if the Hurwitz condition is fulfilled, i.e. if all
739             sub-determinants are larger than zero and a_n/a_0 > 0 for all n >= 1.
740              
741             =cut
742              
743             sub PDL::Matrix::Hurwitz_crit {
744             my $matrix = shift;
745              
746             my $cp_coeff = $matrix->char_pol;
747              
748             $cp_coeff *= -1 if $cp_coeff->at( $cp_coeff->nelem - 1 ) < 0;
749              
750             # foreach my $c (@$cp_coeff) {
751             # return(0) unless $c > 0;
752             # }
753             return (0) unless which( $cp_coeff > 0 )->nelem == $cp_coeff->nelem;
754              
755             # my $hurwitz = $cp_coeff->to_Hurwitz;
756             my $hurwitz = PDL::Matrix::null->to_Hurwitz($cp_coeff);
757              
758             return $hurwitz->is_pos_def;
759             }
760              
761             1;