Skip to content
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

ENH: spatial: Matrix multiplication of Rotation #20690

Closed
moradpur opened this issue May 10, 2024 · 6 comments
Closed

ENH: spatial: Matrix multiplication of Rotation #20690

moradpur opened this issue May 10, 2024 · 6 comments
Labels
enhancement A new feature or improvement scipy.spatial

Comments

@moradpur
Copy link

moradpur commented May 10, 2024

Is your feature request related to a problem? Please describe.

In scipy.spatial.transform.Rotation.mul , If p and q are two rotations, and both p and q contain N rotations, each rotation p[i] is composed with the corresponding rotation q[i], and the output contains N rotations which is not suitable for matrix multiplication and machine learning purposes.

Describe the solution you'd like.

For machine learning purposes, the matrix multiplication of multiple rotations should be possible. I suggest that it gives N*N rotations instead of N Rotation.

Describe alternatives you've considered.

Instead of changing __mul__ method you can add matmul method which is available in numpy too. However, using similarity of rotations as (rotation_1*rotation_2.inv()).magnitude() and giving a covariance N*N matrix will be a blockbuster feature.

Additional context (e.g. screenshots, GIFs)

No response

@moradpur moradpur added the enhancement A new feature or improvement label May 10, 2024
@lucascolley lucascolley changed the title ENH: Matrix multiplication of Rotation ENH: spatial: Matrix multiplication of Rotation May 10, 2024
@scottshambaugh
Copy link
Contributor

scottshambaugh commented May 14, 2024

I don't think we should make the change to __mul__, since it would break standard numpy broadcasting. And putting this behavior in __matmul__ doesn't pattern match with how matmul works in numpy, e.g. a @ b == np.dot(a,b)

However, you can get this to work by storing the rotations in numpy arrays and using its broadcasting rules. Here's an example of how to get what you're looking for:

import numpy as np
from scipy.spatial.transform import Rotation as R

rotations_array_4 = np.array(R.random(4))[:, np.newaxis]  # Shape will be (4, 1)
rotations_array_3 = np.array(R.random(3))  # Shape will be (3,)
resultant_rotations43 = rotations_array_4 * rotations_array_3 

print(resultant_rotations43.shape)
# prints (4, 3), all Rotation objects

You can also keep this going to higher dimensions:

resultant_rotations433 = resultant_rotations43[:, :, np.newaxis] * rotations_array_3
print(resultant_rotations433.shape)
# prints (4, 3, 3), all Rotation objects

@scottshambaugh
Copy link
Contributor

scottshambaugh commented May 14, 2024

Or, to do your blockbuster feature 🙂, you can do the following:

R3 = R.random(3, random_state=0)
rotations_array_3 = np.array(R3)[:, np.newaxis]
rotations_array_3_inv = np.array(R3.inv())
vectorized_magnitude = np.vectorize(lambda x: x.magnitude())
angles = vectorized_magnitude(rotations_array_3 * rotations_array_3_inv)

# In one line:
# angles = np.vectorize(lambda x: x.magnitude())(np.array(R3)[:, np.newaxis] * np.array(R3.inv()))

print(angles)
''' prints out:
[[1.14439170e-16, 2.10407917e+00, 1.50255252e+00],
 [2.10407917e+00, 0.00000000e+00, 2.75493397e+00],
 [1.50255252e+00, 2.75493397e+00, 0.00000000e+00]]
'''

@scottshambaugh
Copy link
Contributor

@nmayorov thoughts?

@nmayorov
Copy link
Contributor

In scipy.spatial.transform.Rotation.mul , If p and q are two rotations, and both p and q contain N rotations, each rotation p[i] is composed with the corresponding rotation q[i], and the output contains N rotations which is not suitable for matrix multiplication and machine learning purposes.

I think the current rules are most suitable for targeted engineering tasks (describe attitude of rotating frames, project or rotate vectors between the frames).

For machine learning purposes, the matrix multiplication of multiple rotations should be possible. I suggest that it gives N*N rotations instead of N Rotation.

Just changing the __mul__ rule is clearly not possible. As @scottshambaugh said the required broadcasting can be achieved by working with matrix (or possibly quaterion) representation of rotations.

Instead of changing mul method you can add matmul method which is available in numpy too. However, using similarity of rotations as (rotation_1rotation_2.inv()).magnitude() and giving a covariance NN matrix will be a blockbuster feature.

I believe this is the case where we can confidently recommend to implement the required logic on top what is already provided. I don't see how we can logically fit "outer product" broadcasting rules into Rotation.

@moradpur
Copy link
Author

Or, to do your blockbuster feature 🙂, you can do the following:

R3 = R.random(3, random_state=0)
rotations_array_3 = np.array(R3)[:, np.newaxis]
rotations_array_3_inv = np.array(R3.inv())
vectorized_magnitude = np.vectorize(lambda x: x.magnitude())
angles = vectorized_magnitude(rotations_array_3 * rotations_array_3_inv)

# In one line:
# angles = np.vectorize(lambda x: x.magnitude())(np.array(R3)[:, np.newaxis] * np.array(R3.inv()))

print(angles)
''' prints out:
[[1.14439170e-16, 2.10407917e+00, 1.50255252e+00],
 [2.10407917e+00, 0.00000000e+00, 2.75493397e+00],
 [1.50255252e+00, 2.75493397e+00, 0.00000000e+00]]
'''

@moradpur
Copy link
Author

@scottshambaugh
Actually, I have tried both of these solutions before. The problem is that they are much slower than just doing a for loop over the regular Rotation(single)*Rotation(Multiple).

@lucascolley lucascolley closed this as not planned Won't fix, can't repro, duplicate, stale May 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement A new feature or improvement scipy.spatial
Projects
None yet
Development

No branches or pull requests

4 participants