Skip to content
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

Merged
merged 10 commits into from
May 7, 2018
Merged

fourier: add quarterwave functions #465

merged 10 commits into from
May 7, 2018

Conversation

kortschak
Copy link
Member

@kortschak kortschak commented Apr 14, 2018

Please take a look.

At this point the change of .*FFT -> .*Coefficients and .*IFFT -> .*Sequence methods names should make sense.

@kortschak kortschak changed the title Fourier/quarterwave fourier: add quarterwave functions Apr 14, 2018
@kortschak
Copy link
Member Author

OK, this is ready. PTAL

@vladimir-ch
Copy link
Member

"github.com/gonum/floats" sneaked into fftpack_test.go (already in the initial PR).

Copy link
Member

@vladimir-ch vladimir-ch left a 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.

}
}

// Subroutine Cosqb computes the fast fourier transform of quarter
Copy link
Member

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.

Copy link
Member Author

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.

ifac [15]int
}

// NewQuarterWave returns a QuarterWave initialized for work on sequences of length n.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NewQuarterWaveFFT

Copy link
Member Author

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 {
Copy link
Member

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.

Copy link
Member Author

@kortschak kortschak Apr 15, 2018

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.

Copy link
Member

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 .

@kortschak
Copy link
Member Author

ping

@@ -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,
Copy link
Member

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.

Copy link
Member Author

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 {
Copy link
Member

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 .

@@ -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,
Copy link
Member

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

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -0,0 +1,238 @@
// Copyright ©2018 The Gonum Authors. All rights reserved.
Copy link
Member

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

Copy link
Member Author

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?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds fine.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

return dst
}

// QuarterWaveFFT implements Fast Fourier Transform for quarter wave data.
Copy link
Member

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

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fftpack.Cosqi(n, t.work, t.ifac[:])
}

// CosCoefficients computes the Fast Fourier Transform of quarter wave data for
Copy link
Member

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.

Copy link
Member Author

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.


want := make([]float64, n)
for i := range want {
want[i] = rand.Float64()
Copy link
Member

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.

Copy link
Member

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

Copy link
Member Author

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)
Copy link
Member

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?

Copy link
Member Author

@kortschak kortschak Apr 29, 2018

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.

Copy link
Member

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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file looks good.

Copy link
Member

@btracey btracey left a 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)
Copy link
Member

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.
Copy link
Member

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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file looks good.

default:
x[0] = 0
for k := 0; k < n/2; k++ {
kc := n - k
Copy link
Member

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.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Member Author

@kortschak kortschak Apr 29, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also in cost.go.

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
Copy link
Member

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 :)

Copy link
Member Author

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.
Copy link
Member

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.

@kortschak
Copy link
Member Author

Next chunk done. Haven't found anything that would cause it to break with negative numbers sadly

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.

@kortschak
Copy link
Member Author

PTAL

@@ -0,0 +1,238 @@
// Copyright ©2018 The Gonum Authors. All rights reserved.
Copy link
Member

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) {
Copy link
Member

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)

Copy link
Member Author

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?

Copy link
Member Author

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.

Copy link
Member

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.

Copy link
Member Author

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.

Copy link
Member

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.

Copy link
Member Author

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).

Copy link
Member

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.

Copy link
Member

@btracey btracey left a 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) {
Copy link
Member

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)
Copy link
Member

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.

Copy link
Member Author

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)
Copy link
Member

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

Copy link
Member Author

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)
Copy link
Member

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

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@kortschak
Copy link
Member Author

PTAL

@kortschak
Copy link
Member Author

Thanks for the review

@btracey
Copy link
Member

btracey commented May 7, 2018

Thanks for all the translation work! Another big chunk of functionality no longer missing!

@kortschak kortschak merged commit ea4bf2f into master May 7, 2018
@kortschak kortschak deleted the fourier/quarterwave branch May 7, 2018 22:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants