Skip to content

MAINT: Use scipy builtins rotation to compute quaternions.#6241

Merged
brisvag merged 5 commits intonapari:mainfrom
Carreau:gimbal
Oct 4, 2023
Merged

MAINT: Use scipy builtins rotation to compute quaternions.#6241
brisvag merged 5 commits intonapari:mainfrom
Carreau:gimbal

Conversation

@Carreau
Copy link
Copy Markdown
Contributor

@Carreau Carreau commented Sep 20, 2023

As often, it is better to rely on existing battle tested code than roll our own. This also help testing the upstream code more.

This replace our own quaternion to euler angles with using the Rotations call in scipy.

This file comes from far at the beginning of napari, and had one significant modification in #2530 (WRT degenerate case), which I believe scipy already handle with the gimbals lock warning and is tested.

Then α, β, γ is not unique when β=±π/2, then current new implementation would have α=<some value>, γ=0, the old code α=0, γ=2.π-<some value>.

Note that the outputs are slightly different, with sometime the angles being +/- pi , +/- zero i in some case.

You can test the first commit of this branch with this test script that has a few hardcoded degenerate case, as well as pick ~10k random quaternion and compare the resutls.

https://gist.github.com/Carreau/53dfaefa0c07aa3a7a0bd90e890810bb

+/- zero
[-2.86, -3.12, -2.88, -2.64]
   old  1.491, 0.000, 1.658
   new  1.491, -0.000, 1.658
   diff 0.000, 0.000, -0.000
 +/- pi
[1.44, 0.48, 3.93, -1.31]
   old  -3.142, 0.702, -2.498
   new  3.142, 0.702, -2.498
   diff 6.283, 0.000, 0.000
gimbal lock:

[0.09, -0.78, 0.09, 0.78]
   old  0.000, 1.571, -2.912
   new  2.912, 1.571, 0.000
   diff 2.912, 0.000, -2.912

[-0.64, -0.58, -0.64, 0.58]
   old  0.000, 1.571, 1.473
   new  4.811, 1.571, 0.000
   diff 4.811, 0.000, 1.473

As often, it is better to rely on existing battle
tested code than roll our own. This also help testing the upstream code
more.

This replace our own quaternion to euler angles with using the Rotations
call in scipy.

This file comes from far at the beginning of napari, and had one
significant modification in napari#2530 (WRT degenerate case), which I believe
scipy already handle with the gimbals lock warning and is tested.

Then α, β, γ is not unique when β=±π/2,
then current new implementation would have `α=<some value>`, `γ=0`, the old code
`α=0`, `γ=2.π-<some value>`.

Note that the outputs are slightly different, with sometime the angles
being +/- p , +/- zero i in some case.

You can test this branch with this test script that has a few hardcoded
degenerate case, as well as pick ~10k random quaternion and compare the
resutls.

https://gist.github.com/Carreau/53dfaefa0c07aa3a7a0bd90e890810bb

```
+/- zero
[-2.86, -3.12, -2.88, -2.64]
   old  1.491, 0.000, 1.658
   new  1.491, -0.000, 1.658
   diff 0.000, 0.000, -0.000
```

```
 +/- pi
[1.44, 0.48, 3.93, -1.31]
   old  -3.142, 0.702, -2.498
   new  3.142, 0.702, -2.498
   diff 6.283, 0.000, 0.000
```

```
gimbal lock:

[0.09, -0.78, 0.09, 0.78]
   old  0.000, 1.571, -2.912
   new  2.912, 1.571, 0.000
   diff 2.912, 0.000, -2.912

[-0.64, -0.58, -0.64, 0.58]
   old  0.000, 1.571, 1.473
   new  4.811, 1.571, 0.000
   diff 4.811, 0.000, 1.473
```
@Carreau
Copy link
Copy Markdown
Contributor Author

Carreau commented Sep 20, 2023

Opening as draft, if we think we are ok with this I can remove completely the old function and rework the documentation, but I wanted to make sure I don't miss anything.

@codecov
Copy link
Copy Markdown

codecov Bot commented Sep 20, 2023

Codecov Report

Merging #6241 (4094d71) into main (eaff277) will increase coverage by 0.05%.
Report is 24 commits behind head on main.
The diff coverage is 100.00%.

@@            Coverage Diff             @@
##             main    #6241      +/-   ##
==========================================
+ Coverage   91.60%   91.65%   +0.05%     
==========================================
  Files         583      584       +1     
  Lines       51364    51391      +27     
==========================================
+ Hits        47051    47103      +52     
+ Misses       4313     4288      -25     
Files Coverage Δ
napari/_vispy/__init__.py 100.00% <100.00%> (ø)
napari/_vispy/_tests/test_utils.py 100.00% <100.00%> (ø)
napari/_vispy/camera.py 93.89% <100.00%> (ø)
napari/_vispy/utils/quaternion.py 100.00% <100.00%> (ø)

... and 31 files with indirect coverage changes

@Carreau
Copy link
Copy Markdown
Contributor Author

Carreau commented Sep 24, 2023

Added a commit that remove old impl and cleanup docs.

@Carreau Carreau marked this pull request as ready for review September 24, 2023 10:02
@Carreau
Copy link
Copy Markdown
Contributor Author

Carreau commented Sep 24, 2023

as a note, this function is always used with degree=True, I would be inclined to remove the degree keyword and always assume it's True.

Copy link
Copy Markdown
Contributor

@brisvag brisvag left a comment

Choose a reason for hiding this comment

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

Nice! I'm in favour of removing the kwarg since it's unneeded.

theta_1 = np.arctan2(
2 * (q.w * q.z + q.y * q.x),
1 - 2 * (q.y * q.y + q.z * q.z),
with warnings.catch_warnings():
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Each time the warnings.catch_warnings() is used then a list of already seen warnings is reset.
So using it in the context of often-used functions may result in bigger spamming of user. Why warning happen and how we avoid it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We can't avoid it because math, it is in SciPy and is triggered when the set of Euler Angles are degenerate and you are looking directly up a pole.

Can you point me to the issue or an explanation of "catch warnings" reseting the warning list ?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I could send a pr to SciPy with a parameter gimbal_lock_ok:bool to prevent the warning.

Comment thread napari/_vispy/utils/quaternion.py Outdated
Carreau and others added 2 commits September 25, 2023 09:03
@github-actions github-actions Bot added the tests Something related to our tests label Sep 25, 2023
@jni
Copy link
Copy Markdown
Member

jni commented Sep 26, 2023

@Carreau @brisvag personally I would keep degree=True (ie revert f9784a4) because as someone reading the code I am never sure of which units the angles are/should be in, and this documents that clearly.

Other than that I am +1 on these changes, minus the caveat from @Czaki — which I suppose would take some time, plus we try to follow SPEC0 for SciPy so it'll be like 2y? How do we handle this? Shall we fix that CPython issue? Wait that'll take longer... 😂

@Carreau
Copy link
Copy Markdown
Contributor Author

Carreau commented Sep 26, 2023

I would keep degree=True (ie revert f9784a4) because as someone reading the code I am never sure of which units the angles are/should be in, and this documents that clearly.

That's fair.

Shall we fix that CPython issue

There is already a 5 Years Old PR that received no comments, so unless we can poke at some CPython core dev to get that fixed...

Another possibility is to insert the filter once and for all.

@Carreau
Copy link
Copy Markdown
Contributor Author

Carreau commented Sep 26, 2023

I would keep degree=True (ie revert f9784a4) because as someone reading the code I am never sure of which units the angles are/should be in, and this documents that clearly.

Would you be ok or prefer to rename to quaternion2euler_degrees or similar ? This way we don't have the keyword, and it is explicit. I just don't like having an unused keyword and adding types to a function which types depends on a boolean is possible but annoying.

@Czaki
Copy link
Copy Markdown
Collaborator

Czaki commented Sep 26, 2023

Other than that I am +1 on these changes, minus the caveat from @Czaki — which I suppose would take some time, plus we try to follow SPEC0 for SciPy so it'll be like 2y? How do we handle this? Shall we fix that CPython issue? Wait that'll take longer... 😂

I do not feel that it is important to not emit warnings on non latest Scipy version.

@jni
Copy link
Copy Markdown
Member

jni commented Sep 27, 2023

@Carreau yes I'm ok with renaming the function to include "degrees". 🙏

@brisvag brisvag added the ready to merge Last chance for comments! Will be merged in ~24h label Oct 1, 2023
@brisvag brisvag added this to the 0.5.0 milestone Oct 1, 2023
@brisvag brisvag added the maintenance PR with maintance changes, label Oct 1, 2023
@brisvag brisvag merged commit be16a7d into napari:main Oct 4, 2023
@brisvag brisvag removed the ready to merge Last chance for comments! Will be merged in ~24h label Oct 4, 2023
@brisvag
Copy link
Copy Markdown
Contributor

brisvag commented Oct 4, 2023

@Carreau I didn't realize this was still marked as ready to merge despite the issue with catch-warnings pointed out by @Czaki, so I shouldn't have merged. I'll revert the PR. Can you then reopen it while we wait for scipy to merge your changes?

@brisvag
Copy link
Copy Markdown
Contributor

brisvag commented Oct 4, 2023

For reference: scipy/scipy#19306

brisvag added a commit that referenced this pull request Oct 4, 2023
…6241)" (#6299)

# References and relevant issues
This reverts #6241.

# Description
I goofed, that PR should have stayed open until we addressed the catch
warning issue.
@Carreau
Copy link
Copy Markdown
Contributor Author

Carreau commented Oct 4, 2023

Do you want to reset hard main to scipy/spatial/transform/tests/test_rotation.py , and I can re-submit cc8d3ec ? This way we don't have a revert on Main ?

@brisvag
Copy link
Copy Markdown
Contributor

brisvag commented Oct 4, 2023

As mentioned on zulip, no problem with reverting with a new PR, but more pain comes from rewriting history. Let's move on with a new Pr!

@Carreau
Copy link
Copy Markdown
Contributor Author

Carreau commented Oct 4, 2023

As mentioned on zulip, no problem with reverting with a new PR, but more pain comes from rewriting history. Let's move on with a new Pr!

No worries, I'll reopen a PR once I have some confirmation that SciPy has the featuer upstream

Carreau added a commit to Carreau/napari that referenced this pull request Nov 8, 2023
This is a partial reapply of napari#6241, that was reverted, but reapply only
the part where we simplify the API that has an unsed argument  and
rename the function.

This is thus a partial revert of napari#6299 which itself is a revert of napari#6241
Carreau added a commit to Carreau/napari that referenced this pull request Nov 8, 2023
This is a partial reapply of napari#6241, that was reverted, but reapply only
the part where we simplify the API that has an unsed argument  and
rename the function.

This is thus a partial revert of napari#6299 which itself is a revert of napari#6241
Carreau added a commit to Carreau/napari that referenced this pull request Nov 8, 2023
This is a partial reapply of napari#6241, that was reverted, but reapply only
the part where we simplify the API that has an unsed argument  and
rename the function.

This is thus a partial revert of napari#6299 which itself is a revert of napari#6241
Carreau added a commit to Carreau/napari that referenced this pull request Nov 8, 2023
This is a partial reapply of napari#6241, that was reverted, but reapply only
the part where we simplify the API that has an unsed argument  and
rename the function.

This is thus a partial revert of napari#6299 which itself is a revert of napari#6241
Carreau added a commit to Carreau/napari that referenced this pull request Nov 20, 2023
This is a partial reapply of napari#6241, that was reverted, but reapply only
the part where we simplify the API that has an unsed argument  and
rename the function.

This is thus a partial revert of napari#6299 which itself is a revert of napari#6241
Carreau added a commit to Carreau/napari that referenced this pull request Nov 20, 2023
This is a partial reapply of napari#6241, that was reverted, but reapply only
the part where we simplify the API that has an unsed argument  and
rename the function.

This is thus a partial revert of napari#6299 which itself is a revert of napari#6241
jni pushed a commit that referenced this pull request Jan 8, 2024
This is a partial reapply of #6241, that was reverted, but reapply only
the part where we simplify the API that has an unsed argument and rename
the function.

This is thus a partial revert of #6299 which itself is a revert of #6241
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

maintenance PR with maintance changes, tests Something related to our tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants