I wanted to test the rank of a matrix without the need to assume the values can be converted to fractions, as necessary in the code proposed by Nandor Sieben.
I also ran into a limitation that order_LR apparently (even with the fuzzy comparison to 0) only works well for square matrices.
The following is a provisional version of rank.pl which seem to work for me in my first test cases.
It has header code to handle the case of non-square matrices by adding 0 rows when there are more rows than columns. (The case of more columns than rows is handled by calling rank() recursively on the transpose matrix).
The indexing in the code is done using MathObjects 1-based indexing for rows/columns, so somewhat different that what was in MatrixReal1.pm which used Perl's 0-based indexing.
Testing and comments on this code would be appreciated.
From prior experience with Matlab, which allows passing in a tolerance level as a second argument to rank(A,tol) it may be necessary to consider adding such an optional second argument which could adjust the values of zeroLevelTol for the Context in which the comparison to Real("0") is made.
# Coded by Nathan Wallach, May 2019
# based on Davide Cervone's recommendation from
# http://webwork.maa.org/moodle/mod/forum/discuss.php?d=3194
# as the current $Matrix->order_LR does not use fuzzy comparisons
# so does not give good results.
sub rank { # It assumes that a MathObject matrix object is sent
my $MM1 = shift;
if ( $MM1->class ne "Matrix") {
return -1; # Not a matrix
}
# For the case it is square
my $MM = $MM1;
my ($Rrows,$Rcols) = $MM1->dimensions;
if ( ( $Rrows <= 0 ) || ( $Rcols <= 0 ) ) {
return -1; # Not a matrix
}
if ( $Rrows < $Rcols ) {
# pad to make it square
my @rows = ();
my $i = 1;
for ( $i = 1 ; $i <= $Rrows ; $i++ ) {
push( @rows, $MM1->row($i) );
}
while ( $i <= $Rrows ) {
# pad with zero rows
push( @rows, ( 0 * $MM1->row(1) ) );
$i++;
}
$MM = Matrix( @rows );
} elsif ( $Rrows > $Rcols ) {
return( rank( $MM1->transpose ) );
}
# Davide's approach from http://webwork.maa.org/moodle/mod/forum/discuss.php?d=3194
my $tempR = $MM->R;
($Rrows,$Rcols) = $tempR->dimensions;
my $rank;
for ( $rank = $Rrows ; $rank >= 1; $rank-- ) {
last if ( $tempR->element($rank,$rank) != Real("0") );
}
return( $rank );
}
1;