simpsong@ncoast.UUCP (Gregory R. Simpson @ The North Coast) (11/14/85)
*** Line eater poison *** Below is a kludge version of a matrix multiplier in C. The primary reason for posting this is in hope that someone out there knows the proper method of doing it. The problem I encountered was that in order to pass a two dimensional array (hence a matrix) I needed to specify the second bound of the array. However, I want this to be a generic matrix multipler, for matrices of all sizes not just a fixed size. Since the column increases most rapidly in a two dimensional array, declaring a large array (like mat[][20]) puts all of a 3x3 into the first row. In the much dreaded language of Fortran, one may simply pass a variable to the dimension statement... this doesn't work in C. I believe this is a job for pointers, but I was unsuccessful in getting them to work. Please give me pointers on pointers (pun intended) for this application. (or if you are ambitious send me a better version of code) My fixes were fairly, crude, and the result is returned in a mxn vector as opposed to an mxn matrix. Please send any pointers, references, code help, flames to the address below. I have included my version below. p.s. Once this issue is resolved, I will post an enhanced matrix manipulation set, (mult., inversion, determinant, co-factors, etc.) Thanks in advance, Gregory R. Simpson UUCP: decvax!cwruecmp!ncoast!simpsong CSNET: simpsong@case.CSNET ARPA: simpsong@purdue-ecn.ARPA p.s. I am also looking for the manual page to roff.c posted from site gloria about 2 months back... ======= bend - mutilate - cut - line-eat - here - ok - ================ /* a sample main program to call matmult */ main() { float a[3][3],b[3][3]; float c[9]; int i,j; /* a typical array */ a[0][0]=1.0; a[0][1]=2.0; a[0][2]=3.0; a[1][0]=4.0; a[1][1]=5.0; a[1][2]=6.0; a[2][0]=7.0; a[2][1]=8.0; a[2][2]=9.0; /* another typical array */ for (i=0;i<=2;i++) { for (j=0;j<=2;j++) { b[i][j] = a[i][j]; } } /* multiply two typical arrays together */ matmlt(a,b,c,3,3,3); /* print the result, (it is in a 3 times 3 vector... boo hiss) */ for (i=0;i<=8;i++) { printf("%f\n",c[i]); } } /* the matrix multiplier */ matmlt(a,b,c,n,m,l) /* this is a lot easier in fortran, cause then I can say dimension a(m,n) */ int n, m, l; float a[], b[]; float c[]; { float a2[10][10], b2[10][10], c2[10][10]; int i,j,k,inc; /* load the arrays I passed into an array - kludgy ehhh? */ inc = 0; for (i=0;i<=2;i++) { for (j=0;j<=2;j++) { a2[i][j] = a[inc]; b2[i][j] = b[inc]; inc++; } } for (i = 0; i <= n-1 ; i++) { for (k = 0; k <= l-1 ; k++) { c2[i][k] = 0; for (j = 0; j <= m-1 ; j++) { c2[i][k] = (a2[i][j])*(b2[j][k]) + c2[i][k]; } } } /* load the array result back into a vector to pass back */ inc = 0; for (i=0;i<=2;i++) { for (j=0;j<=2;j++) { c[inc] = c2[i][j]; printf("%f %f %f \n",a2[i][j],b2[i][j],c2[i][j]); inc++; } } }
rfb@h.cs.cmu.edu (Rick Busdiecker) (11/17/85)
Subject: Re: matrix mult. (source and question) Message-Id: <501087498/rfb@H.CS.CMU.EDU> In-Reply-To: Gregory R. Simpson @ The North Coast's netnews message of Thu, 14-Nov-85 00:18:16 EST If Gregory Simpson's data representation were slightly different, things would go much more nicely, maybe even more nicely than in fortran 8-} Declaring a two-dimensional array is not equivalent to declaring an array of one-dimensional arrays, i.e. double matrix [m][n] is not the same as double (matrix [m]) [n]; however a reference to element (m,n) may still be coded as matrix[m][n] in a routine regardless of which declaration is used. With the second declaration element (m,n) could also be referenced as *(*(matrix + m) + n). /*=========================================================================== * matrix_multiply * * Multiplies the matrix a (dimension m,n) by the matrix b (dimension n,p) * into the matrix c (dimension m,p). Matrices must be declared explicitly * to be arrays of arrays rather than 2d arrays. */ void matrix_multiply (a, b, c, m, n, p) double **a, **b, **c; int m, n, p; { int i, j, k; for (i = 0; i < m; i++) for (j = 0; j < p; j++) { c [i][j] = 0.0; for (k = 1; k < n; k++) c += a [i][k] * b [k][j]; } }
ken@turtlevax.UUCP (Ken Turkowski) (11/18/85)
In article <890@ncoast.UUCP> simpsong@ncoast.UUCP (Gregory R. Simpson @ The North Coast) writes: >Below is a kludge version of a matrix multiplier in C. The primary >reason for posting this is in hope that someone out there knows the >proper method of doing it. Anyone who's diddled with two dimensional arrays, and has tried to think about how a compiler would do it has come up with something like the following: ----------------------------------------------------------------- # This is a shell archive. Remove anything before this line, then # unpack it by saving it in a file and typing "sh file". (Files # unpacked will be owned by you and have default permissions.) # # This archive contains: # matcat.c echo x - matcat.c cat > "matcat.c" << '//E*O*F matcat.c//' /* Matcat multiplies two (n x n) matrices together. The source matrices * are given in A and B, and the result is returned in C. */ # ifndef ARRAYS matcat(C, A, B, n) register double *C; double *A, *B; register int n; { register double *ap, *bp; register int k, j, i; for (i = n; i-- > 0; A += n) { /* Each row in A */ for (j = 0; j < n; j++) { /* Each column in B */ ap = A; /* Left of ith row of A */ bp = B + j; /* Top of jth column of B */ *C = 0.0; for (k = n; --k >= 0; bp += n) *C += *ap++ * (*bp); /* *C += A[i'][k'] * B[k'][j]; */ } } } # else matcat(C, A, B, n) register double *A, *B, *C; register int n; { register int i, j, k; double sum; for (i = 0; i < n; i++) { for (j = 0; j < n; j++) { sum = 0.0; for (k = 0; k < n; k++) sum += A[n*i+k] * B[n*k+j]; C[n*i+j] = sum; } } } # endif //E*O*F matcat.c// exit 0 -- Ken Turkowski @ CIMLINC (formerly CADLINC), Menlo Park, CA UUCP: {amd,decwrl,hplabs,seismo,spar}!turtlevax!ken ARPA: turtlevax!ken@DECWRL.DEC.COM
hester@ICSE.UCI.EDU (Jim Hester) (11/20/85)
Your method is metter in terms of space, and can usually be made better in terms of time. There are some minor improvements, and some pro's and cons to consider. The basic change your code made, relative to the original procedure, was to have the programmer explicitly deal with his own linear array representation. This is a bit more efficient space-wise (than the pointer-vector representation), makes creating a new array much easier, and can probably be made more efficient time-wise than a compiler since, although a compiler might do static array lookups a bit faster, your code could be modified to pre-calculate some terms outside of loops for moderate speedups, which only an extremely intelligent optimizing compiler (if any) could do. The major disadvantage of this scheme is that the programmer cannot use the facilities already provioded by the language for manipulating multi-dimensional arrays, which leads to slight conceptual confusion requiring a fair amount of documentation explaining to potential modifiers your method and why they must uniformly conform to it. The programmer must also implement and consistantly use procedures/macros for array access, changing all lines in all procedures which make use of the arrays from (the system's notation): i = A[ x ][ y ]; A[ x ][ y ] = i; to: i = lookup( A, n, x, y ); assign( A, n, x, y, i ); or: i = A[ loc(n, x, y) ]; A[ loc(n, x, y) ] = i; (I generally use the second method, since it only requires one function, is conceptually closer to what the compiler does, is visually closer to the language's notation, is simpler, and much more easily allows the use of macros.) The original code was intended to be as general as possible, so the program using explicit linear arrays and my examples above should be modified to support n by m arrays (trivial). If you want to limit the problem to square arrays and believe they could get larger than about 50 by 50, there is a reasonably non-complex, asymptotically faster algorithm. Strassen's multiplication algorithm (which I'll spare the readers: see any decent algorithms text) takes Order( n^2.81 ) time as opposed to Gaussian algorithms (all those presented recently), which take Order( n^3 ) time.
levy@ttrdc.UUCP (Daniel R. Levy) (11/21/85)
I have some comments/questions regarding the ongoing discussion of matrix multiplication and articles which have been posted thereon. Those in net.lang.f77 sit tight; f77 comes in near the end of the article. In article <267@h.cs.cmu.edu>, rfb@h.cs.cmu.edu (Rick Busdiecker) writes: > >char *valloc (); > > [Code omitted] Flames will come quickly if I am mistaken, but I can't seem to find any valloc() on our Sys5R2 3B20. In article <960@turtlevax.UUCP>, ken@turtlevax.UUCP (Ken Turkowski) writes: >In article <890@ncoast.UUCP> simpsong@ncoast.UUCP (Gregory R. Simpson @ The North Coast) writes: >>Below is a kludge version of a matrix multiplier in C. The primary >>reason for posting this is in hope that someone out there knows the >>proper method of doing it. >Anyone who's diddled with two dimensional arrays, and has tried to think >about how a compiler would do it has come up with something like the >following: [reduced from the original shar archive format] >/* Matcat multiplies two (n x n) matrices together. The source matrices > * are given in A and B, and the result is returned in C. > */ > ># ifndef ARRAYS > >matcat(C, A, B, n) >register double *C; >double *A, *B; >register int n; >{ > register double *ap, *bp; > register int k, j, i; > > for (i = n; i-- > 0; A += n) { /* Each row in A */ > for (j = 0; j < n; j++) { /* Each column in B */ > ap = A; /* Left of ith row of A */ > bp = B + j; /* Top of jth column of B */ > *C = 0.0; > for (k = n; --k >= 0; bp += n) > *C += *ap++ * (*bp); /* *C += A[i'][k'] * B[k'][j]; */ ^^^^^ What is this supposed to do with respect to the returned ARRAY? I don't ever see C getting bumped up to the next element. Is this supposed to be *(C++) += ..... ? I don't get it even then.... (I don asbestos suit just in case) > } > } >} > ># else > >matcat(C, A, B, n) >register double *A, *B, *C; >register int n; >{ > register int i, j, k; > double sum; > > for (i = 0; i < n; i++) { > for (j = 0; j < n; j++) { > sum = 0.0; > for (k = 0; k < n; k++) > sum += A[n*i+k] * B[n*k+j]; > C[n*i+j] = sum; > } > } >} > ># endif This last seems more reasonable (at least it works!). In fact, it can be easily hacked into a form which can be called on by f77 programs [and which is probably more efficient that anything that could be written in f77 itself due to the register variables and the lousy optimization :-(]: /* call from f77 by: double precision a, b, c integer m data m /<whatever>/ C C Multiply m*m matrices a and b to arrive at matrix c. C call matcat(c,a,b,m) */ matcat_(C, A, B, m) register double *A, *B, *C; int *m; { register int n; register int i, j, k; double sum; n = *m; for (i = 0; i < n; i++) { for (j = 0; j < n; j++) { sum = 0.0; for (k = 0; k < n; k++) sum += A[n*i+k] * B[n*k+j]; C[n*i+j] = sum; } } } There are still some problems with this the way it is stated; Fortran expects arrays to be in row-major order and C puts them in column-major order (did I get it right, Guy? You wrote an article about this some time ago). Also this does not deal with non-square matrix multiplication. It sounds like an easy enough exercise to come up with such a f77-compatible matrix multiplication routine, but does anyone out there have source handy already?