Skip to content

Commit d43d9a1

Browse files
kunhe001meta-codesync[bot]
authored andcommitted
Keep quaternion backend dispatch scriptable (#1604)
Summary: Pull Request resolved: #1604 PyMomentum lazily imports its Triton quaternion backend to avoid a circular dependency. TorchScript attempts to compile that import when a scripted caller uses rotation conversion with `backend="auto"`, causing inference workflows to fail before dispatch can fall back to the Torch backend. Return through the pure Torch implementation immediately when scripting, before resolving the lazy Triton backend. Eager automatic and Triton dispatch remain unchanged, while scripted graphs contain no Python operation and remain serializable. Cover all three rotation conversion entry points, including `torch.jit.save`. ___ Differential Revision: D114808178 fbshipit-source-id: 56928d20f44221765d78f02e72596a3ff4ee47a5
1 parent 1895767 commit d43d9a1

2 files changed

Lines changed: 47 additions & 3 deletions

File tree

‎pymomentum/quaternion.py‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -206,9 +206,11 @@ def to_rotation_matrix_assume_normalized(
206206
Convert quaternions to 3x3 rotation matrices.
207207
208208
:parameter q: (nBatch x k x 4) tensor with the quaternions in ((x, y, z), w) format.
209-
:parameter backend: Backend to use. "auto" opts into the experimental Triton backend on CUDA float32, else torch.
209+
:parameter backend: Backend to use. "auto" opts into the experimental Triton backend on CUDA float32, else torch. Scripted calls always use the Torch backend.
210210
:return: (nBatch x k x 3 x 3) tensor with 3x3 rotation matrices.
211211
"""
212+
if torch.jit.is_scripting():
213+
return torch_quaternion.to_rotation_matrix_assume_normalized(q)
212214
backend = resolve_backend(
213215
backend,
214216
q,
@@ -228,10 +230,12 @@ def to_rotation_matrix(
228230
Convert quaternions to 3x3 rotation matrices.
229231
230232
:parameter q: (nBatch x k x 4) tensor with the quaternions in ((x, y, z), w) format.
231-
:parameter backend: Backend to use. "auto" opts into the experimental Triton backend on CUDA float32, else torch.
233+
:parameter backend: Backend to use. "auto" opts into the experimental Triton backend on CUDA float32, else torch. Scripted calls always use the Torch backend.
232234
:parameter eps: Minimum quaternion norm used during normalization.
233235
:return: (nBatch x k x 3 x 3) tensor with 3x3 rotation matrices.
234236
"""
237+
if torch.jit.is_scripting():
238+
return torch_quaternion.to_rotation_matrix(q, eps=eps)
235239
backend = resolve_backend(
236240
backend,
237241
q,
@@ -329,9 +333,11 @@ def from_rotation_matrix(
329333
330334
:parameter matrices: A tensor of shape (..., 3, 3) representing the rotation matrices.
331335
:parameter eta: Numerical precision threshold (unused, kept for compatibility).
332-
:parameter backend: Backend to use. "auto" opts into the experimental Triton backend on CUDA float32, else torch.
336+
:parameter backend: Backend to use. "auto" opts into the experimental Triton backend on CUDA float32, else torch. Scripted calls always use the Torch backend.
333337
:return: A tensor of shape (..., 4) representing the quaternions in ((x, y, z), w) format.
334338
"""
339+
if torch.jit.is_scripting():
340+
return torch_quaternion.from_rotation_matrix(matrices, eta)
335341
backend = resolve_backend(
336342
backend,
337343
matrices,

‎pymomentum/test/test_quaternion.py‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
# pyre-strict
77

8+
import io
89
import math
910
import unittest
1011

@@ -26,6 +27,43 @@ def generateRandomQuats(sz: int) -> torch.Tensor:
2627

2728

2829
class TestQuaternion(unittest.TestCase):
30+
def test_rotation_conversions_are_scriptable(self) -> None:
31+
quaternions = quaternion.normalize(torch.randn(4, 4))
32+
matrices = quaternion.to_rotation_matrix(quaternions)
33+
34+
scripted_to_matrix_assume_normalized = torch.jit.script(
35+
quaternion.to_rotation_matrix_assume_normalized
36+
)
37+
scripted_to_matrix = torch.jit.script(quaternion.to_rotation_matrix)
38+
scripted_from_matrix = torch.jit.script(quaternion.from_rotation_matrix)
39+
40+
self.assertTrue(
41+
torch.allclose(
42+
scripted_to_matrix_assume_normalized(quaternions, "auto"),
43+
quaternion.to_rotation_matrix_assume_normalized(
44+
quaternions, backend="auto"
45+
),
46+
)
47+
)
48+
self.assertTrue(
49+
torch.allclose(
50+
scripted_to_matrix(quaternions, "auto"),
51+
quaternion.to_rotation_matrix(quaternions, backend="auto"),
52+
)
53+
)
54+
self.assertTrue(
55+
torch.allclose(
56+
scripted_from_matrix(matrices, 1e-6, "auto"),
57+
quaternion.from_rotation_matrix(matrices, backend="auto"),
58+
)
59+
)
60+
for scripted_conversion in (
61+
scripted_to_matrix_assume_normalized,
62+
scripted_to_matrix,
63+
scripted_from_matrix,
64+
):
65+
torch.jit.save(scripted_conversion, io.BytesIO())
66+
2967
def test_euler_conversion(self) -> None:
3068
torch.manual_seed(0) # ensure repeatability
3169
nBatch = 6

0 commit comments

Comments
 (0)