-
Notifications
You must be signed in to change notification settings - Fork 526
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fourier: add quarterwave functions #465
Conversation
72503a7
to
da4de00
Compare
da4de00
to
d5211d1
Compare
OK, this is ready. PTAL |
"github.com/gonum/floats" sneaked into fftpack_test.go (already in the initial PR). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a quick look at what's in the PR.
fourier/internal/fftpack/cosq.go
Outdated
} | ||
} | ||
|
||
// Subroutine Cosqb computes the fast fourier transform of quarter |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Delete "Subroutine" here and also at other places in this PR, shown by golint
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done - and other doc fixes.
fourier/fourier_sincos.go
Outdated
ifac [15]int | ||
} | ||
|
||
// NewQuarterWave returns a QuarterWave initialized for work on sequences of length n. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NewQuarterWaveFFT
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
// the length of dst does not equal t.Len()/2+1, FFT will panic. | ||
func (t *FFT) FFT(dst []complex128, seq []float64) []complex128 { | ||
// the length of dst does not equal t.Len()/2+1, Coefficients will panic. | ||
func (t *FFT) Coefficients(dst []complex128, seq []float64) []complex128 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't make it in time for the initial PR, so I'll comment here. I think the type would be better named DFT
because it computes the Discrete Fourier Transform (the problem being solved) while the Fast Fourier Transform is the (family of) algorithm that computes it (implementation detail that solves the problem). It would be also more consistent with the transforms introduced in this PR. Although I do know that DFT and FFT are often used interchangeably.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately discoverability will likely trump correctness, though this is strictly still correct I suppose.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I'm with @kortschak , while DFT may be correct, everyone will be searching for FFT and clearly having that name would be useful. If there is a potential name clash I could be convinced otherwise, but it seems like bowing to commonality here .
We could also call this DFT, but somehow include FFT in the package comment to help searching .
ping |
fourier/fourier.go
Outdated
@@ -40,15 +40,15 @@ func (t *FFT) Reset(n int) { | |||
fftpack.Rffti(n, t.work, t.ifac[:]) | |||
} | |||
|
|||
// FFT computes the Fourier coefficients of the input sequence, seq, | |||
// Coefficients computes the Fourier coefficients of the input sequence, seq, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add in a comment that's something like "(transforming the time series into the frequency spectrum)". I know it basically says that, but having something that overly clearly states "this is FFT and not IFFT" will help avoid confusion with our somewhat abnormal naming.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
// the length of dst does not equal t.Len()/2+1, FFT will panic. | ||
func (t *FFT) FFT(dst []complex128, seq []float64) []complex128 { | ||
// the length of dst does not equal t.Len()/2+1, Coefficients will panic. | ||
func (t *FFT) Coefficients(dst []complex128, seq []float64) []complex128 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I'm with @kortschak , while DFT may be correct, everyone will be searching for FFT and clearly having that name would be useful. If there is a potential name clash I could be convinced otherwise, but it seems like bowing to commonality here .
We could also call this DFT, but somehow include FFT in the package comment to help searching .
fourier/fourier.go
Outdated
@@ -74,15 +74,15 @@ func (t *FFT) FFT(dst []complex128, seq []float64) []complex128 { | |||
return dst | |||
} | |||
|
|||
// IFFT computes the real perodic sequence from the Fourier coefficients, | |||
// Sequence computes the real perodic sequence from the Fourier coefficients, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
again, it would be nice to have something that makes it overly clear this is what people normally think of as ifft
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
fourier/fourier_sincos.go
Outdated
@@ -0,0 +1,238 @@ | |||
// Copyright ©2018 The Gonum Authors. All rights reserved. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not just call this file sincos.go
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's called this for historical reasons. I wanted it to be in the fourier.go file initially, but that was in the air, so I wrote it into a new file - same with the tests. I actually think the name it has now is more accurate and have grown to think the separation is good.
If it should be separated, I would have sincos.go and quarter.go. Does this sound OK?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That sounds fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
fourier/fourier_sincos.go
Outdated
return dst | ||
} | ||
|
||
// QuarterWaveFFT implements Fast Fourier Transform for quarter wave data. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be nice to provide a reference here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. The best I can find is this https://pdfs.semanticscholar.org/presentation/6e0d/2cbafb339cdf7ba90fdc36997d6b91dfffad.pdf.
fourier/fourier_sincos.go
Outdated
fftpack.Cosqi(n, t.work, t.ifac[:]) | ||
} | ||
|
||
// CosCoefficients computes the Fast Fourier Transform of quarter wave data for |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this say Discrete? DST for instance uses discrete while this uses fast.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They are sort of the same in this domain. The docs reflect the documentation in fftpack.
fourier/fourier_test.go
Outdated
|
||
want := make([]float64, n) | ||
for i := range want { | ||
want[i] = rand.Float64() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These should have their own RNG in the test. Also, should these maybe use NormFloat so there are both positive and negative numbers, as well as numbers > 1? Sorry, missed this in the last PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same for the other tests
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done for the source, but changing the norm floats causes failure that I don't have time to look into right now.
t.Run("Reset DCT", func(t *testing.T) { | ||
dct := NewDCT(1000) | ||
for n := 2; n <= 2000; n++ { | ||
dct.Reset(n) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add one test at the end where you reset to a smaller size?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Functionally this is what happens. In each case the dct is initially created with 1000, and then increased from the smallest value it can take.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point.
@@ -0,0 +1,220 @@ | |||
// Copyright ©2018 The Gonum Authors. All rights reserved. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file looks good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Next chunk done. Haven't found anything that would cause it to break with negative numbers sadly
t.Run("Reset DCT", func(t *testing.T) { | ||
dct := NewDCT(1000) | ||
for n := 2; n <= 2000; n++ { | ||
dct.Reset(n) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point.
@@ -0,0 +1,143 @@ | |||
// Copyright ©2018 The Gonum Authors. All rights reserved. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file looks good.
@@ -0,0 +1,179 @@ | |||
// Copyright ©2018 The Gonum Authors. All rights reserved. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file looks good.
fourier/internal/fftpack/sint.go
Outdated
default: | ||
x[0] = 0 | ||
for k := 0; k < n/2; k++ { | ||
kc := n - k |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe kc := n - k - 1
like you did in the other file so the index just works below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also in cost.go.
fourier/internal/fftpack/sint.go
Outdated
t1 := xh[k] - xh[kc-1] | ||
t2 := was[k] * (xh[k] + xh[kc-1]) | ||
x[k+1] = t1 + t2 | ||
x[kc+1-1] = t2 - t1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Either way with the above we don't need kc+1-1
:)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
@@ -0,0 +1,146 @@ | |||
// Copyright ©2018 The Gonum Authors. All rights reserved. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good minus the comments below.
The reporting of failures is pretty sparse, so it may just be a small delta on the expectation. I just have not had time to look into it. |
PTAL |
fourier/fourier_sincos.go
Outdated
@@ -0,0 +1,238 @@ | |||
// Copyright ©2018 The Gonum Authors. All rights reserved. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That sounds fine.
@@ -616,3 +616,827 @@ var cfftTests = []struct { | |||
wantiifac: []int{2, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, | |||
}, | |||
} | |||
|
|||
func TestSint(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please add comments to the sections of the tests to make it more clear what's going on? (This goes for the other added tests here)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it OK if I just say that these tests reiterate the tests that are part of FFTPACK?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It turns out I do have that, in the header for the public domain attribution.
What I'd write is essentially this, but it's more than we write for lapack and blas, which are arguably more complex in their tests because of the structure of those suites.
// Broadly, the tests each compare the prepared work and
// prime factor slices to golden values for a variety of
// lengths known to excercise all parts of the forward and
// backward transforms. Then for each of these lengths a
// synthetic sequence with known frequency spectrum is
// analysed with the forward transform and compared to the
// spectrum and the spectrum is synthesised and compared to
// the sequence. Finally, the the synthetic spectrum is
// synthesised to the sequence and back again, being compared
// to the original spectrum. In all cases the transforms
// are normalised by the appropriate constant specified in
// the documentation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. Added some comments on what I would add.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll add those, but it does seem odd to me to do it here when the tests that exist in testblas and to a lesser extent testlapack largely don't have any commentary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have particular functions in mind? I did a quick survey and most of the float64 blas tests are only one section, and of the blas/lapack functions that have multiple sections typically those sections have some amount of explanation text.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is always going to be a subjective assessment, but here is a list of tests in blas and lapack that look like they are commented to a similar level. There are others where there is commentary, but at least when I read it, I need to do a lot of work to understand what is being done - this is obviously an interaction between my level of linalg experience and the commentary. I've left out tests where there are tables or the tests are trivial.
blas: Zgbmv, Zhbmv, Ztbmv, Ztpsv, Ztrsv
lapack: Debgd2, Dgelqf, Dgeqp3, Dlags2, Dlange, Dlaln2, Dlanst, Dlansy, Dlanv2, Dlapll, Dlaqr23, Dlarfx, Dlartg, Dlasr, Dlatrd, Dorg2r, Dorgbr, Dorgtr, Dpotf2, Dtrtri2, Dtrtri.
It's worth noting that many of the tests explain what they do in the Errorf
format string, though not necessarily how they get there - I think the fftpack tests do both (intentionally).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the list. I'll go through and try to improve.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be nice to spread the testing comments out something along the lines of what I suggested. Otherwise this looks good.
@@ -616,3 +616,827 @@ var cfftTests = []struct { | |||
wantiifac: []int{2, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, | |||
}, | |||
} | |||
|
|||
func TestSint(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. Added some comments on what I would add.
work := make([]float64, 5*(test.n)/2) | ||
ifac := make([]int, 15) | ||
|
||
Sinti(test.n-1, work, ifac) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Compute the transform of golden sequence and compare with known transform.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Compute the work and factor slices and compare to known values.
continue | ||
} | ||
|
||
fn := float64(test.n) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Construct a sequence with known frequency spectrum and compare the computed spectrum
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
t.Errorf("unexpected sintt value for n=%d: got:%f want:0", test.n, sintt) | ||
} | ||
|
||
Sint(test.n-1, x, work, ifac) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Check that the transform is its own inverse up to a constant
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
PTAL |
Thanks for the review |
Thanks for all the translation work! Another big chunk of functionality no longer missing! |
Please take a look.
At this point the change of
.*FFT
->.*Coefficients
and.*IFFT
->.*Sequence
methods names should make sense.