Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight
 
PerlMonks  

Matrix Multiplier

by zdog (Priest)
on Sep 09, 2000 at 07:35 UTC ( [id://31735]=sourcecode: print w/replies, xml ) Need Help??
Category: Miscellaneous
Author/Contact Info Zenon Zabinski | zdog7@hotmail.com
Description: Being in Algebra II, my class is learning about matrices. Finding that multiplying matrices takes so long, I automated it using Perl with this script.

Suggestions are not only welcomed, but wanted!

Thanks to jcwren, jlp, BlaisePascal, and merlyn's Programming Perl for helping me out.


#!/usr/bin/perl -w
#
# Title    : Matrix Multiplier
# Author   : Zenon Zabinski
# E-Mail   : zdog7@hotmail.com
#
# Versions : 1.00 - Initial product, pun intended
#            1.10 - With several optimizations
#
# Help     : When prompted for each matrix, enter each row of the matr
+ix 
#            followed by an <ENTER> with each element separated by a 
#            space. After you have no more rows left, press enter with
+out
#            typing anything when prompted for the next row. The only 
+keys
#            you should use are: enter, space, and the numbers. Happy
#            matrix multiplying. :-)
#
# Copyright (C) 2000 Zenon Zabinski. No rights reserved.
# 

use strict;

my (@firstfactor, @secondfactor, @product);

@firstfactor  = getmatrix   ("First");
@secondfactor = getmatrix   ("Second");
@product      = getproduct  (\@firstfactor, \@secondfactor);
                
printmatrix (@product);


# SUBS:


sub getmatrix {
  my @matrix;
  print "\n". shift() ." matrix:\n";
  while (<STDIN>) {
    chop;
    last if (!$_);
    if (/[^0-9 ]/) { die "Not a legal matrix row!\n"; }
    push @matrix, [ split / / ];
  }
  return @matrix;
}

sub checkmatrix { 
  my $rowlength = $#{$_[0]};
  for (@_) {
    $rowlength == $#$_ or die "Number of columns was not consistent!\n
+";
  } 
}

sub printmatrix {
  foreach (@_) { 
    print "[ ";
    printf("%-4s", " $_") foreach (@$_); 
    print " ]\n";
  }
}

sub getdimen {
  my $rows = @_;
  my $cols = $#{$_[0]};
  return ($rows - 1, $cols);
}

sub getproduct {
  my ($a, $b) = @_;
  my @product;
  checkmatrix(@$a);
  checkmatrix(@$b);
  my ($arow, $acol) = getdimen(@$a);
  my ($brow, $bcol) = getdimen(@$b);
  $acol == $brow or die "The matrices can't be multiplied!\n";

  for my $i (0..$arow) {
    for my $j (0..$bcol) {
      for my $k (0..$acol) {
        $product[$i][$j] += $$a[$i][$k] * $$b[$k][$j];
      }
    }
  }
  return @product;
}
Replies are listed 'Best First'.
RE: Matrix Multiplier
by Cirollo (Friar) on Sep 09, 2000 at 09:01 UTC
    You might want to take a look at Math::Matrix. In addition to multiplying matrices, it does transposition and concatenation of matrices, and will solve the system represented by an augmented matrix.
      Using the Math::Matrix package (or PDL, for that matter) would prevent zdog from the learning experience of doing it himself.

      There are many valid reasons to reinvent the wheel: One reason is to understand how the wheel works.

        And part of understanding how the wheel works is to make sure you are familiar with the existing art. We are trying to point out the similar solutions, so that the reinvention of the wheel can benefit all, not just make busy work for one person.

        -- Randal L. Schwartz, Perl hacker

RE: Matrix Multiplier
by BlaisePascal (Monk) on Sep 10, 2000 at 08:49 UTC
    Y'know, until now, I never noticed that "d/l code" anchor right below the "comment on this" anchor. The things you learn every day...

    Suggestions subroutine by subroutine...

    getmatrix: Pre-defining $input to 1 to prime the while, then checking for valid input at the bottom after you have read it is a little hard to understand. You could accomplish the same thing by making the read from <STDIN> as part of the while, with <while ($input = <STDIN>). That would allow you to eliminate the $input &&... at the end as well.

    Going one step further, every use of $input at that point is in locations that default to $_, so you can eliminate it all together. I'd recommend, for clarity sake, also renaming @input to @matrix, since that's what it is.

    sub getmatrix { my $what = shift; my @matrix; while (<STDIN>) { chop; if (/^0-9 ]/) die "Not a legal matrix row!\n"; } push @matrix, split / /; } return @matrix; }
    checkmatrix: This is short enough that there really isn't much to suggest for improvement. The one thing I would suggest, though is moving the $rowlength assignment out of the loop. First, for clarity, second for efficiency. That and get rid of @row, it's unnecessary.

    And I just noticed... ne is for string comparison, not numeric. Since you are dealing with numbers, use !=.

    sub checkmatrix { my $rowlength = @$_[0]; for (@_) { $rowlength == @$_ or die "Number of columns was not consistant!\n" +; } }
    As a matter of style, I'd have this return some boolean value rather than simply up and dying. It allows the caller to decide what to do with a broken matrix.

    printmatrix: If that's how you want to format matrices, good enough for me. Not much to criticize here. (This included for completeness).

    getdimen: This is really not clear what you are doing. Obviously, you wanted to get the number of rows and columns, but it seems a round-about way to get them. Especially the for loop with an unguarded last, meaning the loop will execute only once. Not clear at all. I'll do a total rewrite:

    sub getdimen { my $rows = @_; # get number of rows my $cols = @$_[0]; # get number of elems in first row return ($rows,$columns); }
    getproduct: This is the heart of your routine. The major thing I'll mention here is that you can do something like $product[$i][$j] += $a[$i][$k]*$b[$k][$j]; directly, and perl will DWIM correctly. You don't even need to pre-initialize @product. Using that would make your matrix multiply much simpler to understand. This also eliminates most of your "temp" variables.

    The other thing I'd do is make product responsible for validating it's arguments and extracting the info it needs directly, rather than expect it to be called in.

    sub getproduct { my ($aref, $bref) = @_; my @a = @$aref; my @b = @$bref; my @product; checkmatrix(@a); checkmatrix(@b); my ($arow, $acol) = getdimen(@a); my ($brow, $bcol) = getdimen(@b); $acol == $brow or die "The matrices can't be multiplied!\n"; for $i (0..$arow) { for $j (0..$bcol) { for $k (0..$acol) { $product[$i][$j] += $a[$i][$k] * $b[$k][$j]; } } } return @product; }
    Now, if I got the multiplication wrong, it's easy to see what my mistake was (I haven't done linear algebra in a while, I might have the rows/columns mixed up, for instance).

    Perl is very good at having the features that DWIM when you need them to. Trying to go overboard with pushes, temp arrays, etc are not necessarily the way to go. As a general rule, I'd say that if you have to force it to do what you want, there might be an easier way...

    Hope this helps...

      You have the multiplication right. But one major comment. Checking that the matrixes look right is fairly time intensive. You really don't want that effort to be spent on every multiply. Likewise the dimensions. So this is a place where OO fits well. I won't provide that solution so zdog can have the experience of writing it.
RE (tilly) 1: Matrix Multiplier
by tilly (Archbishop) on Sep 09, 2000 at 14:30 UTC
    For intensive numerical work with matrices, you can take a look at the PDL module.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: sourcecode [id://31735]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others learning in the Monastery: (3)
As of 2024-04-18 00:26 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found