-
Notifications
You must be signed in to change notification settings - Fork 667
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
update qcprot to cython.floating types #4273
Conversation
use memoryviews throughout for potential performance allows either float32 or float64 inputs
Linter Bot Results:Hi @richardjgowers! Thanks for making this PR. We linted your code and found the following: Some issues were found with the formatting of your code.
Please have a look at the Please note: The |
Still need to do some 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.
This looks reasonable to me. Is there an associated issue this is working towards? (edit: I know there were a few out there that had been opened about fp precision).
@IAlibay I've tagged a related issue, I think we should be moving towards numpy-like behaviour where output dtype matches input dtype and either float or double are allowed. A future discussion is the next major release of MDA will probably have a single precision option, since we're able to get 2x performance & 1/2 memory footprint for this. The memoryview change is just a best-practice and we should gradually modernise to this as it's documented as the recommended route for best performance. |
package/CHANGELOG
Outdated
@@ -13,7 +13,7 @@ The rules for this file: | |||
* release numbers follow "Semantic Versioning" http://semver.org | |||
|
|||
------------------------------------------------------------------------------ | |||
??/??/?? IAlibay, ianmkenney, PicoCentauri | |||
??/??/?? IAlibay, ianmkenney, PicoCentauri, richardjgowers |
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.
Odd, I thought you had conttributed in 2.6 for the GSD stuff, maybe that was 2.5... time flies by.
previously rmsd was being returned as None for some cases, this didn't make sense removed test for int inputs, nonsensical
@@ -494,7 +509,7 @@ def FastCalcRMSDAndRotation(np.ndarray[np.float64_t, ndim=1] rot, | |||
rot[0] = rot[4] = rot[8] = 1.0 | |||
rot[1] = rot[2] = rot[3] = rot[5] = rot[6] = rot[7] = 0.0 | |||
|
|||
return |
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 branch looks like it was a bug. at this point the RMSD has been calculated and the code has failed to determine a suitable rotation matrix, returning None doesn't seem to make sense
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.
e.g. compared to this implementation in BioPython: https://github.com/biopython/biopython/blob/master/Bio/PDB/qcprot.py#L190
Codecov ReportPatch and project coverage have no change.
Additional details and impacted files@@ Coverage Diff @@
## develop #4273 +/- ##
==========================================
Coverage 93.40% 93.40%
==========================================
Files 170 184 +14
Lines 22250 23358 +1108
Branches 4071 4071
==========================================
+ Hits 20783 21818 +1035
- Misses 951 1024 +73
Partials 516 516 ☔ View full report in Codecov by Sentry. |
using float precision seems to break filling the rotation matrix. Not sure why, this works though.
cython.floating[:, :] coords1, | ||
cython.floating[:, :] coords2, | ||
int N, | ||
cython.floating[:] weight): |
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 remember that a while back I tried to improve performance in SciPy by switching from the old NumPy buffer syntax, to memoryviews, and it actually caused a performance regression: scipy/scipy#12379 (comment)
Anyway, you may want to measure that you remain at least performance neutral, my experience with following that best practice hasn't historically been great, but hopefully the situation is better now.
|
||
err = 0.001 if dtype is np.float32 else 0.000001 | ||
assert r == pytest.approx(0.6057544485785074, abs=err) | ||
assert_array_almost_equal(rot, rot_ref) |
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 use assert_allclose
for new test code per the Note
at https://numpy.org/doc/stable/reference/generated/numpy.testing.assert_array_almost_equal.html ?
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.
The RMSD functions are quite time-critical for many applications so if there's any chance for a performance impact then please provide some benchmark data before merge. Thanks!
About float vs double. I remember vaguely that during one of the REU projects we played around with single vs double precision and arrived at the conclusion that float32 is insufficient for the algorithm to properly converge. Of course, I don't remember any details and have no idea if there are any notes anywhere. Primarily I want to say that it simply might not be as easy to say that we just run the algorithm at whatever dtype the rest of the code wants to run and that this will require careful testing. |
@orbeckst benchmarks from MDAnalysisTests.datafiles import PSF, DCD
import MDAnalysis as mda
from MDAnalysis.lib import qcprot
import numpy as np
u = mda.Universe(PSF, DCD)
prot = u.select_atoms('protein')
w = (prot.masses / np.mean(prot.masses)).astype(np.float64)
ref = prot.positions.astype(np.float64)
u.trajectory[1]
conf = prot.positions.astype(np.float64)
rot = np.zeros(9, dtype=np.float64)
A = np.zeros(9)
N = len(prot) then: %%timeit
qcprot.CalcRMSDRotationalMatrix(ref, conf, len(prot), rot, w) old code: The number fluctuate a bit, I think there's no performance gain/loss. I was able to reproduce the memoryview regression that @tylerjereddy flagged up, but I think it's an order of magnitude smaller than the numbers measured here, so it won't show up. It's something to keep in mind if we're writing code that has a lot of function calls (there's 3 here). re: precision, yes I had to make the qcp function use doubles throughout to keep it working. The |
use memoryviews throughout for potential performance
allows either float32 or float64 inputs
Related #3927
Changes made in this Pull Request:
PR Checklist
Developers certificate of origin
📚 Documentation preview 📚: https://mdanalysis--4273.org.readthedocs.build/en/4273/