Search code examples
perlmultidimensional-arrayarrayref

How to calculate number of rows of a referenced array in Perl


A part of my code calculates inverse of a matrix (generated previously in the code) with dimensions more than 300 X 300. I want to use the elements of the inversed matrix further in the code. Have used the below code for this, trying with only 5X5 matrix for testing:

use strict;
use warnings;
use Math::MatrixReal;
my @a=();                           #a is the matrix obtained
$a[0][0]=0.18761134;
$a[0][1]=0.010779401;               #Have hard-coded the values here till $a[4][4] 

my $ref_a = \@a;
my $b = Math::MatrixReal->new_from_rows($ref_a);
my $b_inv = $b->inverse();
print "\n Inverse is\n",$b_inv;                      #prints correct inverse
print "\n\nTest printing elements\n";
print $$b_inv[0][1][1],"\n";                         #prints the correct element

my $row_b=scalar(@{$b});   
print "Number of rows in b: ",$row_b,"\n";           #prints 6
my $col_b=@{$$b[0]};
print "Columns in b: ",$col_b,"\n";                  #prints 5

my $row_binv=scalar(@$b_inv);
print "Number of rows in b_inv: ",$row_binv,"\n";    #prints 3
my $col_binv=@{$$b_inv[0]};
print "Number of columns in b_inv ",$col_binv,"\n";  #prints 5

I am not able to understand

  1. why the output of number of rows for both b and b_inv is wrong? How to get the correct value of number of rows?

  2. That although the syntax of printing elements of a referenced array is $$b_inv[1][1], I get the correct output when I use $$b_inv[0][1][1]


Solution

  • You are creating a Math::MatrixReal matrix object, and then accessing it as a simple Perl array. Poking around inside a Perl object indiscriminately is wrong, and you must use the methods defined in the documentation

    In particular, your statement

    print $$b_inv[0][1][1],"\n";    # prints the correct element
    

    accesses a three-dimensional array, and there is no way of knowing what the "correct element" should be for this without reading the code of the module

    This modification sets up a 5 x 5 identity matrix (in future, please provide data that we can use to reproduce your results) and takes its inverse. The values output are derived using the object's methods as I described and are all correct. Note that the rows and columns are indexed from one instead of from zero that you would expect for Perl arrays

    use strict;
    use warnings 'all';
    
    use Math::MatrixReal;
    
    my @arr = (
        [1, 0, 0, 0, 0],
        [0, 1, 0, 0, 0],
        [0, 0, 1, 0, 0],
        [0, 0, 0, 1, 0],
        [0, 0, 0, 0, 1],
    );
    
    my $ref_a = \@arr;
    my $b = Math::MatrixReal->new_from_rows(\@arr);
    my $b_inv = $b->inverse;
    
    print "\nInverse is\n", $b_inv;
    print "\n\nTest printing elements\n";
    print $b_inv->element($_, $_), "\n" for 1 .. 5;
    
    my ($row_b, $col_b) = $b->dim;;
    print "Number of rows in b: $row_b\n";           # prints 5
    print "Columns in b: $col_b\n";                  # prints 5
    
    my ($row_binv, $col_binv) = $b_inv->dim;;
    print "Number of rows in b_inv: $row_binv\n";    # prints 5
    print "Number of columns in b_inv $col_binv\n";  # prints 5
    

    output

    Inverse is
    [  1.000000000000E+000  0.000000000000E+000  0.000000000000E+000  0.000000000000E+000  0.000000000000E+000 ]
    [  0.000000000000E+000  1.000000000000E+000  0.000000000000E+000  0.000000000000E+000  0.000000000000E+000 ]
    [  0.000000000000E+000  0.000000000000E+000  1.000000000000E+000  0.000000000000E+000  0.000000000000E+000 ]
    [  0.000000000000E+000  0.000000000000E+000  0.000000000000E+000  1.000000000000E+000  0.000000000000E+000 ]
    [  0.000000000000E+000  0.000000000000E+000  0.000000000000E+000  0.000000000000E+000  1.000000000000E+000 ]
    
    
    Test printing elements
    1
    1
    1
    1
    1
    Number of rows in b: 5
    Columns in b: 5
    Number of rows in b_inv: 5
    Number of columns in b_inv 5