Skip to content

[Fix] Make --deterministic reproduce training runs by configuring the physics backend - #7334

Merged
kellyguo11 merged 8 commits into
isaac-sim:developfrom
hujc7:jichuanh/wire-deterministic-physics
Sep 3, 2026
Merged

[Fix] Make --deterministic reproduce training runs by configuring the physics backend#7334
kellyguo11 merged 8 commits into
isaac-sim:developfrom
hujc7:jichuanh/wire-deterministic-physics

Conversation

@hujc7

@hujc7 hujc7 commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator

Description

--deterministic configured PyTorch and the Isaac RTX renderer but never reached the physics solver, so training on Newton backends was not reproducible even with the flag passed. Two defaults moved out from under the flag between 3.0beta2 and GA:

Default v3.0.0-beta2 GA
CartpolePhysicsCfg.default PhysxCfg() NewtonCfg MJWarp, deterministic_mode="not_guaranteed"
MultiBackendRendererCfg.default IsaacRtxRendererCfg() NewtonWarpRendererCfg()

The physics switch landed in 0caae64dc7c (#7066), absent from all three v3.0.0-beta* tags. At beta2 the flag worked because PhysX is run-to-run deterministic for rigid bodies and Isaac RTX was the default renderer; both premises were removed without re-wiring the flag.

--deterministic now sets PhysicsCfg.deterministic on the resolved physics config, in apply_env_overrides() — the existing CLI-to-cfg seam, after scan() resolves the backend and before the solver is built. That field is the backend-agnostic request; each physics manager translates it when the simulation starts:

  • Newton derives deterministic_mode="run_to_run", applies the MJWarp disable_sensors prerequisite on the GPU path, and leaves MuJoCo-CPU alone. An explicitly set deterministic_mode wins.
  • PhysX / OvPhysX enable enable_enhanced_determinism. OvPhysX is best-effort and not verified end to end.

Validation stays with the backend: NewtonManager._validate_deterministic_solver_cfg() rejects an unsupported solver at solver initialization, so there is one policy and one set of error messages rather than a copy in the RL layer. Adding a backend no longer means editing isaaclab_rl.

A determinism request that would starve a sensor is now refused. Disabling MuJoCo Warp's sensors also skips its rne_postconstraint stage, which fills Newton's body_qdd / body_parent_f. The IMU, PVA and joint-wrench sensors read that state, so Isaac-Ant, Isaac-Humanoid and Isaac-Repose-Cube-Shadow — all defaulting to newton_mjwarp and feeding joint_wrench into their policy observations — would have trained on values that are never refreshed, with no error. NewtonManager raises at solver initialization, the only point where both the solver config and the registered sensors are visible.

The guard restates {"body_qdd", "body_parent_f"}, which Newton also states internally (solver_mujoco.py:4360 as a set, :5179 as an equivalent or-chain). That duplication is tracked upstream in newton-physics/newton#4109, which asks for two things: Newton refusing the combination at the source, and exporting the field set as a public constant. Either lets this guard shrink — the constant and its sensor-tracking delete entirely once Isaac Lab pins a Newton that raises. Until then the guard is what prevents the silent case, so it stays.

Fixes NVBug 6658578 (P0, Isaac Lab 3.0 GA).

Type of change

  • Bug fix (non-breaking change which fixes an issue)

Validation

Three --deterministic runs of Isaac-Cartpole-Camera (50 epochs, task defaults) on one L40, each in its own container:

det1 vs det2   55/55 checkpoint tensors bitwise identical
det1 vs det3   55/55 checkpoint tensors bitwise identical
ctrl1 vs ctrl2 41/55 differ          <- same task, no flag

The unflagged controls diverge, so the agreement above is the flag's doing rather than a task that is trivially reproducible. Isaac-Ant --deterministic fails at startup with the sensor message. MuJoCo-CPU could not be exercised: SolverMuJoCo.get_max_contact_count() raises NotImplementedError on that path, so it is unreachable in Isaac Lab today.

8 unit tests added; 200 pass across test_entrypoints_common.py and test_newton_manager_abstraction.py.

Release backport

  • Backport this pull request to the active release branch after it merges into develop

Checklist

  • I have read and understood the contribution guidelines
  • I have run the pre-commit checks with ./isaaclab.sh --format
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • I have added a changelog fragment under source/<pkg>/changelog.d/ for every touched package
  • I have added my name to the CONTRIBUTORS.md or my name already exists there

The flag configured PyTorch and the Isaac RTX renderer but never reached the
physics solver. Two defaults moved out from under it between 3.0beta2 and GA:
classic core tasks switched to Newton (isaac-sim#7066) and the default renderer to
Newton+Warp, so on Isaac-Cartpole-Camera the flag became a no-op for physics
while NewtonCfg.deterministic_mode stayed "not_guaranteed". Training reward
curves therefore diverged run to run even with --deterministic passed.

Translate the flag onto the resolved physics config in apply_env_overrides,
the existing CLI-to-cfg seam, which runs after scan() has resolved the backend
to a concrete config and before the solver is constructed. Dispatch on the
config type name so this module still imports without the optional backend
packages installed. Backends that cannot honour the guarantee now fail at
config time instead of deep inside launch.
@hujc7
hujc7 requested a review from a team August 25, 2026 07:31
@github-actions github-actions Bot added documentation Improvements or additions to documentation isaac-lab Related to Isaac Lab team labels Aug 25, 2026
@greptile-apps

greptile-apps Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR extends the training --deterministic flag to configure resolved Newton and PhysX backends and adds documentation and determinism coverage.

  • Enables enhanced determinism for both PhysX configurations.
  • Selects Newton run-to-run determinism and applies the MJWarp sensor prerequisite.
  • Rejects Newton solver configurations that cannot provide the requested guarantee.
  • Adds unit and environment-level tests for the new configuration path.

Confidence Score: 3/5

The PR should not merge until supported Newton solver subclasses stop being rejected and the documented explicit determinism opt-out is either honored or corrected.

The new configuration seam breaks valid subclass-based Newton configurations at startup and silently overwrites an explicit not_guaranteed mode despite documenting that setting as an opt-out.

Files Needing Attention: source/isaaclab_rl/isaaclab_rl/entrypoints/common.py, source/isaaclab_rl/test/test_entrypoints_common.py, source/isaaclab_rl/changelog.d/deterministic-flag-wires-physics.rst

Important Files Changed

Filename Overview
source/isaaclab_rl/isaaclab_rl/entrypoints/common.py Adds physics determinism dispatch, but rejects supported solver subclasses and cannot honor the documented explicit not_guaranteed opt-out.
source/isaaclab_rl/test/test_entrypoints_common.py Covers standard Newton and PhysX paths but omits supported solver subclasses and an explicit not_guaranteed override.
source/isaaclab_tasks/test/core/test_environment_determinism.py Adds an end-to-end camera-task determinism check using the settings selected by the flag.
docs/source/features/reproducibility.rst Documents how the flag configures rendering and supported physics backends.
source/isaaclab_rl/changelog.d/deterministic-flag-wires-physics.rst Describes the new behavior but promises an explicit opt-out that the implementation cannot preserve.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
    CLI[--deterministic] --> Scan[Resolve physics backend]
    Scan --> Override[apply_env_overrides]
    Override --> PhysX[Enable enhanced determinism]
    Override --> Newton{Newton solver supported?}
    Newton -->|Yes| Mode[Select run_to_run mode]
    Newton -->|MJWarp| Sensors[Disable internal MJWarp sensors]
    Newton -->|No| Error[Raise configuration error]
    PhysX --> Env[Construct environment]
    Mode --> Env
    Sensors --> Env
Loading

Reviews (1): Last reviewed commit: "Wire --deterministic into the resolved p..." | Re-trigger Greptile

Comment on lines +499 to +504
solver_name = type(solver_cfg).__name__
if solver_name not in _DETERMINISTIC_NEWTON_SOLVERS:
raise ValueError(
f"--deterministic is not supported by {solver_name}. Use MJWarp on the GPU, XPBD, or"
" Featherstone, or drop --deterministic."
)

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.

P1 Subclass support is rejected

When a Newton configuration uses a subclass of MJWarpSolverCfg, XPBDSolverCfg, or FeatherstoneSolverCfg, this exact-name check rejects it, causing training with --deterministic to abort even though Newton's runtime accepts supported solver subclasses through isinstance.

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.

Agreed. More fundamentally, this validation duplicates NewtonManager._validate_deterministic_solver_cfg(), which already uses isinstance and owns the backend-specific error messages. Please remove the solver whitelist/rejection logic from the RL layer: set the requested mode on the resolved config and let NewtonManager validate it during solver initialization. That preserves supported subclasses and prevents the two validation policies from drifting.

Comment on lines +516 to +517
if physics_cfg.deterministic_mode == "not_guaranteed":
physics_cfg.deterministic_mode = "run_to_run"

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.

P1 Explicit opt-out is overwritten

When a user explicitly sets deterministic_mode="not_guaranteed" with --deterministic, this value-only check cannot distinguish the override from the default and replaces it with run_to_run, silently ignoring the documented opt-out and imposing deterministic-physics overhead.

@isaaclab-review-bot isaaclab-review-bot 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.

Isaac Lab Review Bot

The physics override is correctly placed in the shared RL entrypoint path and is covered by focused tests, but the user-facing documentation overstates the scope of --deterministic, and the changelog gives opt-out guidance that the implementation does not support.

  • Design and architecture: Applying the override after physics configuration resolution and before solver construction is appropriate for the RL training entrypoints and avoids optional backend imports. However, this behavior belongs to isaaclab_rl.entrypoints.common.apply_env_overrides, not AppLauncher itself, so the reproducibility documentation must scope the physics behavior to those RL entrypoints rather than presenting it as universal app-level flag behavior.
  • API: The CLI surface remains unchanged, but its documented contract needs correction. Standalone AppLauncher consumers do not receive the physics override, and explicitly setting deterministic_mode="not_guaranteed" does not opt out because the override replaces that value with "run_to_run"; dropping --deterministic is the implemented opt-out.
  • Implementation: The Newton and PhysX mutations are applied at the intended configuration seam, with stronger Newton determinism modes preserved and the MJWarp sensor prerequisite enforced. The implementation is internally consistent, but the documentation and release guidance must accurately describe its entrypoint scope and opt-out behavior.

Minor fixes needed. Posted 2 actionable findings inline.

Automated review; human maintainers own approval decisions.

mode) needs it for reproducible imagery; **Newton** rendering is already deterministic.

Pass ``--deterministic`` to enable reproducible rendering from the app launcher. (Isaac RTX only)
**Physics determinism** comes from the same flag. It sets

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.

🔵 Suggestion · Design Architecture — Docs overstate scope of physics determinism

The section introduces --deterministic as an AppLauncher flag, then claims physics determinism "comes from the same flag" and that unsupported backends raise. That holds only for code paths going through isaaclab_rl.entrypoints.common.apply_env_overrides; standalone scripts that pass the flag to AppLauncher still get no physics configuration and no error. Scope this paragraph to the RL training entrypoints.

* Changed ``--deterministic`` to also configure the resolved physics backend: Newton backends now use
``deterministic_mode="run_to_run"`` (MJWarp additionally with ``disable_sensors=True``, which that
mode requires), and PhysX backends ``enable_enhanced_determinism=True``. Deterministic physics costs
runtime and memory, so drop the flag or set ``deterministic_mode`` explicitly to opt out. Passing

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.

🔵 Suggestion · Api — Opt-out migration guidance is inaccurate

The note tells users to "set deterministic_mode explicitly to opt out", but an explicit "not_guaranteed" is indistinguishable from the default at common.py:516 and is replaced with "run_to_run", and MJWarp disable_sensors is forced regardless of mode. Only dropping the flag opts out; reword the guidance to match the implemented behavior.

)
if getattr(solver_cfg, "use_mujoco_cpu", False):
raise ValueError(
"--deterministic is not supported by the MuJoCo CPU backend. Set"

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.

is CPU actually not deterministic? I would have thought CPU could be deterministic by default

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.

Confirmed: the pure MuJoCo CPU pipeline is documented as entirely deterministic and reproducible: https://mujoco.readthedocs.io/en/stable/computation/index.html#reproducibility. use_mujoco_cpu=True therefore should not be rejected merely because --deterministic was passed. I suggest leaving NewtonCfg.deterministic_mode unchanged for that path while still applying the Torch/rendering parts of the flag. The current Newton validation only establishes that Warp’s DeterministicMode guarantee is not applicable to the CPU solver; it is not evidence that CPU MuJoCo is nondeterministic.

if solver_name == "MJWarpSolverCfg":
# MJWarp's internal sensor kernels mix atomic reduction families, which Warp's
# deterministic code generation rejects. Isaac Lab sensors read Newton state directly.
solver_cfg.disable_sensors = True

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.

would this cause any issues in tasks?

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.

For maintained Isaac Lab tasks this should be safe: disable_sensors disables MuJoCo Warp’s internal sensor computation, while Isaac Lab sensors read Newton state directly. It can still change behavior for custom/direct integrations that consume native MJWarp sensor outputs. Please scope this automatic mutation to the MJWarp GPU path for which the deterministic mode requires it, and document that native sensor-output limitation.

"""
class_name = type(physics_cfg).__name__
if class_name in ("PhysxCfg", "OvPhysxCfg"):
physics_cfg.enable_enhanced_determinism = True

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.

This treats OvPhysxCfg.enable_enhanced_determinism=True as if it satisfies the same reproducibility guarantee. Our OvPhysX 0.5.10 Ant tests still produced three distinct 100-iteration reward hashes with --deterministic, enhanced determinism, PXR_WORK_THREAD_LIMIT=1, and single-threaded USDRT population. Please either exclude OvPhysX from the guaranteed path, document this as best-effort, or add an end-to-end OvPhysX determinism test before claiming the guarantee.

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.

ovphysx determinism issues are addressed in ovphysx 0.5.11

@kellyguo11

Copy link
Copy Markdown
Contributor

I checked this against current develop and #6930. The core CLI-to-physics connection is still needed: #6930 added NewtonCfg.deterministic_mode but deliberately left AppLauncher --deterministic unchanged; today Newton still defaults to not_guaranteed, Cartpole resolves to Newton MJWarp by default, and apply_env_overrides() does not propagate the flag into physics. Thus the reported training command configures Torch/rendering but not Newton physics.

I support a narrower version of this PR: keep the post-preset-resolution wiring, but let each physics backend own validation, special-case pure MuJoCo CPU as already deterministic, scope the MJWarp sensor mutation/limitation precisely, and document the physics behavior as RL-entrypoint behavior rather than universal AppLauncher behavior. Also, do not promise OvPhysX bitwise reproducibility without an end-to-end test; our 0.5.10 results still diverged with all available controls enabled.

hujc7 added 3 commits August 29, 2026 22:50
Review feedback on isaac-sim#7334: the RL entrypoint duplicated the solver policy that
NewtonManager._validate_deterministic_solver_cfg already owns, using name
equality where the backend uses isinstance. The copy rejected supported solver
subclasses and gave the two policies room to drift.

Drop the solver whitelist and both raises. The entrypoint now only requests the
guarantee; NewtonManager rejects an unsupported solver at initialization and
keeps its own error messages.

Leave MuJoCo on the CPU alone: it is documented as reproducible, and Warp's
deterministic mode does not reach that path, so requesting the mode there only
tripped validation. Scope the MJWarp sensor mutation to the GPU path and
document that it also disables the rne_postconstraint stage. Scope the docs to
the RL training entrypoints and mark OvPhysX reproducibility best-effort, since
it is not verified end to end on the pinned ovphysx==0.5.10.
The RL entrypoint still chose backend-specific determinism settings by class
name: deterministic_mode and disable_sensors for Newton, enable_enhanced_
determinism for PhysX. That kept four backend field names in isaaclab_rl and
required matching class names across the MRO because the backend types cannot
be imported there.

Add PhysicsCfg.deterministic, set it from --deterministic, and let each physics
manager translate it. NewtonManager derives the mode, applies the MJWarp
prerequisite on the GPU path, and leaves MuJoCo on the CPU alone; PhysX and
OvPhysX enable enhanced determinism. Adding a backend no longer means editing
isaaclab_rl.

Reject a determinism request that would disable MuJoCo Warp's sensor stage
while a sensor needs it. Doing so also skips rne_postconstraint, which fills
body_qdd and body_parent_f; the IMU, PVA and joint-wrench sensors read that
state, so Isaac-Ant, Isaac-Humanoid and Isaac-Repose-Cube-Shadow would have
trained on values that are never refreshed, with no error. NewtonManager now
raises at solver initialization, which is the only point where both the
solver configuration and the registered sensors are visible.

Validated on 4x L40: three --deterministic runs of Isaac-Cartpole-Camera are
bitwise identical across all 55 checkpoint tensors, two unflagged runs differ,
and Isaac-Ant --deterministic fails with the sensor message.
@kellyguo11

Copy link
Copy Markdown
Contributor

run-ci

@isaaclab-bot isaaclab-bot Bot added ci:run-docker Trigger the on-demand Docker and GPU CI workflow and removed ci:run-docker Trigger the on-demand Docker and GPU CI workflow labels Aug 31, 2026
from isaaclab_newton.physics.newton_collision_cfg import NewtonCollisionPipelineCfg


_SENSOR_STAGE_STATE_ATTRIBUTES = frozenset({"body_qdd", "body_parent_f"})

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.

is there a cleaner way to handle this? or perhaps this is more of a newton issue we should raise? this and the changes below could probably have a cleaner design?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yes, after diving deeper into this, I think it's a newton side bug. Filed issue and following up to see if updates can also happen in mjwarp

@kellyguo11 kellyguo11 moved this to In progress in Isaac Lab Aug 31, 2026
…rs must be off

The error opened with three clauses of MuJoCo mechanism before saying the task
was unsupported. Lead with that instead, name only the sensors actually
implicated, and derive their names from a mapping rather than a fixed string:
a joint-wrench-only task no longer reads advice about the IMU.

Also record why disable_sensors is a prerequisite rather than a choice. MuJoCo
Warp's tactile kernel applies atomic_max and atomic_add to one output array,
and Warp's deterministic code generation cannot lower two reduction families on
a single target, so the sensor module fails to compile under a guarantee --
verified as WarpCodegenError in _sensor_tactile. The docs previously described
only the consequence, leaving a reader to think the prerequisite was optional.
@kellyguo11

Copy link
Copy Markdown
Contributor

run-ci

@isaaclab-bot isaaclab-bot Bot added ci:run-docker Trigger the on-demand Docker and GPU CI workflow and removed ci:run-docker Trigger the on-demand Docker and GPU CI workflow labels Sep 3, 2026
@kellyguo11
kellyguo11 merged commit 3748a72 into isaac-sim:develop Sep 3, 2026
46 of 48 checks passed
@github-project-automation github-project-automation Bot moved this from In progress to Done in Isaac Lab Sep 3, 2026
@isaaclab-bot

isaaclab-bot Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Backported to release/3.0.0 as eb47edd.

isaaclab-bot Bot pushed a commit that referenced this pull request Sep 3, 2026
… physics backend (#7334)

# Description

`--deterministic` configured PyTorch and the Isaac RTX renderer but
never reached the physics solver, so training on Newton backends was not
reproducible even with the flag passed. Two defaults moved out from
under the flag between 3.0beta2 and GA:

| Default | v3.0.0-beta2 | GA |
|---|---|---|
| `CartpolePhysicsCfg.default` | `PhysxCfg()` | `NewtonCfg` MJWarp,
`deterministic_mode="not_guaranteed"` |
| `MultiBackendRendererCfg.default` | `IsaacRtxRendererCfg()` |
`NewtonWarpRendererCfg()` |

The physics switch landed in `0caae64dc7c` (#7066), absent from all
three `v3.0.0-beta*` tags. At beta2 the flag worked because PhysX is
run-to-run deterministic for rigid bodies **and** Isaac RTX was the
default renderer; both premises were removed without re-wiring the flag.

`--deterministic` now sets `PhysicsCfg.deterministic` on the resolved
physics config, in `apply_env_overrides()` — the existing CLI-to-cfg
seam, after `scan()` resolves the backend and before the solver is
built. That field is the backend-agnostic request; each physics manager
translates it when the simulation starts:

- **Newton** derives `deterministic_mode="run_to_run"`, applies the
MJWarp `disable_sensors` prerequisite on the GPU path, and leaves
MuJoCo-CPU alone. An explicitly set `deterministic_mode` wins.
- **PhysX / OvPhysX** enable `enable_enhanced_determinism`. OvPhysX is
best-effort and not verified end to end.

Validation stays with the backend:
`NewtonManager._validate_deterministic_solver_cfg()` rejects an
unsupported solver at solver initialization, so there is one policy and
one set of error messages rather than a copy in the RL layer. Adding a
backend no longer means editing `isaaclab_rl`.

**A determinism request that would starve a sensor is now refused.**
Disabling MuJoCo Warp's sensors also skips its `rne_postconstraint`
stage, which fills Newton's `body_qdd` / `body_parent_f`. The IMU, PVA
and joint-wrench sensors read that state, so `Isaac-Ant`,
`Isaac-Humanoid` and `Isaac-Repose-Cube-Shadow` — all defaulting to
`newton_mjwarp` and feeding `joint_wrench` into their policy
observations — would have trained on values that are never refreshed,
with no error. `NewtonManager` raises at solver initialization, the only
point where both the solver config and the registered sensors are
visible.

The guard restates `{"body_qdd", "body_parent_f"}`, which Newton also
states internally (`solver_mujoco.py:4360` as a set, `:5179` as an
equivalent `or`-chain). That duplication is tracked upstream in
newton-physics/newton#4109, which asks for two
things: Newton refusing the combination at the source, and exporting the
field set as a public constant. Either lets this guard shrink — the
constant and its sensor-tracking delete entirely once Isaac Lab pins a
Newton that raises. Until then the guard is what prevents the silent
case, so it stays.

Fixes NVBug 6658578 (P0, Isaac Lab 3.0 GA).

## Type of change

- Bug fix (non-breaking change which fixes an issue)

## Validation

Three `--deterministic` runs of `Isaac-Cartpole-Camera` (50 epochs, task
defaults) on one L40, each in its own container:

```
det1 vs det2   55/55 checkpoint tensors bitwise identical
det1 vs det3   55/55 checkpoint tensors bitwise identical
ctrl1 vs ctrl2 41/55 differ          <- same task, no flag
```

The unflagged controls diverge, so the agreement above is the flag's
doing rather than a task that is trivially reproducible. `Isaac-Ant
--deterministic` fails at startup with the sensor message. MuJoCo-CPU
could not be exercised: `SolverMuJoCo.get_max_contact_count()` raises
`NotImplementedError` on that path, so it is unreachable in Isaac Lab
today.

8 unit tests added; 200 pass across `test_entrypoints_common.py` and
`test_newton_manager_abstraction.py`.

## Release backport

- [x] <!-- backport-active-release --> Backport this pull request to the
active release branch after it merges into `develop`

## Checklist

- [x] I have read and understood the [contribution
guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html)
- [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with
`./isaaclab.sh --format`
- [x] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] I have added a changelog fragment under
`source/<pkg>/changelog.d/` for every touched package
- [x] I have added my name to the `CONTRIBUTORS.md` or my name already
exists there

(cherry picked from commit 3748a72)
kellyguo11 pushed a commit that referenced this pull request Sep 4, 2026
# Description

`--deterministic` did not make camera observations reproducible.
Training `Isaac-Cartpole-Camera --deterministic` on an RTX PRO 6000
Blackwell splits into two bitwise-identical outcome clusters; on an L40
it does not. Reported as NVBug 6658578 against Isaac Lab 3.0 GA.

Newton's solvers accept a `deterministic` argument and apply it as a
per-module option, so PR #7334 already covered the physics kernels. Its
**sensor and geometry kernels take no such argument** and fall back to
`warp.config.deterministic`, which stayed at `NOT_GUARANTEED` — Isaac
Lab has never set it (`git log --all -S "config.deterministic"` is
empty).

The concrete path: `newton._src.geometry.bvh.compute_enabled_shapes`
claims output slots with `wp.atomic_add`, so the enabled-shape order —
and with it the primitive order the scene BVH is built over — varies
between processes. Ray queries then break ties differently, and a tiled
camera renders a few pixels differently from bit-identical simulation
state. That is enough to make an image-observation policy diverge.

`apply_env_overrides` now also requests `wp.config.deterministic =
RUN_TO_RUN`. Warp reads the setting at module build time and the BVH is
built during `ModelBuilder.finalize()`, so the request must land before
the environment is created — `_apply_deterministic_request` at solver
init is too late. An explicitly chosen mode such as `GPU_TO_GPU` is left
alone.

## Type of change

- Bug fix (non-breaking change which fixes an issue)

## Validation

Where the divergence starts, from the real training path with every
renderer input fingerprinted per frame (6 runs, Blackwell). All
geometric inputs are bit-identical at the frame where the output already
differs:

```
cam_xform / cam_rays / shape_transform / shape_body : never diverge
body_q, BVH bounds                                  : render 390  (after)
rendered colour (output)                            : render 389  (first)
```

`Isaac-Cartpole-Camera --deterministic`, epoch-25 reward, unique run
dirs, 8 runs per arm:

| Branch | Configuration | Runs | Distinct outcomes |
|---|---|---|---|
| develop | before this change | 8 | 2 |
| develop | `wp.config.deterministic` set externally | 8 | 1 |
| develop | **this change**, plain `--deterministic` | 8 | **1** |
| release/3.0.0 | before | 8 | 2 |
| release/3.0.0 | `wp.config` set | 8 | 1 |

Architecture control on L40 (4gpu), 12 runs, unique run dirs: all
identical — the defect does not reproduce on Ada, which is why PR
#7334's L40-only validation missed it.

Unit tests: 31 pass in `test_entrypoints_common.py`, including three
added here covering the request, an explicit mode being preserved, and
no change without the flag.

Fixes NVBug 6658578.

## Follow-up (not in this PR)

Newton-side: sensor and geometry modules should accept a `deterministic`
option like the solvers do, rather than depending on a process global;
and `compute_enabled_shapes` produces order-dependent output regardless
of that setting.

## Checklist

- [x] I have read and understood the [contribution
guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html)
- [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with
`./isaaclab.sh --format`
- [x] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] I have added a changelog fragment under
`source/<pkg>/changelog.d/` for every touched package
- [x] I have added my name to the `CONTRIBUTORS.md` or my name already
exists there
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation isaac-lab Related to Isaac Lab team

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants