Skip to content

Fix edge_edge_mollifier_gradient_wrt_x to use ∇_rest ε_x#239

Draft
Huangzizhou wants to merge 1 commit into
ipc-sim:mainfrom
Huangzizhou:fix/ee-mollifier-shape-derivative
Draft

Fix edge_edge_mollifier_gradient_wrt_x to use ∇_rest ε_x#239
Huangzizhou wants to merge 1 commit into
ipc-sim:mainfrom
Huangzizhou:fix/ee-mollifier-shape-derivative

Conversation

@Huangzizhou
Copy link
Copy Markdown
Collaborator

Summary

edge_edge_mollifier_gradient_wrt_x returns the gradient of the EE parallel mollifier with respect to rest positions, computed as ∇_rest m = ∂m/∂ε · ∇_rest ε (since m depends on rest only through eps_x). The second factor was using edge_edge_mollifier_gradient(positions, eps_x) (returns ∇_position m) instead of edge_edge_mollifier_threshold_gradient(rest) (returns ∇_rest ε).

Sister function edge_edge_mollifier_gradient_jacobian_wrt_x already used the correct factor; this restores consistency between the two.

 if (ee_cross_norm_sqr < eps_x) {
     return edge_edge_mollifier_derivative_wrt_eps_x(
                ee_cross_norm_sqr, eps_x)
-        * edge_edge_mollifier_gradient(ea0, ea1, eb0, eb1, eps_x);
+        * edge_edge_mollifier_threshold_gradient(
+              ea0_rest, ea1_rest, eb0_rest, eb1_rest);
 }

Why it matters

The existing IPC shape_derivative test (test_barrier_potential.cpp:248) uses a stacked-cube scene that doesn't trigger the mollifier branch, so the bug was invisible to existing coverage. It surfaces on near-parallel EE pairs near dhat — in a polyfem shape-optimization transient adjoint, this produced a ~400× per-pair inflation and an ~10⁸× sign-flipped end-to-end gradient.

Test plan

New regression test tests/src/tests/potential/ipc_test_ee_near_parallel_shape_derivative.cpp constructs a 4-vertex / 2-edge scene with a single mollified EE pair (y_sep = 9.99e-4, z_twist ∈ {5e-3, 1e-2, 2e-2}) and FD-checks the full 12×12 analytic shape_derivative against centered FD of gradient at fixed collision set (same convention as test_barrier_potential.cpp:377-391).

  • Without the fix: rel ≈ 1.4–2.6×10⁻² (test FAILS).
  • With the fix: rel ≈ 3–5×10⁻⁴ (test PASSES). Threshold is 5×10⁻³.

🤖 Generated with Claude Code

`edge_edge_mollifier_gradient_wrt_x` returns the gradient of the EE
parallel mollifier with respect to rest positions, computed via the
chain rule ∇_rest m = ∂m/∂ε · ∇_rest ε (since m depends on rest only
through eps_x). The second factor was incorrectly using
`edge_edge_mollifier_gradient(positions, eps_x)` (which returns
∇_position m via cross_squarednorm chain) instead of
`edge_edge_mollifier_threshold_gradient(rest)` (which returns ∇_rest ε).
The sister function `edge_edge_mollifier_gradient_jacobian_wrt_x`
already used the correct factor; this restores consistency between
the two.

Adds a regression test under tests/src/tests/potential/ that
constructs a 4-vertex/2-edge scene with a single mollified EE pair and
FD-checks the full 12x12 analytic shape_derivative against centered FD
of gradient at fixed collision set (same convention as
test_barrier_potential.cpp:377-391). Without the fix, the test shows
rel ≈ 1.4-2.6e-2 across the three z_twist values; with the fix,
rel ≈ 3-5e-4. Threshold is 5e-3.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Huangzizhou Huangzizhou marked this pull request as draft June 6, 2026 21:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant