Fixed MJC shape mapping#514
Conversation
Signed-off-by: Milad Rakhsha <mrakhsha@nvidia.com>
📝 WalkthroughWalkthroughThe changes update the MuJoCoWarp solver to support multiple environments by making shape-to-geom index mappings environment-aware. This involves converting relevant arrays and dictionary mappings from 1D to 2D, indexed by both environment and geometry indices, and updating kernel argument types and test code to reflect the new structure. Changes
Sequence Diagram(s)sequenceDiagram
participant Test as Test Case
participant Solver as MuJoCoSolver
participant Kernel as update_geom_properties_kernel
Test->>Solver: convert_to_mjc(model, ...)
Solver->>Solver: Build full_shape_mapping with (env_idx, geom_idx)
Solver->>Solver: Initialize to_newton_shape_array[env_idx, geom_idx]
Solver->>Solver: Set model.to_newton_shape_index as 2D array
Solver->>Kernel: Call update_geom_properties_kernel(..., to_newton_shape_index)
Kernel->>Kernel: Access to_newton_shape_index[worldid, geom_idx]
Note right of Kernel: Properties updated per environment
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~15 minutes Possibly related PRs
Suggested reviewers
Note 🔌 MCP (Model Context Protocol) integration is now available in Early Access!Pro users can now connect to remote MCP servers under the Integrations page to get reviews and chat conversations that understand additional development context. ✨ Finishing Touches
🧪 Generate 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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
Documentation and Community
|
There was a problem hiding this comment.
Actionable comments posted: 2
🔭 Outside diff range comments (3)
newton/solvers/mujoco/solver_mujoco.py (1)
2530-2536: update_geom_properties: use mjw_model’s world dimension, not model.num_envsWhen envs aren’t separated, mjw_model holds nworld=1 fields. Launching kernels with dim=(model.num_envs, ...) will cause out-of-bounds writes to mjw_model arrays. Use the world dimension from mjw_model’s field shapes.
Apply this diff:
- num_geoms = self.mj_model.ngeom - num_worlds = self.model.num_envs # why is there no 'self.mjw_model.nworld'? + num_geoms = self.mj_model.ngeom + # Use the actual world dimension of the mjw model arrays. + # This is 1 when environments are not separated. + num_worlds = int(self.mjw_model.geom_pos.shape[0])newton/tests/test_mujoco_solver.py (2)
688-696: Add an invariant check to catch the original cross-env mapping bug.Right now, “expected” values are derived from the same mapping under test, which could mask regressions (e.g., all worlds mapping to env 0 shapes). Assert that, for each world, geom→shape indices differ from world 0 by a constant offset across geoms.
Apply this diff inside the loop after computing shape_idx to ensure the per-env offset is consistent (skip unmapped geoms):
for world_idx in range(self.model.num_envs): for geom_idx in range(num_geoms): - shape_idx = to_newton_shape_index[world_idx, geom_idx] + shape_idx = to_newton_shape_index[world_idx, geom_idx] + # Invariant: for replicated envs, mapping should shift by a constant per-env offset + if world_idx > 0: + base_shape_idx = to_newton_shape_index[0, geom_idx] + if base_shape_idx >= 0 and shape_idx >= 0: + if env_offsets[world_idx] is None: + env_offsets[world_idx] = int(shape_idx - base_shape_idx) + else: + self.assertEqual( + int(shape_idx - base_shape_idx), + env_offsets[world_idx], + f"Geom→shape mapping offset must be constant within world {world_idx}", + ) if shape_idx < 0: # No mapping for this geom continueAdditionally, define env_offsets before the loop:
# Place right after `tested_count = 0` env_offsets = [None] * self.model.num_envs
893-901: Repeat the invariant check in the update test to guard against regressions.Ensure we don’t silently reintroduce the “all envs map to env 0” bug when updating properties.
Apply this diff inside the loop after computing shape_idx:
for world_idx in range(self.model.num_envs): for geom_idx in range(num_geoms): - shape_idx = to_newton_shape_index[world_idx, geom_idx] + shape_idx = to_newton_shape_index[world_idx, geom_idx] + # Invariant: ensure a constant per-env offset relative to world 0 + if world_idx > 0: + base_shape_idx = to_newton_shape_index[0, geom_idx] + if base_shape_idx >= 0 and shape_idx >= 0: + if env_offsets[world_idx] is None: + env_offsets[world_idx] = int(shape_idx - base_shape_idx) + else: + self.assertEqual( + int(shape_idx - base_shape_idx), + env_offsets[world_idx], + f"Geom→shape mapping offset must be constant within world {world_idx}", + ) if shape_idx < 0: # No mapping continueAnd define env_offsets before the loop:
# Place right before the `for world_idx` loop env_offsets = [None] * self.model.num_envs
🧹 Nitpick comments (1)
newton/solvers/mujoco/solver_mujoco.py (1)
992-1009: Migration note: public field shape changeto_newton_shape_index is now 2D. Please update the migration guide and any downstream docs/usages accordingly.
I can draft a short migration snippet (old vs new indexing) for warp.sim users if helpful.
📜 Review details
Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
newton/solvers/mujoco/solver_mujoco.py(4 hunks)newton/tests/test_mujoco_solver.py(2 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: adenzler-nvidia
PR: newton-physics/newton#479
File: newton/solvers/mujoco/solver_mujoco.py:2147-2149
Timestamp: 2025-07-28T15:04:54.360Z
Learning: The ls_parallel option is specific to MuJoCo Warp and is not available in the pure MuJoCo mjOption structure. It should only be set on mjw_model.opt, not mj_model.opt.
📚 Learning: 2025-07-15T21:00:03.709Z
Learnt from: dylanturpin
PR: newton-physics/newton#394
File: newton/tests/test_ik.py:119-119
Timestamp: 2025-07-15T21:00:03.709Z
Learning: In the Newton physics engine codebase, it's acceptable to test private API functions (those prefixed with underscore) in unit and component tests, as the maintainers consider this appropriate for testing internal functionality.
Applied to files:
newton/tests/test_mujoco_solver.py
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Run GPU Benchmarks (Pull Request)
- GitHub Check: Run GPU Unit Tests on AWS EC2 (Pull Request)
- GitHub Check: run-newton-tests / newton-unittests (ubuntu-latest)
- GitHub Check: run-newton-tests / newton-unittests (windows-latest)
🔇 Additional comments (6)
newton/solvers/mujoco/solver_mujoco.py (3)
992-1009: 2D to_newton_shape_index: signature + indexing look correctSwitch to wp.array2d and (worldid, geom_idx) access is consistent with the new env-aware design. No issues spotted here.
2251-2261: Env-aware full_shape_mapping expansion is correctMapping each global Newton shape index to its (env_idx, mj_geom_idx) is the right approach when separating envs to worlds.
2262-2263: Fallback mapping when not separating envsMapping to (0, geom_idx) keeps a single-world MuJoCo model consistent. Good.
newton/tests/test_mujoco_solver.py (3)
690-693: Correct: switch to env-aware indexing for geom→shape mapping.Using to_newton_shape_index[world_idx, geom_idx] aligns tests with the new 2D mapping in the solver.
895-899: Correct: env-aware indexing for dynamic updates as well.Mirroring the conversion test, this change ensures updates validate per-env mappings.
662-669: No single-dimension indexing ofto_newton_shape_indexremains
A project-wide search (rg -n 'to_newton_shape_index\[[^]]+\]' | gawk …) returned no instances of one-dimensional subscripting without a comma. No further changes are needed.
|
I was curious about the massive perf hit (20x slowdown) to |
|
Thanks Eric yes I was also curious what is going on there |
I ran what should be the equivalent |
I couldn't reproduce it on my local machine either when running asv ( |
|
Awesome, thanks for helping! |
Signed-off-by: Milad Rakhsha <mrakhsha@nvidia.com>
Signed-off-by: Milad Rakhsha <mrakhsha@nvidia.com>
Currently all geoms in all environments of MJC map to the geoms in the first environment in Newton. This is a wrong behavior and prevents material shape randomization.
Description
Newton Migration Guide
Please ensure the migration guide for warp.sim users is up-to-date with the changes made in this MR.
docs/migration.rstis up-to dateBefore your PR is "Ready for review"
newton/tests/test_examples.py)pre-commit run -aSummary by CodeRabbit
New Features
Bug Fixes