http://www.perlmonks.org?node_id=1002795

supriyoch_2008 has asked for the wisdom of the Perl Monks concerning the following question:

Hi PerlMonks,

I am interested to get the inverse of a square matrix (given below) using perl script. I have written a script i.e. inv1.pl (given below) where the matrix is placed in the script itself at specific location. It makes use of the math_module.pm. The script inv1.pl works well and it gives the correct result for inverse matrix.

But I want to input the square matrix from a text file (d1.txt) using <STDIN> in the script inv2.pl. The matrix is easily uploaded in the inv2.pl but it does not give the result. I am at my wit's end to solve this problem. I request the perl monks to look into the problem and provide constructive suggestions. I want to input the matrix as a text file in the script inv2.pl so that I can use it for getting the inverse of any higher order matrix like 12x12. It will be easy for me if only the matrix values are written as comma separated values in input text file without writing the $m2= and braces i.e.[ ] with values in d1.txt file.

Here goes the script inv1.pl which gives correct result:

#!/usr/bin/perl use warnings; use math_module; ### TO PRODUCE THE INVERSE OF A SQUARE MATRIX in PERL: # In a square matrix the number of rows and columns are equal. # Here we intend to get the inverse of a 4x4 square matrix. ###################################################### print"\n Here we intend to get the inverse of a 4x4 square matrix.\n"; print"\n Enter the Number of Rows i.e 4: "; $n= <STDIN>; chomp$n; $N=$n; # Input the matrix below: $m2[0]=[3,2,1,1]; $m2[1]=[1,0,2,2]; $m2[2]=[4,1,3,3]; $m2[3]=[1,2,3,4]; ### Data Ends Here ########################### @inv=math_module::get_inverse($N,@m2); ############################################## # Checking if the inverse matrix is correct: ############################################## for ($i=0; $i<$N; $i++) { for ($j=0; $j<$N; $j++) { $t=0; for($k=0; $k<$N; $k++) { $t=$t+ $m2[$i][$k]*$inv[$k][$j]; } } } ################################################# print "\n\n The Inverse Matrix is:\n\n"; ## Use of for LOOP: for ($i=0; $i<$N; $i++) { for ($j=0; $j<$N; $j++) { print $inv[$i][$j]; print " "; } print "\n\n"; } exit;

The math_module.pm is given below:

package math_module; sub get_det($\@) { my ($m__size, $m_matrix)=@_; my $m_det=0; my @new_matrix; my $i_mat; if ($m__size ==1) { return $$m_matrix[0][0]; # Line 10 } if ( $m__size ==2) { $m_det=$$m_matrix[0][0]*$$m_matrix[1][1]-$$m_matrix[0][1]*$$m_m +atrix[1][0]; return $m_det; } else { for ($i_mat=0; $i_mat<$m__size; $i_mat++) { # Line 20 for ($m_r=1; $m_r<$m__size; $m_r++) { my $k_ajust=0; # Line 23 for ($m_k=0; $m_k < $m__size; $m_k++) { if ($m_k != $i_mat) { $new_matrix[$m_r-1][$m_k-$k_ajust] =$$m_matrix[ +$m_r][$m_k]; } else # Line 30 { $k_ajust=1; } } } $m_det=$m_det + (-1)**(2+$i_mat) * $$m_matrix[0][$i_mat] * get_ +det($m__size-1, \@new_matrix); } return $m_det; } } # Line 40 sub get_inverse ($\@) { my ($matrix_size, $m) =@_; for ($i=0; $i<$matrix_size; $i++) { for ($j=0; $j<$matrix_size; $j++) { $m5[$i][$j]=$$m[$i][$j]; $mt[$i][$j]=$$m[$j][$i]; } # Line 50 } for ($ii=0; $ii<$matrix_size; $ii++) { for ($jj=0; $jj<$matrix_size; $jj++) { @aa=remove_row_col($ii,$jj,$matrix_size, @m5); $inverse_matrix[$ii][$jj]=math_module::get_det($matrix_size-1, @a +a) / math_module::get_det($matrix_size,@mt); $inverse_matrix[$ii][$jj]=((-1)**($ii+$jj+2))*$inverse_matrix[$ii +][$jj]; } } # Line 60 for ($ii=0; $ii<$matrix_size; $ii++) { for ($jj=0; $jj<$matrix_size; $jj++) { $inverse_matrix1[$ii][$jj]=$inverse_matrix[$jj][$ii]; } } return @inverse_matrix1; } sub remove_row_col ($$$@) { my ($X_X, $Y_Y, $matrix_size, @matrix) =@_; for($i_i=0; $i_i < $matrix_size-1; $i_i++) { for($j_j=0; $j_j < $matrix_size; $j_j++) { if ($i_i < $X_X) { $new_matrix[$i_i][$j_j]=$matrix[$i_i][$j_j]; } if ($i_i >= $X_X) { $new_matrix[$i_i][$j_j]=$matrix[$i_i+1][$j_j]; } } } for($i_i=0; $i_i < $matrix_size-1; $i_i++) { for($j_j=0; $j_j < $matrix_size-1; $j_j++) { if ($j_j < $Y_Y) { $new_matrix[$i_i][$j_j]=$new_matrix[$i_i][$j_j]; } if ($j_j >= $Y_Y) { $new_matrix[$i_i][$j_j]=$new_matrix[$i_i][$j_j+1]; } } $new_matrix[$i_i][$matrix_size-1]=""; } return @new_matrix; } return 1;

Here goes the correct results of inv1.pl

C:\Users\x>cd desktop C:\Users\x\Desktop>inv1.pl Here we intend to get the inverse of a 4x4 square matrix. Enter the Number of Rows i.e 4: 4 The Inverse Matrix is: -0.4 -1 0.8 0 1 1 -1 0 2.4 5 -2.8 -1 -2.2 -4 2.4 1

I have written the script inv2.pl as follows but it does not work with input file d1.txt:

#!/usr/bin/perl ### TO PRODUCE THE INVERSE OF A SQUARE MATRIX (ANY SIZE) in PERL: use warnings; use math_module; ################################## # Inverse matrix calculation: ################################## print"\n Enter the Number of Rows i.e. 4 here: "; $n= <STDIN>; chomp$n; $N=$n; ## Input the matrix text file: print "\n\n Please type the filename(.txt) i.e. d1.txt: "; $DNAfilename = <STDIN>; chomp $DNAfilename; ## To remove white space: $DNAfilename=~ s/\s//gis; # open the file, or exit unless ( open(DNAFILE, $DNAfilename) ) { print "Cannot open file \"$DNAfilename\"\n\n"; exit; } # Line 10 @a = <DNAFILE>; @m2=@a; print"\n The contents of input matrix file are (array m2):\n\n @m2\n"; ### Data Ends Here ########################### @inv=math_module::get_inverse($N,@m2); ############################################## # Checking if the inverse matrix is correct: ############################################## for ($i=0; $i<$N; $i++) { for ($j=0; $j<$N; $j++) { $t=0; for($k=0; $k<$N; $k++) { $t=$t+ $m2[$i][$k]*$inv[$k][$j]; } } } ################################################# # Output inverse matrix: ################################################## print "\n\n The Inverse Matrix is:\n\n"; ## Use of for LOOP: for ($i=0; $i<$N; $i++) { for ($j=0; $j<$N; $j++) { print $inv[$i][$j]; print " "; } print "\n\n"; } exit;

The contents of text file d1.txt is given below. It will be easy for me if only the comma separated values

of the matrix like  3,2,1,1 are used in input file d1.txt without writing the $m2= and braces i.e.[ ] with values in d1.txt file.

$m2[0]=[3,2,1,1]; $m2[1]=[1,0,2,2]; $m2[2]=[4,1,3,3]; $m2[3]=[1,2,3,4];

The incorrect results of inv2.pl goes like:

C:\Users\x>cd desktop C:\Users\x\Desktop>inv2.pl Enter the Number of Rows i.e. 4 here: 4 Please type the filename(.txt) i.e. d1.txt: d1.txt The contents of input matrix file are (array m2): $m2[0]=[3,2,1,1]; $m2[1]=[1,0,2,2]; $m2[2]=[4,1,3,3]; $m2[3]=[1,2,3,4]; Illegal division by zero at C:/Perl/lib/math_module.pm line 60, <DNAFI +LE>line 4.

The input file d1.txt for inv2.pl should look like:

3,2,1,1 1,0,2,2 4,1,3,3 1,2,3,4
  • Comment on Is it possible to get the inverse of a square matrix to be input as a text file using STDIN?
  • Select or Download Code

Replies are listed 'Best First'.
Re: Is it possible to get the inverse of a square matrix to be input as a text file using STDIN?
by space_monk (Chaplain) on Nov 08, 2012 at 05:30 UTC
    One of the things to learn about any programming language is that your problem has almost certainly been addressed in some way before, and in this case there are PERL CPAN libraries like Math::Matrix and Math::MatrixReal to avoid having to do any work. Using CPAN libraries also makes your code more maintainable as other people will have more chance of having come across the library concerned on CPAN than of understanding your custom code.

    In answer to the input question, (and modifying an earlier answer) why not get your input as

    while (<>) do { push @m2, [ split /,/, $_ ]; }
    and then your script can get your input from any file or stdin....
    e.g.
    ./myprogram.pl < d1.txt

      Hi, space_monk

      Thank you very much for providing valuable suggestions, particularly about CPAN. The idea did not come to my mind before posting the question in Seekers of Perl Wisdom. I shall try to use Math::Matrix perl module.

      With regards

Re: Is it possible to get the inverse of a square matrix to be input as a text file using STDIN?
by 2teez (Vicar) on Nov 08, 2012 at 04:56 UTC

    Hi, supriyoch_2008,

    • Please you may use strict in your script.
    • Replace these lines in inv2.pl:
      ## Input the matrix text file: print "\n\n Please type the filename(.txt) i.e. d1.txt: "; $DNAfilename = <STDIN>; chomp $DNAfilename; ## To remove white space: $DNAfilename=~ s/\s//gis; # open the file, or exit unless ( open(DNAFILE, $DNAfilename) ) { print "Cannot open file \"$DNAfilename\"\n\n"; exit; } # Line 10 @a = <DNAFILE>; @m2=@a;
      with these:
      my @m2; ## Input the matrix text file: print "\n\n Please type the filename(.txt) i.e. d1.txt: "; chomp( my $filename = <STDIN> ); open my $fh, '<', $filename or die "can't open file $!"; while (<$fh>) { push @m2, [ split /,/, $_ ]; } close $fh or die "can't close file: $!";
    Note:
    Ofcourse, your script(s) can be improved upon, but these should give you what you want to start with.
    Hope it helps.

    If you tell me, I'll forget.
    If you show me, I'll remember.
    if you involve me, I'll understand.
    --- Author unknown to me

      Hi, 2teez,

      Thanks for your suggestions with code. I shall try.

      Regards