Problem statement
In an n-dimensional, given a 2D subspace and a target direction (unit vector) , how to rotate a random vector along such that the projection of on to aligned with .
Idea
Naive approach
A naive approach would be to Rotate any vector to a target angle in high dimensional space, which involves the following steps:
- Project onto the mapping of to get
- Find the angle between and
- Calculate the rotation matrix using
Transclude of Rotate-from-one-vector-to-another-vector-in-high-dimensional-space#^e160cf
- Apply on
Each of these steps will involve 1 matrix multiplication, except for step 3 (if using eq. (2) with precomputed values), resulting in 3 matrix multiplications in total. This is not efficient and we can do better.
Rotation with only 1 matrix multiplication
Looking at eq. (1) from above, computes the to-be-rotated component of by
- map down to
- do the rotation by
- then map back up
Since this transformation preserves the norm, we can instead precompute this for the unit vector, then scale the result by .
Thus, the transformation becomes:
with be the angle between and the unit vector .
As the result, only one matrix multiplication is needed to compute , other multiplications can be precomputed.
Implementation
def rotate_to_target(x, target_degree, basis1, basis2):
assert len(basis1.shape) == 1
assert len(basis2.shape) == 1
assert basis1.shape == basis2.shape
n = basis1.shape[-1]
# ensure bases are orthonormal
u = basis1 / np.linalg.norm(basis1)
v = basis2 - (basis2 @ u) * u
v /= np.linalg.norm(v)
theta = np.deg2rad(target_degree)
cos_theta = np.cos(theta)
sin_theta = np.sin(theta)
P = np.outer(u, u) + np.outer(v, v)
# rotate counter-clockwise
R_theta = [
[cos_theta, -sin_theta],
[sin_theta, cos_theta]
]
uv = np.column_stack([u, v])
rotated_component = uv @ R_theta @ np.array([1, 0])
Px = x @ P
scale = np.linalg.norm(Px, axis=-1, keepdims=True)
result = x - Px + scale * rotated_component
return result