Page 1 of 1

Diagonalize a matrix

Posted: Wed Aug 26, 2009 6:46 pm
by piero.ugliengo
I am completely new to Revolution. I have downloaded the revmedia alpha version and played a bit with it. I was impressed by how fast I was able to port a little VB6 code to RevMedia. This code diagonalize a symmetric matrix using the Jacobi algorithm. I checked against the VB6 code and I got exactly the same results in the same number of iterations so that the numerics is the same. However the RevMedia code is at least one order of magnitude slower than the VB6. For instance a 50x50 matrix is diagonalized in a couple of seconds in VB6 and it tooks more than a minute on RevMedia. I know that RevMedia is not meant for numerical intensive calculations; however, I would like to use it in a scientific teaching context so some power is needed. I wonder if anybody much expert than me can try the code using the most powerful Revolution Studio.
The link to download my script is here. One can set the size of the matrix in the onmouse routine.
Thanks a lot

http://sites.google.com/site/pierouglie ... edirects=0

Posted: Wed Aug 26, 2009 6:59 pm
by malte
Piero, welcome!

Be sure there are quite a few of us who love a scripting challenge. So if you post your code, we can try to speed it up.

All the best,

Malte

Posted: Wed Aug 26, 2009 7:01 pm
by malte
Just saw, you attached a stack. Taking a peek. :)


Cheers,

Malte

Posted: Wed Aug 26, 2009 7:24 pm
by malte
Ok, just looked at your script. I think this challenge to speed it up is best passed on to the use list too:

http://lists.runrev.com/mailman/listinfo/use-revolution

But for being completely new to rev, I just can say wow. Pretty clean code. :)


Now, I am shocked...

I changed your while statement in the repeat loop. to just

repeat

before the corresponding

end repeat, I inserted:

if s9 > (N * N * tol2 ) then exit repeat

And got my test run from 21 seconds down to 2 seconds.... So evaluating the while condition in each iteration seems to be costy.

All the best,

Malte
Cheers,

Malte

Posted: Wed Aug 26, 2009 7:34 pm
by piero.ugliengo
malte wrote:Ok, just looked at your script. I think this challenge to speed it up is best passed on to the use list too:

http://lists.runrev.com/mailman/listinfo/use-revolution

But for being completely new to rev, I just can say wow. Pretty clean code. :)


Now, I am shocked...

I changed your while statement in the repeat loop. to just

repeat

before the corresponding

end repeat, I inserted:

if s9 > (N * N * tol2 ) then exit repeat

And got my test run from 21 seconds down to 2 seconds.... So evaluating the while condition in each iteration seems to be costy.

All the best,

Malte
Cheers,

Malte
Dear Malte thanks a lot. With your change is the matrix diagonalized?
It seems to me that the

repeat while s9 > (N * N * tol2 )

should become:

if s9 < (N * N * tol2 ) then exit repeat
rather than n
if s9 > (N * N * tol2 ) then exit repeat

in other words s9 should be less than in order to jump outside the loop

Posted: Wed Aug 26, 2009 7:46 pm
by SparkOut
I took a look and my brain nearly melted. I have no idea how to make the maths on this work. For a matrix with N of 50 it ran in 41 seconds on my machine, and I saved 10 seconds on subsequent trials by putting:
"lock messages" at the top of the Eigen handler, and "unlock messages" before answering the result.
If I had the faintest idea how to even conceptualise a Jacobi diagonalisation of a matrix, then I would love to help, but this is as much as I can offer I'm afraid.

I also tried with the "if... then exit repeat" and it didn't really affect the speed at all.

Posted: Wed Aug 26, 2009 7:46 pm
by malte
Of course you are right. *smacks forehead*

There goes my speed. At least most of it. Still a tad bit faster than while though it seems.

Best bring it up on use list. More people to chime in there.

All the best,

Malte

Posted: Wed Aug 26, 2009 7:48 pm
by malte
Sparkout, I feel the same. The math is way over my head. :D

Cheers,

Malte

Posted: Wed Aug 26, 2009 9:59 pm
by Mark
Hi Piero,

This looks interesting. I might have a look. In the mean time, although it is a long shot, I wonder whether you might win some time by using array[j] instead of array[i,j]. I suspect that the new array routines are slightly faster than the old ones.

Best,

Mark

Posted: Thu Aug 27, 2009 12:54 am
by BvG
I took a look at the code. Here some general ideas, not all are necessarily about speed, or your code. ;-)
  • Don't use functions
    Every function call will slow your code down, so use them as seldom as possible. So if speed is your main consideration, try to make it all within the mouseUp handler. If you make a function, try to make it a "private function" this has a small speed advantage (but needs a recent version of rev).
  • Don't pass variables
    Rev evaluates every variables content when they're passed. Again this will slow down a tiny bit for each passed variable. Consider using local variable declarations outside of handlers instead.
  • Don't ever use @ to simulate lower level languages pointer behaviour
    This is not necessarily slow in Rev (so i'm guessing), but it's contraire to the way rev works. Most speed advantages in rev come from localising and restricting the context, so if you forfeit that by using @, that advantage might be lost.
  • Only use an array when the position is not sequentially handled
    I don't know if this applies to your code, but when you're handling a list with items from topleft to bottom right (or simply from top to bottom), then you should not use an array. Instead use a continous text with itemdelimiter and "repeat for each line"
  • Do not show anything on the screen
    If you want speed, then do not show anything onscreen until you're done. Of course for debugging purpose the rules are different. But if you time your code, and have "answer" or "put into field" etc. then you will not time your code, but just the display update times, which suck horribly in rev.
  • Don't use "format", or other broad functions
    Most of the time using a build in function is faster then replicating the function in rev yourself. However, there are exceptions, and unfortunately most of them are traps for people coming from other languages. So don't use "/n". Instead use "put myValue & return after theResult" (similar traps are complex filter conditions and regular expressions, which often are easier solved using items and lists).

Posted: Thu Aug 27, 2009 1:33 am
by BvG
as for specific hints, and if you need to use an array, I'd say you should try to use "repeat for each key" instead of:

Code: Select all

repeat with k = 1 To N
 repeat with j = k To N
Another problem is, that you have the following (in variations) all over the code:

Code: Select all

put stringa & a[i,j] & "    "  into stringa
the problem here is that you tell rev to first get the content of your var, then find out where that content ends, and put stuff there. finally you put it all back into the same var, extending it's size and making a memory allocation. For rev this will be much faster (and for you easier to read):

Code: Select all

put a[i,j] & "    "  after stringa
I see some redundancy in your code, where you always take N (defined as 10 at the beginning) and use that to manipulate stuff that already has been constructed to be 1 to 10. in most cases in rev it's faster and easier to use the value directly, if you can assure that it is sequential (not an array):

Code: Select all

--assuming your data is delimited by tab, the default would be comma
private function Matrice_print theList
  set the itemdelimiter to tab
  repeat for each line theLine in theList
      repeat for each item theItem in theLine
      put theItem & space & space & space & space after theResult
    end repeat
    put return after theResult
  end repeat
  delete char -1 of theResult -- remove trailing return
  return theResult
  --note that normally you wouldn't even use any repeat here, instead the code would be:
  --  replace tab with "    " in theList
  --  return theList
end Matrice_print
now if you want a random but specific entry repeatedly, so in a not sequential fashion, only then I'd suggest an array. If you know that random access is only happening after a user action, or only a few times (less then 10 or so, depending on the size of the matrix) then you can count the items and lines:

Code: Select all

--gives you the equivalent of x=3 and y=10, therefore 10,3 in your code
put item 3 of line 10 of theList