-
Notifications
You must be signed in to change notification settings - Fork 101
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
WIP: native implementation of fftfit #777
base: master
Are you sure you want to change the base?
Conversation
The point of Tolerances can be a problem for this sort of testing - we need to be realistic about how exact the answers are going to be, particularly in noiseless test cases. It is made more complicated by the fact that once we start introducing noise, we have tests that we expect to fail a certain fraction of the time; the current mechanism for this is to use random seeds and repeat failing tests - if a test that's supposed to fail 1% of the time fails three times in a row, something is wrong. @Zac-HD might be interested in this for |
@aarchiba Just curious: Is the pure-python version better in various ways from the original Fortran Joe Taylor code that PRESTO uses? I like the idea of getting rid of ancient code, but I do think it is kind of cool that that code has had so much use over the years! (pulsar code nostalgia) |
Good stuff @aarchiba. Let's see what these tests say! |
I see: https://travis-ci.org/github/nanograv/PINT/jobs/722025712. |
Heh. I do see the charm of keeping ancient code in use. And if it works reliably, great! In fact might it be worth firing up f2py or something and putting it in here? As it stands this is meant basically as a shootout between the various options. Ultimately I know we only need one that works, but... |
That particular error - f(a) and f(b) have the same sign - means that your first-pass effort at finding the peak is not actually successfully finding the peak; it's starting somewhere that might or might not contain a peak. My guess is that using only 32 harmonics for a 2048-bin profile (or whatever) is not accurate. And also unnecessary. |
One reason I implement this from scratch is that for finding the phase shift there is a cheap fast algorithm that is more or less guaranteed to succeed: FFT the profile and template, multiply, IFFT and compute the CCF at (say) sixteen times the number of bins in the larger of profile and template. Because the CCF is band-limited all it's peaks, noise or real, are at least sixteen bins wide, so you can pick the highest point and it's neighbours are more or less guaranteed to make a three-point maximization bracket. And you can compute points of the IDFT for precision maximization. The problem cases are the ones that are legitimately problems - pairs of very nearly equal maxima, or completely flat CCFs. (And once you have the phase shift everything else is a linear problem.) You do have to work out the appropriate derivatives to get approximate error bars, though, and my existing code does not do an adequate job of that (as hypothesis demonstrates). |
Also - have people stopped blackening their code? The code formatting checker fails all over the place. It's easy to add a pre-commit hook that doesn't let you commit ill-formatted code. |
As far as I know, everything should be black. The master branch passes the black test. |
@aarchiba I definitely did not blacken my code, I follow pep8 as a guideline but never ran the code through |
https://travis-ci.org/github/nanograv/PINT/jobs/722068749
That job is marked as allowed to fail in Travis. |
Oh I just hit f8 before saving and all the code in files I touch gets blackened. So that's not the problem here. |
I stand corrected: master does appear to pass, somehow, in spite of the 61 untouched files I have that now fail. |
Change in |
As for the original fftfit, using f2py is exactly what PRESTO has in it (using Joe's original code). So we should be able to do a shootout just calling it. The way it is usually used is via measure_phase() in get_TOAs.py in PRESTO. |
I can't |
You can't pip install PRESTO, in general. You have to do a normal source install for the C-code (which on Linux is super easy). Then in the top level directory you do "pip install .". But yes, it works with python3 for almost a year now. |
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.
Sticking to the testing part, this looks great to me.
You tend to reach for @composite
when I'd go for a less powerful .map(f)
or builds(f, ...)
, but in simple cases there's not much downside - just a personal style either way.
assert_rms_close()
and assert_allclose_phase()
do seem like good places to add hypothesis.target()
though - I'd experiment with targeting both np.abs(a-b).max()
and np.abs(a-b).sum()
to drive up localised and non-localised imprecision. This should also help with the threshold problem, since I wrote some extra reporting for you.
tests/test_fftfit.py
Outdated
@composite | ||
def vonmises_templates(draw, ns=powers_of_two(), phase=floats(0, 1)): | ||
return fftfit.vonmises_profile(draw(floats(1, 1000)), draw(ns), draw(phase)) |
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.
Because there are no dependencies between the drawn values, you could use builds()
here instead.
@composite | |
def vonmises_templates(draw, ns=powers_of_two(), phase=floats(0, 1)): | |
return fftfit.vonmises_profile(draw(floats(1, 1000)), draw(ns), draw(phase)) | |
def vonmises_templates(ns=powers_of_two(), phase=floats(0, 1)): | |
return builds(fftfit.vonmises_profile, floats(1, 1000), ns, phase) |
This involves slightly less indirection, but the main benefit is readability rather than performance.
@composite | ||
def random_templates(draw, ns=powers_of_two()): | ||
return np.random.randn(draw(ns)) |
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 return builds(np.random.randn, ns)
or even return ns.map(np.random.randn)
.
Aha! I confess I haven't really looked into the functional tools for generating examples; You may notice that the second half of the file doesn't use |
I should add the stingray version too. https://github.com/StingraySoftware/stingray/blob/d64644b3ed69e5cce0c94d73da4bc446aa584b4e/stingray/pulse/pulsar.py |
@aarchiba that version is more unstable even in my limited tests, so I don't think it's worth it |
I tend to go with
and then in
The obvious approach of using Hypothesis to choose parameters (and maybe also random seeds) would probably work OK... I haven't tried it, but while the performance wouldn't be as good as usual I don't think there are any conceptual problems. |
The version I wrote seems to be pretty much bulletproof but I do not have code that computes the uncertainty correctly.
Definitely not random seeds - the point here is that there is a known (small) fraction of random seeds that will make the test fail, even if the code is correct. So searching for seeds that make the test fail and then keeping them around is exactly wrong. Even if |
Okay got error estimates that check with the actual scatter for a simple profile plus noise, now to come up with some more challenging test cases and/or a way to use |
Hi @aarchiba, Could you join the telecon tomorrow Sept 2nd, 2020, at 10:00 am EDT. I would like to have a discussion on this pull request. |
I don't have zoom coordinates but otherwise can make the telecon, sure. |
Initial tests with PRESTO's fftfit produce... segmentation fault in |
Resolved the segfaults (results for too-long profiles). I think there is still something wrong with how I am calling PRESTO's |
80d146b
to
602cc6e
Compare
TODO: test |
Any chance we can get this merged before v0.8? |
We might want to merge Ann's algorithm, that seems the one with the best performance among Python ones, start using it and test it "on the road". I would be happy to call it from Stingray as well, frankly (we already have PINT among our optional dependencies). |
I've had some trouble getting my version of fftfit to work as a drop-in replacement for the PRESTO version - there's some stuff about definitions of the FFT I was having trouble with. The code is all there, though, with an interface I'm happy with, so if I can get the cross-checking working okay this can go in. People might want to comment on the interface I've chosen. |
@aarchiba your interface is good, I had no problem trying it out. |
Indeed that would be a very good idea! Sorry it's not there yet, I wanted to be surer that this was going to work before fleshing out the docs. In fact the intention is that the public-facing interface is all in |
Ah, right! It's all good then. In my opinion, my implementation can be ditched unless there is at least one clear case where it performs better than the others. |
Now that I think of it: is there a reason to put all those functions in |
Initial docstrings for all functions.
Codecov Report
@@ Coverage Diff @@
## master #777 +/- ##
==========================================
+ Coverage 55.57% 55.82% +0.24%
==========================================
Files 85 89 +4
Lines 17725 17999 +274
Branches 3015 3039 +24
==========================================
+ Hits 9851 10048 +197
- Misses 7245 7314 +69
- Partials 629 637 +8
Continue to review full report at Codecov.
|
Reported coverage isn't going to be great overall because we're not testing on a machine with the PRESTO version of FFTFIT. |
This would be a good place to put some hypothesis configuration - in particular to avoid time-based failures, and also introduce faster runs for most CI and one more thorough run. |
Hi @aarchiba, in my opinion, at this point, your method works great and an incomplete/draft implementation would be much better than none (perfect is an enemy of good, someone says). I would love to see this merged :) |
@aarchiba periodic ping to ask for this awesome piece of code to get merged :) |
Yes, @aarchiba how about resolving the conflicts and merging? |
This PR should introduce an implementation of
fftfit
that does not depend on PRESTO. There are several floating around, the goal is to come up with one that demonstrably:Currently the code includes two implementations and a test suite that uses
hypothesis
to test one of them. The hope is that we can have a robust test suite so that we know thefftfit
we use is reliable.Closes #231