As part of the effort to make Rotation broadcastable to arbitrary batch dimensions, we came across a shape inconsistency in the results of the Rotation.from_euler
/ from_davenport
functions.
@j-bowhay proposed to take the discussion here to see if there are any objections to making a breaking change. Please let us know if you have a strong opinion on this one / are opposed to making this change without a major release.
The proposed changes are as follows:
Euler Angle Cases and Proposed Behavior
Case | Sequence | Input Shape | Output Shape | Behavior |
---|---|---|---|---|
0D | "x" |
() |
(4,) |
Unchanged |
0D | "xyz" |
() |
raise |
Unchanged |
1D | "x" |
(1,) |
(1, 4) → (4,) |
Changed |
1D | "xyz" |
(3,) |
(4,) |
Unchanged |
2D | "x" |
(N, 1) |
(N, 4) |
Unchanged |
2D | "xyz" |
(N, 3) |
(N, 4) |
Unchanged |
Note: After the 2D case, standard broadcasting rules are followed already.
The logic here is that we promote 0D arrays, float
s and int
s to 1D. angles.shape[-1]
must then be the same as num_axes
, i.e. the last dimension of angles describes by how much we turn around each axis. Consequently, 0D arrays remain invalid for sequences with more than one axis. The resulting rotation has the shape np.atleast_1d(angles).shape[:-1]
.
Davenport Cases and Proposed Behavior
Case | Axes Shape | Angles Shape | Output Shape | Behavior |
---|---|---|---|---|
0D | (3,) |
() |
(4,) |
Unchanged |
1D | (3,) |
(1,) |
(1, 4) → (4,) |
Changed |
1D | (3,) |
(N,) |
(N, 4) → raise |
Changed, raise if N != 1 |
1D | (2, 3) |
(2,) |
(4,) |
Unchanged |
2D | (3,) |
(N, 1) |
(N, 4) |
Unchanged |
2D | (2, 3) |
(2, 1) |
(2, 4) |
Changed, raise 2 != 1 |
2D | (2, 3) |
(2, 3) |
— (error) | Unchanged |
2D | (1, 3) |
(2, 3) |
(2, 4) |
Changed, raise 1 != 3 |
Note: After the 2D case, standard broadcasting rules are followed already.
The logic here is that axes are promoted to at least 2D and angles to at least 1D. axes.shape[-2]
must then match angles.shape[-1]
, i.e. as in euler, the last dimension of angles describes by how much we turn around each axis. Consequently, 0D arrays remain invalid for sequences of more than one axis. The resulting rotation has the shape np.broadcast_shapes(np.atleast_2d(axes).shape[:-2], np.atleast_1d(angles).shape[:-1])
.
This is consistent with from_euler
if we treat seq
as a 2D array of axes and replace each letter with its respective turn axis.
Github issue for reference: MAINT: spatial.transform.Rotation: `from_euler/from_davenport` shape consistency · Issue #23568 · scipy/scipy · GitHub
Larger overview over what’s happening: ENH: spatial.transform: array API standard support tracker · Issue #23391 · scipy/scipy · GitHub