Skip to content

Preserve MJCF collision filtering semantics - #3714

Merged
eric-heiden merged 12 commits into
newton-physics:mainfrom
eric-heiden:eric/mjcf-mask-compiler-prototype
Aug 4, 2026
Merged

Preserve MJCF collision filtering semantics#3714
eric-heiden merged 12 commits into
newton-physics:mainfrom
eric-heiden:eric/mjcf-mask-compiler-prototype

Conversation

@eric-heiden

@eric-heiden eric-heiden commented Jul 29, 2026

Copy link
Copy Markdown
Member

Description

Closes #3014. Tracks the automatic-mask and round-trip work in #3713.

MuJoCo accepts an automatically generated geom pair when:

(contype_a & conaffinity_b) != 0 or (contype_b & conaffinity_a) != 0

Newton's signed collision groups cannot express every such asymmetric relation directly. This change preserves the exact pair relation without adding public collision_type / collision_affinity arrays or changing broad-phase kernels:

  • MJCF import stores each geom's effective 32-bit masks in internal model.mujoco.contype / model.mujoco.conaffinity custom attributes, including inherited defaults.
  • A bounded optimizer compiles the mask relation into existing signed Newton groups plus exact sparse exclusions. Import uses at most one positive group to retain the prior interaction with Newton-authored shapes, and assigns unique negative group IDs across separate imports.
  • SolverMuJoCo(use_mujoco_contacts=True) forwards preserved source masks verbatim when every selected collision shape has them and all Newton pair filters are already enforced by those masks, same-body topology, or body-wide MuJoCo <exclude> elements. Otherwise it reverse-compiles the combined Newton graph so later partial pair-filter edits remain effective. Preserved masks remain authoritative over later collision-group edits.
  • Native Newton models use the reverse conversion: an exact, pair-matrix-verified biclique cover of their collision groups and pair filters. A successful result cannot add or remove a pair. Any graph with at most 33 selected shapes is guaranteed to fit; larger structured graphs commonly fit as well.
  • If a native graph does not fit 32 bits, the solver uses the established legacy graph-color fallback. For example, 33 disconnected collision edges provably require 33 bits.
  • Reverse compilation is bounded at 256 selected shapes or 1,024 sparse exclusions. Larger workloads skip directly to the established graph-color fallback without allocating the dense reverse-cover matrix.
  • Replicated worlds with group-only filtering now reuse one contact-pair template even when they have no sparse filter blocks.

Each MuJoCo bit is an oriented biclique: geoms carrying the bit in contype form one side and geoms carrying it in conaffinity form the other. Minimum biclique cover is NP-hard, so the larger-graph compiler uses safe group-derived bicliques and per-shape stars, accepts only a fully verified result, and treats an uncovered edge as capacity failure rather than silently approximating it.

MuJoCo references: collision selection, contype / conaffinity XML fields.

Menagerie analysis

Benchmark corpus: mujoco_menagerie@71f066a.

  • Scanned all 259 XML files.
  • MuJoCo compiled 233 standalone models; all 233 forward pair matrices were verified exactly.
  • The other 26 files are non-standalone include fragments (missing referenced bodies, sites, defaults, or keyframe state).
  • All 67 top-level asset directories have a compilable representative.
  • 35 standalone XML models use masks beyond only 0/0 and 1/1; these occur in 11 representative asset directories.

Representative complex patterns:

Model Geoms (active) Effective mask counts Group-1 exclusions Compiled exclusions Reverse bits
Agility Cassie 45 (20) 0/0×25, 0/15×1, 1/0×5, 2/4×7, 4/2×7 83 35 8
IIT SoftFoot 197 (51) 0/0×146, 1/1×1, 1/15×1, 1/2×49 1,126 0 1
PAL Talos 105 (52) 0/0×53, 1/1×50, 6/1×2 1 0 2
Tetheria Aero Hand 94 (30) 0/0×64, 1/1×29, 2/2×1 16 16 28

Across all 233 compilable XMLs:

  • Naive group-1 lowering: 3,677 mask exclusions.
  • Compiled lowering: 86 exclusions, a 97.66% reduction.
  • Forward compile time: 33.9 μs median, 1.129 ms maximum (3 repetitions/model).
  • Reverse compile: exact for 233/233 compiler-produced Newton graphs, with no capacity failures and at most 28 bits.
  • Reverse compile time: 0.940 ms median, 6.171 ms maximum (3 repetitions/model).

For a 1,024-world synthetic replication of SoftFoot's 51 active geom masks (median of 5 CPU runs):

Representation Replicate Finalize Stored filters
Group 1 + exclusions 292.9 ms 1,328.8 ms 1,204,224
2 compiled groups 299.4 ms 1,287.2 ms 0

This removes about 9.19 MiB of packed filter-pair storage, improves finalization by 3.1%, and improves combined replication/finalization by 2.2%. Preserved masks add two int64 custom-attribute values, or 16 bytes per imported shape.

Related work

  • #3104 adds first-class public masks and applies them in every broad phase. This PR is the no-new-core-API alternative: compile masks into current groups/exclusions and preserve raw values only in the MuJoCo namespace.
  • #3283 and #3285 landed geom visualization-group and explicit-pair geom preservation.
  • #3645, #3662, and #3671 cover explicit-pair parameters, selection, and default inheritance. Explicit <pair> contacts remain separate from automatic mask filtering here.

Checklist

  • New or existing tests cover these changes
  • The documentation is up to date with these changes
  • CHANGELOG.md has been updated

Test plan

uv run --extra dev --with mujoco -m unittest newton.tests.test_import_mjcf
# 240 passed

uv run --extra dev -m unittest newton.tests.test_collision_mask_compiler
# 21 passed

uv run --extra dev -m unittest newton.tests.test_builder_replicate
# 19 passed

uv run --extra dev --with mujoco -m unittest newton.tests.test_mujoco_solver.TestMuJoCoSolverCollisionMasks
# 2 passed

uv run --extra dev -m unittest newton.tests.test_model.TestModelMesh
# 48 passed

uvx pre-commit run -a
# all passed

Bug fix

Steps to reproduce:

  1. Import an MJCF floor with contype=0 conaffinity=1.
  2. Add two spheres with contype=1 conaffinity=0.
  3. Finalize the Newton model.
  4. Before this change, all three shapes shared a Newton collision group, so the two spheres incorrectly collided with each other. With this change, both floor/sphere pairs remain and the sphere/sphere pair is absent.

Summary by CodeRabbit

  • New Features

    • Preserved MJCF contype/conaffinity collision filtering during import and MuJoCo contact generation.
    • Added exact translation between MuJoCo masks and Newton collision groups, including sparse exclusions.
    • Improved collision-filter handling for native Newton models, with capacity-aware fallback behavior.
  • Bug Fixes

    • Corrected collision filtering when combining preserved MuJoCo masks with Newton pair filters.
    • Improved contact-pair reuse for replicated worlds without filters.
  • Documentation

    • Added detailed guidance on collision filtering, mask precedence, capacity limits, and fallback behavior.

Compile MuJoCo collision masks into Newton groups and sparse exclusions while retaining the authored masks for exact MuJoCo round trips. Compile native Newton filtering back into verified 32-bit MuJoCo masks and warn before the legacy fallback when the graph exceeds capacity.
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

MJCF collision masks are preserved during import, compiled into Newton groups and exclusions, and forwarded or converted for MuJoCo contacts. Newton collision graphs now support exact mask compilation with bounded fallbacks. Replicated contact-pair template reuse also gains stricter validation.

Changes

Collision filtering integration

Layer / File(s) Summary
Collision mask compilers
newton/_src/solvers/mujoco/collision_masks.py, newton/tests/test_collision_mask_compiler.py
Adds MuJoCo-to-Newton and Newton-to-MuJoCo compilation, verification, result contracts, capacity handling, exclusions, and validation tests.
MJCF mask preservation
newton/_src/utils/import_mjcf.py, newton/tests/test_import_mjcf.py
Registers and stores imported contype/conaffinity, compiles them into Newton groups with sparse exclusions, and tests asymmetric masks and subsequent Newton filters.
Solver mask conversion
newton/_src/solvers/mujoco/solver_mujoco.py, newton/tests/test_mujoco_solver.py, docs/solvers/mujoco.rst, CHANGELOG.md
Selects preserved, compiled, or legacy collision encoding paths and documents exactness, precedence, and fallback behavior.
Replicated contact-pair validation
newton/_src/sim/builder.py, newton/tests/test_builder_replicate.py
Validates world segmentation and replicated filter-block ranges before reusing contact-pair templates, with coverage for unfiltered replicated worlds.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant MJCF
  participant ModelBuilder
  participant SolverMuJoCo
  participant MuJoCo
  MJCF->>ModelBuilder: contype/conaffinity values
  ModelBuilder->>ModelBuilder: compile groups and exclusions
  ModelBuilder->>SolverMuJoCo: preserved masks and Newton filters
  SolverMuJoCo->>MuJoCo: geom contype/conaffinity
Loading

Possibly related issues

  • Issue 3713: Implements the automatic MuJoCo collision-filtering behavior described by the imported mask preservation and solver conversion changes.

Possibly related PRs

Suggested reviewers: nvtw, vreutskyy

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The MJCF import path now preserves contype/conaffinity semantics, and tests cover the floor/two-sphere reproduction.
Out of Scope Changes check ✅ Passed The added solver, docs, and tests align with the stated collision-filtering and MuJoCo round-trip objectives.
Docstring Coverage ✅ Passed Docstring coverage is 92.50% which is sufficient. The required threshold is 80.00%.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: preserving MJCF collision filtering semantics across import/export and solver paths.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@newton/_src/solvers/mujoco/collision_masks.py`:
- Around line 751-790: Guard the greedy biclique-cover path around the candidate
construction and selection logic in the collision-mask compiler when shape_count
exceeds a practical threshold, avoiding dense star-candidate generation and
repeated rescans for large scenes. Preserve the existing exact-cover behavior
for smaller graphs and ensure the caller uses the established legacy fallback
when the threshold is exceeded.

In `@newton/_src/solvers/mujoco/solver_mujoco.py`:
- Around line 5378-5408: Update the preserved-mask decision in the solver flow
around use_preserved_collision_masks so it also verifies every
shape_collision_filter_pairs entry involving colliding_shapes is already blocked
by the imported contype/conaffinity masks or covered by an installed body-level
exclusion. If any remaining shape-level pair is not represented by those masks,
set use_preserved_collision_masks false so execution falls through to
_compile_newton_collision_masks; retain the existing preserved path only when
all filtered pairs are safely enforced.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro Plus

Run ID: 917ddb2f-29e4-45ab-a964-b5589d36d072

📥 Commits

Reviewing files that changed from the base of the PR and between 92ae945 and 209fff6.

📒 Files selected for processing (11)
  • CHANGELOG.md
  • docs/solvers/mujoco.rst
  • newton/_src/sim/builder.py
  • newton/_src/solvers/mujoco/collision_masks.py
  • newton/_src/solvers/mujoco/solver_mujoco.py
  • newton/_src/utils/import_mjcf.py
  • newton/tests/test_builder_replicate.py
  • newton/tests/test_collision_mask_compiler.py
  • newton/tests/test_import_mjcf.py
  • newton/tests/test_mujoco_solver.py
  • scripts/benchmark_collision_mask_compiler.py

Comment thread newton/_src/solvers/mujoco/collision_masks.py
Comment thread newton/_src/solvers/mujoco/solver_mujoco.py
@codecov

codecov Bot commented Jul 29, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.27027% with 28 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
newton/_src/solvers/mujoco/collision_masks.py 94.90% 23 Missing ⚠️
newton/_src/sim/builder.py 84.00% 4 Missing ⚠️
newton/_src/utils/import_mjcf.py 97.72% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Honor Newton pair filters that are not already represented by preserved MJCF masks or body exclusions. Bound reverse-cover work for large and heavily filtered graphs so established graph coloring remains the practical fallback.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@newton/_src/solvers/mujoco/collision_masks.py`:
- Around line 669-674: Update the validation for max_bits, max_shape_count, and
max_excluded_pair_count to reject NaN and any non-integral values before
applying their existing range checks. Preserve the current allowed bounds and
None handling, and ensure all three capacity parameters accept only integral
values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro Plus

Run ID: 3b462aaf-cf0c-4a8d-8faa-b15a977cbddd

📥 Commits

Reviewing files that changed from the base of the PR and between 209fff6 and 3fdcda4.

📒 Files selected for processing (6)
  • CHANGELOG.md
  • docs/solvers/mujoco.rst
  • newton/_src/solvers/mujoco/collision_masks.py
  • newton/_src/solvers/mujoco/solver_mujoco.py
  • newton/tests/test_collision_mask_compiler.py
  • newton/tests/test_import_mjcf.py
🚧 Files skipped from review as they are similar to previous changes (5)
  • CHANGELOG.md
  • newton/tests/test_collision_mask_compiler.py
  • docs/solvers/mujoco.rst
  • newton/tests/test_import_mjcf.py
  • newton/_src/solvers/mujoco/solver_mujoco.py

Comment thread newton/_src/solvers/mujoco/collision_masks.py
Use the established graph-color fallback without a new advisory when an exact MuJoCo mask cover is unavailable. Strict-warning test runs include supported models that legitimately take this path.
@eric-heiden
eric-heiden marked this pull request as ready for review July 29, 2026 14:57
@eric-heiden
eric-heiden requested a review from a team as a code owner July 29, 2026 14:57
@eric-heiden
eric-heiden requested review from nvtw and vreutskyy July 29, 2026 15:57
Declare preserved masks alongside the other MuJoCo geom attributes and use the unified registration path for direct MJCF parsing. Drop the exploratory collision-mask benchmark script from the PR.

@vreutskyy vreutskyy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple of questions from my review:

Comment thread newton/_src/solvers/mujoco/solver_mujoco.py
Comment thread newton/_src/solvers/mujoco/solver_mujoco.py Outdated
Track independent MJCF imports as separate mask domains so
SolverMuJoCo reverse-compiles their combined collision graph.

Map sparse filter pairs directly and skip oversized graphs before
allocating quadratic candidate arrays.
@eric-heiden
eric-heiden requested a review from vreutskyy August 4, 2026 06:46

@vreutskyy vreutskyy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One finding from the follow-up review:

Comment thread newton/_src/solvers/mujoco/solver_mujoco.py
vreutskyy
vreutskyy previously approved these changes Aug 4, 2026

@vreutskyy vreutskyy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. Thanks for addressing the review findings.

@eric-heiden
eric-heiden requested a review from vreutskyy August 4, 2026 18:55
@eric-heiden

Copy link
Copy Markdown
Member Author

Just fixed a performance regression in the ModelBuilder, should be good to go now.

@vreutskyy vreutskyy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. Re-reviewed the mask-domain remapping optimization and latest main merge.

@eric-heiden
eric-heiden added this pull request to the merge queue Aug 4, 2026
Merged via the queue into newton-physics:main with commit 4d64341 Aug 4, 2026
30 checks passed
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.

[BUG] contype/conaffinity collision filtering not working

2 participants