Hi Liam,
As I briefly indicated on #lisp and contrary to our earlier belief, there _is_ at least one case in which a user would like to supply the stride, vector size n and offset to the FFT functions. Namely, this is for the 2-dimensional (more generally of course, N-dimensional) Fourier transform.
GSL only provides one-dimensional FFTs. Naturally, an N-dimensional FFT can be easily implemented from a one-dimensional function. From saying this, you can probably already immediately see what I mean, but nevertheless, to be sure there's no mistake here, I'll give a more concrete example.
Let us approach the problem in C, and consider a 2D array of size a x b, where a is the number of rows and b is the number of columns. This is a linear, contiguous array in C (a "raw" C array). A 2D FFT can be performed by first FFT'ing one dimension and then the other.
Thus, we first FFT each row, and then each column. The order actually doesn't matter. In C, this would be handled by first performing a times the (forward) FFT, thus applying an FFT to each row, where the vector size n = b, the stride = 1 and each consecutive FFT call supplies the vector with an offset with a multiple of b.
Next up is the FFT of each column. In C we perform b times the (forward) FFT, thus applying an FFT to each column where the vector size n = a, the stride = b, and each consecutive FFT call supplies the vector with an offset equal to the column number.
For N-dimensional arrays, simply lather, rinse and repeat.
This shows the necessity of the optional (keyword?) argument that indicates the size of the vector to be FFT'd, the stride (which is already in there) and also the offset (which is missing at the moment). This also shows that the Fourier transform environment that is in fft-interface.lisp is very nice to have, when dealing with non-radix-2 array lengths. Each consecutive call when FFT-ing in one direction only needs to allocate the workspace and wavetable once and, more importantly, calculate the wavetable once, while the FFT is repeated on each row (or column).
I have to admit that I haven't looked at how multi-dimensional marrays or more correctly, marrays representing tensors of rank > 1, from the GSLL point of view. As I understand, the interface allows for a truly multi-rank approach (using maref with multiple indices), as with the normal CL arrays and aref.
In my opinion, a nice approach would be to make the whole FFT by default work on the entire marray, thus performing multi-dimensional FFTs when marrays have higher ranks. That way, we can hide the messing about with strides, sizes and offsets from the user. Just think, loading an image into a marray, and just calling forward-fourier-transform, and instantly (depending on the speed of your computer...) have a Fourier-space image!
On the other hand, while such an interface would be nice for the casual user (and probably for most normal users) I don't know if there are "power users" that (would want to use GSL's FFT functions, and) would require direct control over such parameters.
As I may have mentioned before on #lisp, I have used GSL's FFT some time ago, as well as FFTW briefly. FFTW has functions for higher-dimensional FFTs, and deducing from that interface, I would say that an FFT function that "does the right thing (TM)" depending on the rank of the marray would not be so bad.
Anyway, sorry for the long post. I hope my explanations/considerations/ideas are useful and I would not mind implementing some/most of this in an experimental interface, possibly with some help with respect to some of the fancy internals of GSLL.
Please let me know what you think (and any suggestions from users of GSLL and FTT, I'm looking at you, Mirko...) are greatly appreciated!
Thanks, Sumant
Sumant,
In writing GSLL, I've tried to adhere to the philosophy of providing a good Lisp interface to GSL as it exists in the latest release. When I've had ideas on better ways to do GSL, I've restrained myself from putting them in GSLL. So, there may well be ways they could have made a better, more versatile, etc. library, but I have not tried to implement them. I've chosen this policy in order to limit the amount of work required for GSLL, which has been substantial as it is.
Specifically with regard to FFTs, the 2+ dimensions case may be very useful, but I don't see that GSL has done it, so I will avoid it in GSLL. The stride is there in GSLL because it's in GSL; offset is not.
So where does that leave your good ideas? A lot of what you're talking about I think can be helped along by having a mechanism for array creation, slicing, concatenation, etc., and perhaps another chunk by a port of fftw that is compatible with GSLL arrays. As you have seen and we have discussed, there has been a lot of discussion on array utilities and they are a highly requested feature. I have given this a lot of thought recently and decided there needs to be a separate system to do this, in part because I was uneasy in adding things to GSLL that weren't part of GSL and in part because I realize the extent of what needs to be done really justifies a separate system (or more likely, systems). I have not had a chance to create and test much code, but I hope to soon.
I'll continue to work on the FFT port and let you work it over before I pull it into master. Then I'm going to try to pull together the beginnings of the array system; it will take a while to get that settled but I think it will be worth making this as versatile as possible.
Liam
On Tue, Nov 03, 2009 at 10:54:00PM -0400, Liam Healy wrote:
Sumant,
In writing GSLL, I've tried to adhere to the philosophy of providing a good Lisp interface to GSL as it exists in the latest release. When I've had ideas on better ways to do GSL, I've restrained myself from putting them in GSLL. So, there may well be ways they could have made a better, more versatile, etc. library, but I have not tried to implement them. I've chosen this policy in order to limit the amount of work required for GSLL, which has been substantial as it is.
I totally understand your point here, and agree in general.
Specifically with regard to FFTs, the 2+ dimensions case may be very useful, but I don't see that GSL has done it, so I will avoid it in GSLL. The stride is there in GSLL because it's in GSL; offset is not.
This is where I do not agree. GSL has not explicitly done 2D FFTs, that is true. However, it provides a mechanism to implement this yourself. When you say offset is not in GSLL because it is not in GSL, I don't think you appreciate the ease with which an offset is introduced to a C array; you just add a number to it. Thus, an array a of size n can be passed with an offset by saying a+offset, where the latter array has a size n-offset. Therefore, GSL doesn't have to explicitly provide the offset as a parameter.
True, stride is mostly in there to only apply an FFT to, say, one column of an array. The size n is there to be able to do an FFT on just part of your array. Generally, the latter makes no sense without the ability to also have offsets.
I think it's fine to not have the 2D FFTs as a direct functionality in GSLL. I just wouldn't like to see GSLL lose flexibility with respect to GSL, and as it is now, in GSL someone can easily write an N-dimensional FFT in GSL by just looping over his array, while in GSLL, aside from looping, lots of slicing, copying (and probably consing in the process) would have to be done. I've looked at some old code I used, and see that I indeed did perform a 2D transform in GSL (before I started using FFTW because it was faster on huge arrays).
So where does that leave your good ideas? A lot of what you're talking about I think can be helped along by having a mechanism for array creation, slicing, concatenation, etc., and perhaps another chunk by a port of fftw that is compatible with GSLL arrays. As you have seen and we have discussed, there has been a lot of discussion on array utilities and they are a highly requested feature. I have given this a lot of thought recently and decided there needs to be a separate system to do this, in part because I was uneasy in adding things to GSLL that weren't part of GSL and in part because I realize the extent of what needs to be done really justifies a separate system (or more likely, systems). I have not had a chance to create and test much code, but I hope to soon.
This sounds good. However, note that in GSL, you just need N loops for an N-dimensional FFT, no "array utilities" required.
I'll continue to work on the FFT port and let you work it over before I pull it into master. Then I'm going to try to pull together the beginnings of the array system; it will take a while to get that settled but I think it will be worth making this as versatile as possible.
Also sounds good. I'm still available to implement some of the FFT work. You won't have to be afraid that I put in all sorts of crazy stuff before discussing it with you :)
Regards, Sumant
On Wed, Nov 4, 2009 at 4:40 AM, Sumant Oemrawsingh soemraws@xs4all.nl wrote: ...
This is where I do not agree. GSL has not explicitly done 2D FFTs, that is true. However, it provides a mechanism to implement this yourself. When you say offset is not in GSLL because it is not in GSL, I don't think you appreciate the ease with which an offset is introduced to a C array; you just add a number to it. Thus, an array a of size n can be passed with an offset by saying a+offset, where the latter array has a size n-offset. Therefore, GSL doesn't have to explicitly provide the offset as a parameter.
Well, it turns out offset *is* in GSLL, much to my surprise! However, it looks at first blush like it's wrongly implemented, but works for offset = 0 which is all I've ever tested. There is a function #'offset which reads a value set in the definition; see the defclass for foreign-array in data/foreign-array.lisp. It looks like this value is computed in find-original-array in data/foreign-friendly.lisp by making use of displaced arrays. Much of this code comes from Tamas Papp's foreign-friendly arrays; but in adapting it to GSLL, I'm not sure I got the tracking of the foreign array with the displaced CL array right.
The second problem I see is that while #'c-pointer correctly takes into account the offset for #+(and native sbcl) in foreign-friendly.lisp, it looks like it's just reading out the slot value in #'c-pointer for #-native in the defclass foreign-array. I don't see the offset being used there, so this might give the wrong answer.
If you want to dig into this code to see if it works or confirm there might be a problem, then we can fix this up. That should give you the ability to offset a (m)array, and so do the 2D FFT with reasonable ease as in the C case.
Liam