-
Notifications
You must be signed in to change notification settings - Fork 272
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
Speed up rotations, closes #2097 #2098
Speed up rotations, closes #2097 #2098
Conversation
Codecov ReportBase: 92.51% // Head: 92.75% // Increases project coverage by
Additional details and impacted files@@ Coverage Diff @@
## master #2098 +/- ##
==========================================
+ Coverage 92.51% 92.75% +0.23%
==========================================
Files 199 214 +15
Lines 16561 17876 +1315
==========================================
+ Hits 15322 16580 +1258
- Misses 1239 1296 +57
Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here. ☔ View full report at Codecov. |
@maxnoe I also tried to implement the missing |
So astropy blocks this because it thinks there to many coordinates (6) for only three transforms (3) |
Works now, but it's a little bit hacky... |
1b51e71
to
e23304c
Compare
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.
Should we also replace other places where we use hand-coded rotation matrices? I see for example:
ctapipe.utils.linalg.rotation_matrix_2d()
, used inCameraGeometry.rotate()
andtoymodel
ctapipe.reco.impact.ImPACTReconsturctor.rotate_translate()
Maybe there are others?
Yes I think so. I also saw a few other by hand rotations in the code. But I think that I have to check if this is at least not slower right now with scipy |
I did a quick check with %timeit and a 2D rotation matrix using ctapipe.utils.linalg, and it was slightly faster to use scipy's method, at least there is not a huge difference (the small difference is likely due to units)
|
However, I notice in #2052 that ImPACT now Certainly you can replace any usage of |
The ground to tilted transform also seems to be faster.
On this branch:
|
But it's slower for the |
I will do some profiling to see if the generation of the matrices is the problem or the |
Could be something else, since when I use many phis (remember to construct them outside the timeit loop), I get this: phis = np.asarray(1000*[np.pi])
In [12]: %timeit rotation_matrix_2d(phis*u.rad)
228 µs ± 5.35 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
In [13]: %timeit spatial.transform.Rotation.from_euler("x", phis).as_matrix()
55.5 µs ± 21.2 µs per loop (mean ± std. dev. of 7 runs, 10,000 loops each) EDIT: I left the unit in the first one, which adds overhead. Removing it they are closer: In [21]: phis = np.asarray(1000*[np.pi]) * u.rad
In [22]: %timeit rotation_matrix_2d(phis)
181 µs ± 24.6 µs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
In [23]: %timeit spatial.transform.Rotation.from_euler("x", phis.to_value(u.rad)).as_matrix()
98.9 µs ± 3.73 µs per loop (mean ± std. dev. of 7 runs, 10,000 loops each) |
3d0d27c
to
e23304c
Compare
So profiling says |
I'm now using quaternions to produce the rotation matrices. It's faster on this branch now: from ctapipe.coordinates import GroundFrame, TiltedGroundFrame
from astropy.coordinates import AltAz, SkyCoord
import astropy.units as u
import numpy as np
from cProfile import Profile
ground = GroundFrame(x=1000*[1] * u.m, y=1000*[2] * u.m, z=1000*[3] * u.m)
pointing = SkyCoord(alt=1000*[90] * u.deg, az=1000*[180] * u.deg, frame=AltAz())
frame = TiltedGroundFrame(pointing_direction=pointing) this branch: %timeit ground.transform_to(frame)
176 µs ± 714 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each) main: %timeit ground.transform_to(frame)
203 µs ± 607 ns per loop (mean ± std. dev. of 7 runs, 1,000 loops each) |
a5ff2f7
to
e2407b5
Compare
Just to understand the difference: was it just that the code in scipy to turn Euler angles into a quaternion is slower than doing it manually? (i.e. |
Yes it is slower in this case because scipy constructs a quaternion for the first rotation axis and one for the second rotation axis. Those are multiplied via Hamilton product. |
So 12 of 16 terms for this 2 axis rotation in scipy are always 0 but are computed everytime |
I think this is also the reason why your test for the 2d rotation is faster. This doesn't require the Hamilton product since it is only one axis. E.g. an xyz rotation in scipy computes 3 basic quaternions and multiplies them. Resulting in about 6 cos/sin calls and 32 multiplications |
I am not sure these quaternion calculations are more readable than what we have before and njitting the Rotation matrix should be simple and probably even faster... |
Yes I think so too... it's not readable at all. It's even more difficult to understand this calculation than the matrix product used before |
I returned to the original implementation plus Max's faster _get_xyz, the many_to_many relation and some einsums. on master:
this branch:
The title of this PR as well as the history is more or less a mess now. Should I open a new PR just for the current changes? |
@StFroese Not needed, just update the title of the PR please. If you want you can rebase / squash into more meaningfull commits or I will just "squash and merge" at the end. |
948ecf8
to
fdd14a6
Compare
@maxnoe best I can do for now. I let you decide if you want to squash it into one commit. I don't mind. |
No description provided.