PDA

View Full Version : New Comp Idea


Dru Lee Parsec
08-15-2002, 02:34 PM
OK, we havn't done one of these in a while. So here's a new programming challenge.

1. Build a matrix class that can hold an N by M matrix of integers

2. Fill 2 of those matrix objects with numbers.

3. Mutliply the 2 matricies together (using Matrix multiplication of course (dot products etc)). If the size of the 2 matricies won't allow a multiplication then output an appropriate error message. i.e. a 2 x 3 matrix multiplied by a 3 x 5 matrix will result in a 2 x 5 matrix. But multiplying them in the reverse order ( 3 x 5 times 2 x 3) is illegal.

4. Display the 2 input matricies and the result matrix as your output.


Now, if you REALLY want some extra credit. Do this program in a language that you DON"T know. Since I'm a Java guy I'm going to see if I can find the time to figure this out in Python.


If you're new to matrix multiplication then here's a good site to learn that technique. http://www.mai.liu.se/~halun/matrix/matrix.html

Good luck.

P.S. In APL this would be a 4 line program. Maybe even less.

stuka
08-15-2002, 03:37 PM
heh - I had to do this in my assembly class (well, not objects, of course, but matrix multiplication). That sucked! That being said, maybe I could learn O'Caml for this....

Strike
09-06-2002, 08:03 AM
Bwahaha, you thought the competition was being squandered. Rest assured that I will try to have a goo (see: http://www.googoogaga.org for more details) version out soon. I already have the matrix class, I just gotta figure out an easy way of doing dot products in goo and I am golden.

Strike
09-06-2002, 10:20 AM
Okay, this code isn't the cleanest, and to those of you who don't know Goo, you will scratch your heads anyway. BUT, in spite of that, I am pasting the 84 lines of methods ANYWAY, so feast your eyes.

coderforums.goo

; Our master class
(dc <matrix> (<any>))

; Its only real member
(dp rows (<matrix> => <vec>))

; Define a "fab" that will make a matrix
(dm matrix (nrows|<int> ncols|<int> => <matrix>)
(def M (new <matrix>))
(set (rows M) (fab <vec> nrows))
(rep loop ((i 0))
(when (< i nrows)
(set (elt (rows M) i) (fab <vec> ncols))
(loop (+ i 1))))
M)

; To be used for the multiplication method
(dm numrows (M|<matrix> => <int>)
(len (rows M)))

; This method naively assumes that all rows in the
; matrix are the same length, but as long as it was
; created with the above matrix method, it should
; be the case that they are all the same length.
(dm numcols (M|<matrix> => <int>)
(len (1st (rows M))))

; Get item at (row, col), handy in general, and handy
; in some of the other methods.
(dm get-item (M|<matrix> row|<int> col|<int> => <int>)
(elt (elt (rows M) row) col))

; Just to make things a little cleaner looking:
(dm get-row (M|<matrix> index|<int> => <vec>)
(elt (rows M) index))

; Really handy for the multiplication method
(dm get-col (M|<matrix> index|<int> => <vec>)
(def retcol (fab <vec> (numrows M)))
(rep loop ((i 0))
(when (< i (numrows M))
(set (elt retcol i) (get-item M i index))
(loop (+ i 1))))
retcol)

; I needed a zip function to do some nice pairing
; between two vectors, so I made my own. This is used
; in the dot-product function.
(dm my-zip (v1|<vec> v2|<vec> => <vec>)
(when (= (len v1) (len v2))
(def retvec (fab <vec> (len v1)))
(rep loop ((i 0))
(when (< i (len v1))
; This could probably be done more cleanly, but hey it works
(set (elt retvec i) (rev (add (fabs <vec> (elt v1 i)) (elt v2 i))))
(loop (+ i 1))))
retvec))

; Lame as this is, I'm using it because it makes things
; easy and because I'm too lazy to figure out the more
; goo-ish way of doing this.
(dm multpair (foo|<vec> => <int>)
(* (elt foo 0) (elt foo 1)))

; Really cool (to me anyway) that this works.
(dm dot-product (v1|<vec> v2|<vec> => <int>)
(fold+ + (map multpair (my-zip v1 v2))))

; The method we've all been waiting for!
(dm mult (M1|<matrix> M2|<matrix> => <matrix>)
(when (= (numcols M1) (numrows M2))
(def M (matrix (numrows M1) (numcols M2)))
(rep row-loop ((row 0))
(when (< row (numrows M))
(rep col-loop ((col 0))
; We actually iterate over the rows and columns of the
; result matrix and set those by taking the dot product
; of the
(when (< col (numcols M))
(set (elt (elt (rows M) row) col)
(dot-product (get-row M1 row) (get-col M2 col)))
(col-loop (+ col 1))))
(row-loop (+ row 1))))
M))

(edit: oops, didn't finish that last comment, add on "row from the left matrix and column of the right matrix", it's empty in the attachment too)

Now, from someone whose primary languages are Python and C/C++ ... that's a bit of a deviation. So use this as inspiration to try even those "weird" languages :)

Anyway, what would the code be without testing? Here's a bit of sample code from the bottom of that file:

; Now for some example code:
;
; Test case 1 - 2x2 identity squared
;
; /1 0\ /1 0\ - /1 0\
; \0 1/ \0 1/ - \0 1/
;
; Test case 2 - 2x3 times 3x2
;
; /1 2 3\ /1 2\ /22 28\
; \4 5 6/ |3 4| = \49 64/
; \5 6/
;

; Test case 1
(dv mat1a (matrix 2 2))
(dv mat1b (matrix 2 2))
(set (rows mat1a) (fabs <vec> (fabs <vec> 1 0) (fabs <vec> 0 1)))
(set (rows mat1b) (fabs <vec> (fabs <vec> 1 0) (fabs <vec> 0 1)))
(mult mat1a mat1b)

; Test case 2
(dv mat2a (matrix 2 3))
(dv mat2b (matrix 3 2))
(set (rows mat2a)
(fabs <vec> (fabs <vec> 1 2 3) (fabs <vec> 4 5 6)))
(set (rows mat2b)
(fabs <vec> (fabs <vec> 1 2) (fabs <vec> 3 4) (fabs <vec> 5 6)))
(mult mat2a mat2b)

Complete with ASCII art!
Here's the pertinent output from running goo < coderforums.goo (attached below as coderforums.txt):

goo/user 0<= goo/user 0=> #[#[1 0] #[0 1]]
goo/user 0<= goo/user 0=> #[#[1 0] #[0 1]]
goo/user 0<= goo/user 0=> #{<matrix> rows: #[#[1 0] #[0 1]]}
goo/user 0<= goo/user 0=> #[#[1 2 3] #[4 5 6]]
goo/user 0<= goo/user 0=> #[#[1 2] #[3 4] #[5 6]]
goo/user 0<= goo/user 0=> #{<matrix> rows: #[#[22 28] #[49 64]]}

The first two lines of each triple are just restating the matrix that has just been set. The third line in each triple is the result of the mult command.

Well, before I make the "longest single post" record totally unbreakable by adding more to this post, I'm going to stop here and attach the code.

(edit: I lied, I forgot I need to give vomjom mad props for his help in getting me over some of the initial bumps and for basically writing that "matrix" method at the top for me, and jemfinch for reminding me how handy the zip function is for getting cross products)

kmj
09-06-2002, 10:24 AM
P.S. In APL this would be a 4 line program. Maybe even less.


well, because it'd be kind of like just using '*' in C. :) APL is, after all, the Array Programming Language. Incidentally, have you ever heard of J? It's a child of APL. One of my professors loved it; I thought it was cool when we learned it in our survey class, but I never got into it deeply. Very interesting (and unreadable) language.

If I had more time, I'd do this in O'Caml, but I'm not optimistic that that's going to happen.

PrBacterio
09-06-2002, 03:07 PM
This is in X86 assembly. I think this counts as a "new language" because I haven't done anything in assembly language which uses the FPU before:
segment .code

global _matrmulx86

;;; void _stdcall matrmulx86(double *a, double *b, double *c, int arows, int acols, int bcols)
_matrmulx86
;; set up stack frame
;; (ie access to parameters)
push ebp
mov ebp,esp

;; local variables
sub esp,16

;; ...
;finit

;; save some registers
push edi

mov edi,[ebp+16] ; pointer to destination matrix

mov eax,[ebp+28] ; # of columns in matrix #2
mov [ebp-12],eax
shl eax,3
mov [ebp-8],eax ; row distance (matrix #2)

@0005
mov edx,[ebp+12] ; pointer to matrix #2
mov [ebp-4],edx ; store it for use while inside a row

@0004
mov ebx,[ebp+8] ; pointer to matrix #1
mov edx,[ebp-4] ; pointer to matrix #2

fld qword[ebx] ; calculate A(row)(0) * B(0)(col)
fmul qword[edx]

mov ecx,[ebp+24] ; # of columns in matrix 1/rows in #2

dec ecx
jz @0001

@0002
add ebx,8 ; next entry in current row
add edx,[ebp-8] ; next row
fld qword[ebx] ; multiply and ...
fmul qword[edx]

faddp st1 ; ... add

dec ecx
jnz @0002

@0001
fstp qword[edi] ; done
add edi,8
add dword[ebp-4],8 ; next column

dec dword[ebp-12] ; next value in current row
jnz @0004

mov eax,[ebp+28] ; # of columns in matrix #2
mov [ebp-12],eax

add ebx,8
mov [ebp+8],ebx ; next row
dec dword[ebp+20] ; rows left?
jnz @0005

@0000
;; ...
;finit

;; clean up stack & return
pop edi
mov esp,ebp
pop ebp
ret

Of course, there's no such thing as a 'class' in assembly. I simply use a two-dimensional row-major array.

jemfinch
09-11-2002, 03:23 AM
I'm still finding my language to do this in. The current contenders are, in descending order of likelihood:


Haskell: Type Classes give me the benefits of OO, and I can expand my horizons with monads and lazy evaluation.
Dylan: Like an optimized Python with a more functional bent.
Forth: It's weird, and low-level...


I'll post my code when I learn enough of the language I choose to write it. In the meantime, can someone provide a reference for how to multiply matrixes? I don't quite remember from Physics...

Jeremy

PrBacterio
09-11-2002, 04:19 AM
Originally posted by jemfinch
In the meantime, can someone provide a reference for how to multiply matrixes?

Pseudocode:
FOR i = 1 TO A.height DO
FOR j = 1 TO B.width DO
S := 0
FOR k = 1 TO A.width WHICH IS = B.height DO
S := S + A[i,k] * B[k,j]
END FOR(k)
R[i,j] := S (* dot product of i'th row of A with j'th column of B *)
END FOR(j)
END FOR(i)Each entry R[i,j] in the product matrix is the dot product of the i'th row of the first matrix with the j'th column of the second matrix. As easy as that! :)