Newsgroups: comp.lang.apl
Path: watmath!watserv1!torn.onet.on.ca!utgpu!cs.utexas.edu!sun-barr!ames!haven.umd.edu!socrates!socrates!rockwell
From: rockwell@socrates.umd.edu (Raul Deluth Miller-Rockwell)
Subject: Re: Probability Convolutions
In-Reply-To: vpcsc4@sfsuvax1.sfsu.edu's message of Thu, 28 May 1992 00:14:43 GMT
Message-ID: <ROCKWELL.92May28011810@socrates.umd.edu>
Sender: rockwell@socrates.umd.edu (Raul Deluth Miller-Rockwell)
Organization: Traveller
References: <1992May28.001443.1786@csus.edu>
Date: Thu, 28 May 1992 06:18:10 GMT
Lines: 102

Emmett McLean:
   I am writing a program to calculate the nth convolution for a
   p.d.f.

whatever that is...

   My question actually addresses looping (simple enough) and how to
   put all this stuff in one function.

   If I define f1, f2, X, and P as :
     f1 =. ' +//. */~x.':11
     f2 =. ' +//. +/~x.':11
     Y =. 1 5 7
     P =. 0.1 0.2 0.7

   Then each time the following three statements are typed I get a
   convolution for the p.d.f
     index =. /: Y =. f2 Y
     Y =. index { Y
     P =. index { f1 P

   This function should execute these three comands n times.
   Any suggestions on how I should write it?

Well, there's a couple ways that I can think of.  The first would be
to use an explicit definition:
t =. 0 0 $''    [.    l =. '0# t=:t,y.':''

l 'f1 =.   '' +//. */~x. '':11'
l 'f2 =.   '' +//. +/~x. '':11'
l 'Y  =.   1 5 7'
l 'P  =.   0.1 0.2 0.7'
l '$. =. ,3 # ,: $.'
l 'index =. /: Y =. Y =. f2 Y'
l 'Y =. index { Y'
l 'P =. index { f1 P'
l 'Y ,: P'
doit =. t : ''
doit ''

But this involves building up that explicit definition, which is kind
of akward to work with.

A second way of doing this would be to decide that Y and P will be
represented by the same piece of data.  For example, 'Y ,: P' which I
used in the explicit definition to be able to return both results.  If
I do this, I can define functions which operate on this data structure
to give Y and P:

 f1 =. ' +//. */~y.':11
 f2 =. ' +//. +/~y.':11
 Y  =. ' 0 { y.':11
 P  =. ' 1 { y.':11
 Y2 =. ' f2 Y y.':11
 index =. '/: Y2 y.':11
 Y3 =. ' (index y.) { Y2 y.':11
 P3 =. ' (index y.) { f1 P y.':11
 next =. Y3,:P3

Then you can do
   next ^: 3   (1 5 7 ,: 0.1 0.2 0.7)

And, yes, this involves some redundant calculations.  In principle,
this function (next) can be rearranged automatically to minimize
redundancy.  However, since no one has implemented that yet, we can do
it by hand.  To see how, let's expand 'next' out a few levels:
 next
 Y3,:P3
 (index { Y2) ,: (index { f1@P)
 (index { f2@Y) ,: (index { f1@P)
 (/:@f2@Y { f2@Y) ,: (/:@f2@Y { f1@P)

Notice that f2@Y is calculated three times, and that the only other
piece of data that is used is f1@P.  So this can be rephrased as a
hook:

   f2@Y ((/:@[ { [) ,: (/:@[ { ]) f1@P

It might be nice to get rid of the dual grade-ups, but it may be more
trouble than it's worth.  Let's see... consider this as three
transformations, first extract Y and P and apply f1 and f2
respectively.  Second, duplicate that information and append the
gradeup of the f2 result.  Third, build the Y and P structure [for the
next pass, or for final consumption:

 pass1 =. f2 @(0&{)   ,:  f1 @(1&{)
 pass2 =. ] , /:@(0&{)
 pass3 =. (2&{  {  0&{) ,:  (2&{  {  1&{)
 next =.  pass3 @ pass2 @ pass1

then, do
      next^:(3) 1 5 7 ,: 0.1 0.2 0.7

Presumably, for large enough arguments, this will be more efficient
than the simpler form,
 next =. f2@(0&{) ((/:@[ { [) ,: (/:@[ { ]) f1@(1&{)

Whether it's worth the trouble is, of course, a matter of opinion.

-- 
Raul Deluth Miller-Rockwell                   <rockwell@socrates.umd.edu>
The U.S. government went another thousand million dollars into debt today.
