Skip to content

Commit 3b5ab51

Browse files
shapovalovmeta-codesync[bot]
authored andcommitted
Fix backprop through cot_laplacian
Summary: `_cot_laplacian_python` used in-place tensor operations (`clamp_`, `/=`, `+=`) on intermediates that participate in autograd. Once the resulting sparse Laplacian is used in a backward pass, these in-place mutations raise a runtime error: ``` RuntimeError: one of the variables needed for gradient computation has been modified by an inplace operation: [torch.sparse.FloatTensor [4, 4]], which is output 0 of SparseCooTensorWithDimsAndTensors, is at version 1; expected version 0 instead. Hint: enable anomaly detection to find the operation that failed to compute its gradient, with torch.autograd.set_detect_anomaly(True, check_nan=False). ``` Replace the in-place ops with out-of-place equivalents so gradients can flow back to the input vertices. - `clamp_` → `clamp` - `cot /= 4.0` → `cot = cot / 4.0` - `L += L.t()` → `L = L + L.t()` Reviewed By: bottler Differential Revision: D111896032 fbshipit-source-id: 4c36677487bae5d37a9d81cb791400576f4b2c5f
1 parent c8fcd83 commit 3b5ab51

2 files changed

Lines changed: 15 additions & 4 deletions

File tree

pytorch3d/ops/laplacian_matrices.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,16 +103,16 @@ def cot_laplacian(
103103
s = 0.5 * (A + B + C)
104104
# note that the area can be negative (close to 0) causing nans after sqrt()
105105
# we clip it to a small positive value
106-
# pyre-fixme[16]: `float` has no attribute `clamp_`.
107-
area = (s * (s - A) * (s - B) * (s - C)).clamp_(min=eps).sqrt()
106+
# pyre-fixme[16]: `float` has no attribute `clamp`.
107+
area = (s * (s - A) * (s - B) * (s - C)).clamp(min=eps).sqrt()
108108

109109
# Compute cotangents of angles, of shape (sum(F_n), 3)
110110
A2, B2, C2 = A * A, B * B, C * C
111111
cota = (B2 + C2 - A2) / area
112112
cotb = (A2 + C2 - B2) / area
113113
cotc = (A2 + B2 - C2) / area
114114
cot = torch.stack([cota, cotb, cotc], dim=1)
115-
cot /= 4.0
115+
cot = cot / 4.0
116116

117117
# Construct a sparse matrix by basically doing:
118118
# L[v1, v2] = cota
@@ -127,7 +127,7 @@ def cot_laplacian(
127127
# L[v2, v1] = cota
128128
# L[v0, v2] = cotb
129129
# L[v1, v0] = cotc
130-
L += L.t()
130+
L = L + L.t()
131131

132132
# For each vertex, compute the sum of areas for triangles containing it.
133133
idx = faces.view(-1)

tests/test_laplacian_matrices.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,14 @@ def test_norm_laplacian(self):
118118
Lnaive[e1, e0] += w01
119119

120120
self.assertClose(L.to_dense(), Lnaive)
121+
122+
def test_cot_laplacian_backward(self):
123+
"""Regression: in-place ops in _cot_laplacian_python break autograd."""
124+
verts = torch.tensor(
125+
[[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [1.0, 1.0, 0.0]],
126+
requires_grad=True,
127+
)
128+
faces = torch.tensor([[0, 1, 2], [1, 3, 2]])
129+
L, inv_areas = cot_laplacian(verts, faces)
130+
(L.to_dense().sum() + inv_areas.sum()).backward()
131+
assert verts.grad is not None

0 commit comments

Comments
 (0)