Conversation
| """ | ||
| atomgroup = group.atoms | ||
| e_val, e_vec = np.linalg.eig(atomgroup.moment_of_inertia(wrap=wrap)) | ||
| e_val, e_vec = np.linalg.eigh(atomgroup.moment_of_inertia(wrap=wrap)) |
There was a problem hiding this comment.
I think this should fix it?
Someone with better maths knowledge should double check this - my understanding is that the moment of intertia tensor is always a real symmetric matrix, so it should satisfy the requirements of np.linalg.eigh.
My understanding is that eigh doesn't have the stability issues of eig, so I tried swapping it out and it seems to be working fine.
There was a problem hiding this comment.
Indeed, eigh is preferred for symmetric/Hermitian matrices.
| [ | ||
| [0.78787867, 0.26771575, -0.55459488], | ||
| [-0.40611024, -0.45112859, -0.7947059], | ||
| [-0.78787867, -0.26771575, 0.55459488], |
There was a problem hiding this comment.
I believe these are fine since the eigenvector sign is arbitrary?
There was a problem hiding this comment.
Yes, the sign is algorithm-dependent and therefore can differ between eig and eigh.
There was a problem hiding this comment.
Looks like some tests are still failing because of sign issues. Maybe to make them more robust we could check for all the eigenvector tests that they are numerically equivalent up to a sign change?
There was a problem hiding this comment.
Yeah I'm thinking we should do an abs check, will update the tests accordingly.
There was a problem hiding this comment.
Done - just to sanity check me, can I get a re-review please?
There was a problem hiding this comment.
I thought about it again. abs is not the same as sign-check. Will comment on review.
| """ | ||
| atomgroup = group.atoms | ||
| e_val, e_vec = np.linalg.eig(atomgroup.moment_of_inertia(wrap=wrap)) | ||
| e_val, e_vec = np.linalg.eigh(atomgroup.moment_of_inertia(wrap=wrap)) |
There was a problem hiding this comment.
Indeed, eigh is preferred for symmetric/Hermitian matrices.
| [ | ||
| [0.78787867, 0.26771575, -0.55459488], | ||
| [-0.40611024, -0.45112859, -0.7947059], | ||
| [-0.78787867, -0.26771575, 0.55459488], |
There was a problem hiding this comment.
Yes, the sign is algorithm-dependent and therefore can differ between eig and eigh.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #5404 +/- ##
===========================================
+ Coverage 93.63% 93.85% +0.21%
===========================================
Files 182 182
Lines 22509 22509
Branches 3202 3202
===========================================
+ Hits 21076 21125 +49
+ Misses 965 922 -43
+ Partials 468 462 -6 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Although testing for abs() equality works, it's not equivalent to testing that the two vectors are equal up to a sign change. The abs check can, in principle, lead to false positives.
I suggest to do this properly and use a helper function such as
def assert_vectors_no_sign_close(a, b):
if not (np.allclose(a, b) or np.allclose(a, -b)):
# raise assertion with the sign choice that looks closest
x = b if np.linalg.norm(a - b) < np.linalg.norm(a + b) else -b
assert_allclose(a, x)allclose() may need proper rtol and atol if the defaults aren't appropriate.
| * The `principal_axes` method in :class:`Masses` now uses | ||
| `np.linalg.eigh` instead of `np.linalg.eig`, improving numerical | ||
| stability. This may lead to slightly different results from previous | ||
| version of MDAnalysis. |
There was a problem hiding this comment.
Should go under changes, not really a fix. Reference PR #5404
There was a problem hiding this comment.
Interesting, I see this as a a fix in the sense of "before we were using something that could yield a complex number because it was prone to floating point precision issues, now it's fixed" - what do you think?
There was a problem hiding this comment.
My view was that this was a robustness change that was surfaced by a newer version of numpy. However, your interpretation also makes sense so I am happy to leave it as a fix. I'd still add the issue/PR number, though.
| assert_almost_equal(result[0], ref[method_name][0], self.prec) | ||
| assert_almost_equal(result[1], ref[method_name][1], self.prec) | ||
| elif method_name == "principal_axes": | ||
| assert_almost_equal( |
There was a problem hiding this comment.
Adding comment regarding sign-check would be useful + ref GH-5404
There was a problem hiding this comment.
comment along the lines of
The direction (sign) of the principal axes is dependent on the specific algorithm
used in numpy.linear.eigh() but the direction itself is not physically relevant
so we have to test both directions.
| [ | ||
| [0.78787867, 0.26771575, -0.55459488], | ||
| [-0.40611024, -0.45112859, -0.7947059], | ||
| [-0.78787867, -0.26771575, 0.55459488], |
There was a problem hiding this comment.
I thought about it again. abs is not the same as sign-check. Will comment on review.
|
Thanks @orbeckst - here's a second attempt with something a little bit more fancy (einsum -> sign then use that to flip antiparallel vectors before the assert). |
| # specific algorithm used, but the direction itself is not physically | ||
| # relevant, so we get the signs to flip any anti-parallel vectors before | ||
| # comparing the two results arrays | ||
| signs = np.sign(np.einsum("ij,ij->i", result, ref[method_name])) |
There was a problem hiding this comment.
fancy, I convinced myself that it does the right thing for the output of principal_axes().
Fixes #5403
Changes made in this Pull Request:
np.degreescould be fed a complex number fromprincipal_axesdue to floating point noise.LLM / AI generated code disclosure
LLMs or other AI-powered tools (beyond simple IDE use cases) were used in this contribution: no
PR Checklist
package/CHANGELOGfile updated?package/AUTHORS? (If it is not, add it!)Developers Certificate of Origin
I certify that I can submit this code contribution as described in the Developer Certificate of Origin, under the MDAnalysis LICENSE.