[FEATURE] Add analytical sphere-sphere narrow-phase contact#2963
[FEATURE] Add analytical sphere-sphere narrow-phase contact#2963Kashu7100 wants to merge 2 commits into
Conversation
Sphere-sphere pairs previously fell through to the iterative MPR
(and, on a cold cache, GJK+EPA) narrow-phase path. A sphere-sphere
contact is a closed form, so route it through a dedicated analytic
function alongside the existing sphere-capsule / sphere-box /
capsule-capsule specializations.
func_sphere_sphere_contact computes the contact directly from the two
centers and radii:
normal = (c_a - c_b) / ||c_a - c_b|| (B -> A)
penetration = r_a + r_b - ||c_a - c_b||
contact = c_a - (r_a - penetration/2) * normal
with an arbitrary-direction fallback for coincident centers.
Wired into all three narrow-phase dispatch sites (multi-contact loop,
func_narrow_phase_convex_vs_convex, and the split contact0 path).
Benchmark (8192 parallel pairs, float32 GPU, pure detection()):
- analytic: ~96 us/call, penetration error ~7e-9 (float32 eps),
exact normal and contact point
- MPR baseline: ~12408 us/call, penetration error ~6e-6, normal
~0.5 deg off-axis
=> ~128x faster and more accurate for sphere-sphere.
The closed form is also exactly differentiable, unlike the EPA path
which fails to converge on the smoothly-curved sphere-sphere Minkowski
boundary.
Adds test_sphere_sphere_analytical_accuracy.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 94a77bab8c
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| geoms_info, | ||
| rigid_global_info, | ||
| ) | ||
| elif geoms_info.type[i_ga] == gs.GEOM_TYPE.SPHERE and geoms_info.type[i_gb] == gs.GEOM_TYPE.SPHERE: |
There was a problem hiding this comment.
Allow sphere-sphere through the requires_grad guard
This new analytical dispatch still cannot run in differentiable scenes: collider setup rejects any smooth/smooth pair when requires_grad=True, and the existing guard in genesis/engine/solvers/rigid/collider/collider.py lines 548-555 explicitly raises for sphere-sphere before narrowphase is reached. As a result, the added closed-form sphere-sphere path remains unusable for the differentiable case it is meant to unblock; the guard/specialized mask needs to exempt sphere-sphere pairs handled analytically.
Useful? React with 👍 / 👎.
|
Nice that you updated tests/test_rigid_physics_analytical_vs_gjk.py 🙌 Skimming the PR, looks plausible to me. (skimmed very quickly, like 30 seconds, to be clear). |
|
🔴 Benchmark Regression Detected ➡️ Report |
Addresses Codex review on Genesis-Embodied-AI#2963: the analytic sphere-sphere path was unreachable under requires_grad=True because the collider guard rejected all smooth/smooth pairs before narrowphase, so the closed-form path could not unblock the differentiable case it was meant to. Make sphere-sphere fully differentiable: - collider guard: exempt sphere-sphere (still rejects sphere-ellipsoid and ellipsoid-ellipsoid, which have no analytic path and tunnel through diff_gjk's EPA). - backward: func_differentiable_sphere_contact reconstructs the contact in closed form from the geom centres and radii, bypassing the Minkowski- triangle reconstruction (degenerate for two smoothly-curved surfaces). - backward kernel: both contact loops dispatch sphere-sphere pairs to the analytic reconstruction. - forward: func_add_diff_contact_input_sphere registers a self-referential diff-contact-input entry so the backward pass processes the analytic contact. Gradients w.r.t. geom positions match finite differences to ~1e-11 (64-bit). Tests: test_diff_smooth_pair_raises now covers a sphere-ellipsoid pair (still unsupported); new test_diff_sphere_sphere_contact checks the exact forward contact and FD-validates the analytic gradients. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Addressed the Codex P2 in 38c9a78 — sphere-sphere is now fully supported under Codex correctly noted the guard made the analytic path unreachable. Just relaxing the guard wasn't enough, though: the differentiable backward (
Gradients w.r.t. geom positions match finite differences to ~1e-11 (64-bit). @codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 38c9a78b11
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| radius_b = geoms_info.data[i_gb][0] | ||
| delta = geoms_state.pos[i_ga, i_b] - geoms_state.pos[i_gb, i_b] | ||
| dist = delta.norm() | ||
| contact_normal = delta / dist |
There was a problem hiding this comment.
Guard the sphere-sphere backward at coincident centers
When two differentiable spheres have the same center (or are close enough that the forward path uses its dist <= EPS fallback), this backward reconstruction still computes delta / dist with dist == 0, so collider.backward() produces NaN contact normals/gradients even though func_sphere_sphere_contact generated a finite contact using an arbitrary normal. This leaves an initialization/state that the new guard now allows under requires_grad=True but cannot actually differentiate safely.
Useful? React with 👍 / 👎.
|
that's fair. but the speed ups are real. |

Summary
Sphere-sphere collision pairs currently fall through to the iterative MPR narrow-phase path (and, on a cold contact cache, GJK+EPA as well). A sphere-sphere contact is a closed form, so this PR routes it through a dedicated analytic function — mirroring the existing
sphere-capsule,sphere-box, andcapsule-capsulespecializations.func_sphere_sphere_contact(incollider/capsule_contact.py) computes the contact directly from the two centers and radii:with an arbitrary-direction fallback for coincident centers. It is wired into all three narrow-phase dispatch sites (the multi-contact perturbation loop,
func_narrow_phase_convex_vs_convex, and the splitcontact0path).Motivation
The closed form is also exactly differentiable, unlike the EPA path, which fails to converge on the smoothly-curved sphere-sphere Minkowski boundary (see the
requires_gradguard incollider.py).Benchmark
Two overlapping spheres (r=0.10 / r=0.15, centers 0.18 apart → exact penetration 0.07), 8192 parallel envs, float32 GPU, timing pure
collider.detection()with pinned positions (500 calls after warmup). MPR baseline measured by reverting only the dispatch branch in the same build.detection()call(-1,0,0)→ ~128× faster on the full detection workload and more accurate.
Test
Adds
test_sphere_sphere_analytical_accuracytotests/test_rigid_physics_analytical_vs_gjk.py(passes).🤖 Generated with Claude Code