Newsgroups: comp.lang.apl
Path: watmath!watserv1!utgpu!cs.utexas.edu!wupost!darwin.sura.net!haven.umd.edu!socrates!socrates!rockwell
From: rockwell@socrates.umd.edu (Raul Deluth Miller-Rockwell)
Subject: Re: APL execution efficiency revisited
In-Reply-To: andrew@rentec.com's message of 25 Mar 92 23:15:39 GMT
Message-ID: <ROCKWELL.92Mar26223823@socrates.umd.edu>
Sender: rockwell@socrates.umd.edu (Raul Deluth Miller-Rockwell)
Organization: Traveller
References: <ROCKWELL.92Mar23224842@socrates.umd.edu> <756@kepler1.rentec.com>
	<ROCKWELL.92Mar25000046@socrates.umd.edu> <762@kepler1.rentec.com>
Date: Fri, 27 Mar 1992 03:38:23 GMT
Lines: 186

I wrote:
   >Um.. before I get into the mechanisms one might use for
   >implementing this, what sort of applications does this have?

Andrew Mullhaupt:
   There are three big applications of this:

           1. Showing that you can't _really_ expect to avoid bad
              inner and outer products with any great generality.

I don't see why you say this.

           2. Introducing computer science students to dynamic
              programming, and providing a test problem for fast
              dynamic programming ideas.

And?  I mean, what's the application for this style of dynamic
programming? 

           3. Giving an example where most people _think_ they know
              how to write fast code, but they don't, unless they've
              studied computer science, not just programming.

Fast code depends on an understanding of the amount of time required
to evaluate the underlying operations.  In any event, I'm not sure I
understand the thrust of this statement.

   You mean _real_ applications? This is not a good question for
   language design.

Sure it is.  It can give you a feel for what you're working with, can
inspire good names for the the functions you define, and can be useful
in testing the results.

Anyways, below is a script I put together to express what I believe is
a solution to this problem.  Care to comment on it?

-- 
Raul Deluth Miller-Rockwell                   <rockwell@socrates.umd.edu>



NB. fake dot product, so we can look at results

M        =. <@":"0@i.
plus     =. (    [  , '+'"1 ,    ]  )&.>
times    =. (    [  , '*'"1 ,    ]  )&.>
paren    =. ( '('"1 ,    ]  , ')'"1 )&.>
dot      =. paren @(plus/ . times)

NB. dry run.
NB. I expect that reducing the 'big axis' first results in the least
NB. overall work.

   (M 2 3) dot (M 3 2) dot (M 2 2)              NB. should be inefficient
+-------------------------------------+-------------------------------------+
|(0*(0*0+1*2)+1*(2*0+3*2)+2*(4*0+5*2))|(0*(0*1+1*3)+1*(2*1+3*3)+2*(4*1+5*3))|
+-------------------------------------+-------------------------------------+
|(3*(0*0+1*2)+4*(2*0+3*2)+5*(4*0+5*2))|(3*(0*1+1*3)+4*(2*1+3*3)+5*(4*1+5*3))|
+-------------------------------------+-------------------------------------+

   ((M 2 3) dot (M 3 2)) dot (M 2 2)            NB. should be efficient
+---------------------------------+---------------------------------+
|((0*0+1*2+2*4)*0+(0*1+1*3+2*5)*2)|((0*0+1*2+2*4)*1+(0*1+1*3+2*5)*3)|
+---------------------------------+---------------------------------+
|((3*0+4*2+5*4)*0+(3*1+4*3+5*5)*2)|((3*0+4*2+5*4)*1+(3*1+4*3+5*5)*3)|
+---------------------------------+---------------------------------+

NB. ok, we've got a starting place.

NB. given a boxed list of n arrays, return a 2 by n boxed array, where
NB. the first column indicates 'desired order of operation'.  The
NB. array with the lowest number should be multiplied by it's
NB. immediate neighbor to the left (that is, the row above) before any
NB. other multiplications.

rate=.   ;~"0   /: @\: @:(#&>)

Sample data:
   list =. 2  <@M@,/\  1+6?20           NB. random list of arrays
   $&>  list                            NB. shapes of arrays
18  5
 5 10
10  8
 8 13
13 15
   > {."1 rate list                     NB. desired order of reduction
0 4 2 3 1

NB. ignore the first entry -- it's just a place holder
NB.
NB. We need a routine to split our list of matrices into two pieces,
NB. with the split occurring at the location indicated by the highest
NB. rating in the list.  More specifically, we need a routine which,
NB. given a list of ratings (e.g. 0 4 2 3 1) will give the number of
NB. matrices to keep on the left half of the split (in this case, 1
NB. matrix).

choose=.  (i. >./@}.)  @> @:({."1)

NB. test it
     choose rate list                   NB. should split on smallest dimension
1

NB. there are four cases we'll have to deal with in this 'optimal
NB. reduction', and they correspond to lists of length 0, 1, 2, and
NB. more than 2.

case0    =.   (i. 0 0)"]        NB. return an empty matrix
case1    =.   > @((<0 1)&{)     NB. return the matrix
case2    =.   dot&>&(1&{)/      NB. multiply the matrices

switch   =. 3&<.@#              NB. classify length of array

opt_red  =. case0`case1`case2`(choose ($:@{. dot $:@}.) ]) @.switch

NB. for test purposes, lets make a definition of 'dot' which shows the
NB. operations that would have happened

dot=.  times & paren &.<

NB. test this fake dot product (just shows shapes and order of operation)
   (": 2 3) dot (": 3 4)
(2 3)*(3 4)

NB. For test purposes, we'll also need to use a fake list which holds
NB. formatted shape information in place of the arrays:

   ] fake_list =. (0 _1 }. rate list) ,.  ": @$&.> list
+-+-----+
|0|18 5 |
+-+-----+
|4|5 10 |
+-+-----+
|2|10 8 |
+-+-----+
|3|8 13 |
+-+-----+
|1|13 15|
+-+-----+

NB. ok, acid test time:
   opt_red  fake_list
(18 5)*(((5 10)*(10 8))*((8 13)*(13 15)))

NB. Looks good to me.

NB. let's restore our earlier 'test' version of dot:

dot      =. paren @(plus/ . times)

NB. and repeat our earlier example, but see if the computer can pick
NB. the right option.

   opt_red rate (M 2 3) ; (M 3 2) ; < (M 2 2)
+---------------------------------+---------------------------------+
|((0*0+1*2+2*4)*0+(0*1+1*3+2*5)*2)|((0*0+1*2+2*4)*1+(0*1+1*3+2*5)*3)|
+---------------------------------+---------------------------------+
|((3*0+4*2+5*4)*0+(3*1+4*3+5*5)*2)|((3*0+4*2+5*4)*1+(3*1+4*3+5*5)*3)|
+---------------------------------+---------------------------------+

NB. looks good.

NB. Finally, let's check this thing  for correctness

dot=. +/ .*
reduce=. opt_red @rate

   res1=. reduce         i.@$&.> list
   res2=. >   +/ .* &.>/ i.@$&.> list
   res1 -: res2
1
   $ res1
18 15
   7 7 { res1
7.41958e10 7.49855e10 7.57752e10  7.6565e10 7.73547e10 7.81444e10 7.89341e10
 2.0833e11 2.10547e11 2.12765e11 2.14982e11   2.172e11 2.19417e11 2.21634e11
3.42464e11 3.46109e11 3.49754e11 3.53399e11 3.57045e11  3.6069e11 3.64335e11
4.76599e11 4.81671e11 4.86744e11 4.91817e11 4.96889e11 5.01962e11 5.07035e11
6.10733e11 6.17233e11 6.23734e11 6.30234e11 6.36734e11 6.43235e11 6.49735e11
7.44867e11 7.52795e11 7.60723e11 7.68651e11 7.76579e11 7.84507e11 7.92435e11
8.79001e11 8.88357e11 8.97713e11 9.07069e11 9.16424e11  9.2578e11 9.35136e11

NB. presumably somebody gets some benefit from this

NB. that's all, folks
