Repository navigation
roadmap: restore Motrix as a tensor-only Manager HOST_BRIDGE backend for canonical G1 training #2054
Description
Roadmap: restore Motrix as a tensor-only Manager HOST_BRIDGE backend for canonical G1 training
Follow-up to #2045 and #2048 with the same governance and acceptance discipline. The enabled physics backend is Motrix; the reference runtime boundary is MuJoCo/mjbatch. Other shelved adapters remain out of scope.
Goal
Restore Motrix as an active tensor-only Manager-Based HOST_BRIDGE backend for exactly two canonical training workloads:
- SAC /
g1_walk_flat/ Motrix - FlashSAC /
g1_motion_tracking/ Motrix
MotrixSim remains CPU-authoritative. This roadmap does not attempt to make it DEVICE_RESIDENT. The D2H/H2D host bridge is a legitimate public boundary, but every semantic crossing must be explicit, packed, countable, and bounded.
Development and validation occur on the local workstation. Server deployment is out of scope.
Development governance (non-negotiable)
- Manager-Based API is the sole training entry point.
- Do not restore direct mode.
- Do not restore task-owned direct runtime registration.
- Do not restore the generic NumPy Manager.
- No generic NumPy owner/composer fallback.
- Do not restore the NumPy RNG owner.
- Manager-owned Torch RNG remains authoritative.
- Do not restore
env.tensor_runtime/env.tensor_runtime_device.- Placement derives from public env carriers and declared backend capabilities.
- No backend-private probing from task/env/training code.
- Consume only declared
unisim.backend.base.SimBackendAPIs/capabilities. - Add a public unisim contract first when a capability is absent.
- Consume only declared
- Motrix is a CPU-authoritative packed HOST_BRIDGE.
- No hidden NumPy state mirror outside the backend-owned host bridge.
- No hidden task-side host reset composer.
- Each control/reset/read phase owns a bounded, countable device crossing.
- Do not masquerade HOST_BRIDGE as DEVICE_RESIDENT.
- Unsupported capabilities fail closed.
- Tensor reset randomization and fixed variants remain disabled unless a public contract is added.
- No silent host fallback to satisfy an owner.
- Historical Motrix owners are reference evidence only.
- Do not restore the old reward-retuned SAC Motrix owner.
- Do not restore the task-owned FlashSAC Motrix host-bridge runtime.
- Sibling changes require their own reviewable PRs and evidence.
- Do not modify
main; do not publish PyPI artifacts. - Do not weaken the perf: end-to-end MJWarp training throughput gate and tensor-native closeout #2023 measurement contract.
run_summary.json:tail_env_steps_per_secremains decisive.- Transfer counters and benchmarks remain diagnostic-only.
Current audit
Runtime scope
Current active tensor Manager scope:
mujoco, mjwarp, genesis, newton
Motrix remains shelved:
motrix, drake, isaacgym, isaacsim, superdex
Evidence:
src/unilab/base/registry.pykeepsmotrixin_SHELVED_SIM_BACKENDS.src/unilab/cli.pykeeps Motrix out ofSUPPORTED_SIMS.register_env(..., sim_backend="motrix")currently fails closed.--sim motrixis rejected before dependency detection.
Task registration and owners
No canonical Motrix factory is registered:
G1WalkFlat:mujoco,mjwarp,genesis,newtonG1MotionTrackingSAC:mujoco,mjwarp,genesis,newton
No Motrix owner YAML exists under src/unilab/conf.
The historical owners removed in 402aed69 are not directly restorable:
- SAC Motrix retuned reward shaping and predates fused tensor reward/reset owners.
- FlashSAC Motrix was a MuJoCo-checkpoint sim2sim/eval owner, not a canonical training owner.
- An older task-owned FlashSAC host-bridge factory violates current governance.
Local runtime
After uv sync --extra motrix:
motrixsim-core 0.10.1
motrixsim installed
Focused existing Motrix backend smokes pass, including construction, stepping, state reads, body state, and quaternion convention. Existing unisim packed host-bridge adapter tests pass.
Existing public tensor lifecycle
The unisim Motrix backend already declares:
execution HOST_BRIDGE
state_views true
state_fields {qpos, qvel, ctrl}
sensor_views true
stepping true
selected_reset true
reset_randomization false
fixed_variants false
host_pre_step_control false
packed_host_bridge true
process_topology IN_PROCESS
data_plane HOST_BRIDGE
torch_devices {cpu, cuda}
Public APIs already present:
compile_host_bridge_iostep_tensorset_state_tensorget_state_viewsget_sensor_viewget_tensor_capabilities
The packed plan already owns stable host/device buffers and semantic transfer counters:
- control: one packed D2H;
- selected reset: one packed D2H;
- full state/sensor read: one packed H2D;
- post-reset selected publication: one packed H2D.
Existing adapter coverage proves selected-row preservation, bounded validation, no hidden CUDA-to-CPU detours, portable parity, and fail-closed fixed variants/randomization.
Gap 1 — Public state-width contract
Motrix does not implement get_public_state_widths():
- MuJoCo/mjbatch exposes
PublicStateWidths(nq, nv). - Motrix tensor layout currently infers widths by reading full public state.
- Canonical tensor reset must consume an explicit public width contract.
This must be repaired in unisim before owner enablement.
Gap 2 — Selected-reset publication semantics
MuJoCo/mjbatch declares:
selected_reset_publication=AUTHORITATIVE_VIEWS
Motrix cannot truthfully declare that today because:
set_state_tensorcommits selected rows through the CPU bridge;- device views become authoritative only after the plan's explicit selected-row H2D publication.
Do not add a false capability declaration. The initial owner must preserve the paired reset/read boundary. A stronger public reset transaction/readiness declaration is optional follow-up work and requires its own contract tests.
Gap 3 — Selected reset host commit is not a compiled transaction
The non-portable Motrix set_state path currently:
- constructs a full-width boolean mask;
- slices native data;
- resets defaults;
- clears forces;
- writes qvel/qpos/control through multiple Python/native calls;
- runs FK;
- refreshes caches.
The portable path uses disjoint indices but still makes multiple native calls rather than one compiled selected-row reset program.
MotrixLab provides a public reference pattern:
model.compile_write(..., reset=True, forward_kinematic=True)
program.execute(data, env_ids)
The equivalent semantics must live inside the unisim Motrix host boundary; UniLab must not call MotrixLab APIs directly.
Gap 4 — Selected read computes full-batch sources
Current selected packed reads:
- read full qpos/qvel;
- compute full tracked-body state;
- read full native sensor batches;
- select rows on the host;
- publish selected packet H2D.
This preserves semantics but can dominate motion tracking with many tracked bodies. MotrixLab's compiled query program supports selected-row execution and stable views:
query_program.execute(source, env_ids=selected_rows)
The backend should adopt the same public-shaped pattern internally without leaking MotrixLab manager architecture into UniLab.
Gap 5 — Reset randomization and fixed variants
Motrix tensor reset rejects randomization. The canonical SAC base still declares pd_gains; the owner must explicitly set it to null. Fixed variants must remain absent. Any future packed randomization payload is a separate unisim public contract, not a host fallback.
Gap 6 — Canonical owner tests and support evidence
Existing tests cover the adapter boundary, not canonical Manager training. Needed:
- owner materialization;
- unsupported reset randomization disabled explicitly;
- real construction/reset/rollout;
- selected reset/read row identity;
- fixed boundary counts;
- no task-side backend-private access;
- support matrix/docs/CI updates.
Gap 7 — Diagnostic benchmark scope
The G1 FlashSAC backend benchmark currently rejects Motrix as shelved. It should accept Motrix as diagnostic-only and report boundary timing/counters. End-to-end training remains decisive.
Reference implementation boundary
/home/user/ws/simulator/MotrixLab is useful evidence for:
- compiled reset/write programs with selected rows;
- compiled read query programs with selected rows;
- stable program-owned buffers/views;
- task-level G1 walk and WBT semantics.
It must not be imported by UniLab task/runtime code. Its direct/NumPy manager architecture is not a governance model for this roadmap.
Proposed phases
Phase 0 — Contract and boundary baseline
- Freeze the canonical Motrix D2H/H2D measurement contract.
- Record current packed control/reset/full-read/selected-read counters and timing on the canonical G1 scenes.
- Establish MuJoCo/mjbatch and Motrix interleaved diagnostic baselines.
- Create the integration branch from
develop/tensor-runtime.
Exit: every canonical requirement maps to an existing public API or a concrete unisim gap.
Phase 1 — Public unisim contract parity
Minimum changes:
- Implement Motrix
get_public_state_widths(). - Add explicit tests proving public widths match canonical tensor reset layout.
- Document the initial selected-reset publication boundary: commit + explicit selected H2D read, not
AUTHORITATIVE_VIEWS. - Strengthen fail-closed tests for randomization/fixed variants.
Exit: canonical reset composition no longer depends on backend-private state-shape inference.
Phase 2 — HOST_BRIDGE optimization
Maintain the existing path as the A/B baseline.
Then optimize only where evidence attributes cost:
- Path B — compiled selected reset: replace the multi-call selected reset with one compiled reset/write transaction when MotrixSim supports the required semantics.
- Path C — compiled selected read: refresh only selected rows and preserve stable packet views instead of computing full body/sensor sources for selected publication.
- Path D — plan ownership audit: inspect direct/reset and scene read plans for duplicate buffers or synchronization. Unify only if measured evidence shows hot-path cost or split reset state.
Acceptance for every optimization:
- selected rows update correctly;
- untouched rows remain unchanged;
- semantic D2H/H2D counts do not increase;
- no hidden CPU detour on a CUDA hot path;
- portable and non-portable parity where applicable;
- diagnostic timing/counters improve without semantic loss.
Phase 3 — UniLab canonical owner enablement
Only after Phase 1:
- Move Motrix into active registry/CLI scope.
- Add fail-closed
motrixsimdependency diagnostics. - Register only the two canonical factories through the current generic Manager factory.
- Add canonical owner YAMLs:
- SAC
g1_walk_flat/motrix.yaml - FlashSAC
g1_motion_tracking/motrix.yaml
- SAC
- SAC owner explicitly disables
pd_gains. - Both owners avoid fixed variants and unsupported randomization.
- Restore Motrix to support matrix, benchmark diagnostic scope, docs, ADR, and gated CI.
- Add real CUDA/CPU manager lifecycle tests for both canonical owners.
Exit: both canonical train commands construct, reset, train, and shut down normally through Manager-Based API only.
Phase 4 — Local performance convergence
Commands:
uv run --extra motrix train \
--algo sac \
--task g1_walk_flat \
--sim motrix \
algo.max_iterations=160 \
algo.num_envs=2048 \
training.no_play=true \
training.export_onnx=false
uv run --extra motrix train \
--algo flashsac \
--task g1_motion_tracking \
--sim motrix \
algo.max_iterations=160 \
algo.num_envs=2048 \
training.no_play=true \
training.export_onnx=falseReference:
uv run --extra mujoco train --algo sac --task g1_walk_flat --sim mujoco ...
uv run --extra mujoco train --algo flashsac --task g1_motion_tracking --sim mujoco ...Measure:
run_summary.json:tail_env_steps_per_sec
At least three interleaved runs per workload; report every run.
Initial gates, subject to the first honest Phase 4 baseline:
| Workload | Initial Motrix gate |
|---|---|
SAC / g1_walk_flat |
≥35% of MuJoCo/mjbatch tail throughput |
FlashSAC / g1_motion_tracking |
≥35% of MuJoCo/mjbatch tail throughput |
Optimization target: ≥50%. Stretch: ≥60%.
Motrix is not required to match mjbatch exactly, but acceptance requires attribution showing that the remaining gap is dominated by MotrixSim native CPU physics rather than avoidable Python, reset, selected-read, or transfer-boundary work. If the first baseline materially exceeds the initial gate, raise the floor before optimization.
Every run must:
- complete 160/160;
- report normal shutdown;
- leave no residual collector/worker process;
- produce finite rewards/observations;
- preserve observation/action dimensions;
- write
tail_iteration_count=20; - report stable boundary counts.
Phase 5 — Closeout
make checkmake testmake test-all- unisim and UniLab PRs with focused evidence;
- final interleaved local evidence table;
- support matrix/docs/CI update;
- final merge into
develop/tensor-runtime; - explicit unsupported-capability statement.
Explicit non-goals
- Full mjbatch throughput parity.
- Motrix DEVICE_RESIDENT execution.
- Tensor reset randomization parity.
- Fixed-variant support.
- Other Motrix tasks/owners.
- Server deployment.
- Playback/rendering parity beyond safe record/native validation.
- Restoring direct mode, task-owned runtimes, generic NumPy Manager, NumPy RNG, or
tensor_runtimeswitches.
Activity
TATP-233 commentedon Oct 4, 2026
Phase 0 integration branch created:
dev/issue-2054-motrix-tensor-runtimefromdevelop/tensor-runtime@90068b82.feat(motrix): publish public tensor state widths (#339)
TATP-233 commentedon Oct 4, 2026
Phase 0 complete; Phase 1 public-width contract merged.
Integration branch:
dev/issue-2054-motrix-tensor-runtime.Phase 0 audit conclusions
Phase 1 merged
unisim PR #339 →
roadmap/issue-1811-tensor-manager, mergecbcb3c6.Adds:
and proves public widths match public state layout and packed reset validation.
All remote CI passed:
The selected-reset publication boundary remains explicitly paired commit + selected H2D read; Motrix does not falsely declare
AUTHORITATIVE_VIEWS.Current heads
dev/issue-2054-motrix-tensor-runtime90068b82roadmap/issue-1811-tensor-managercbcb3c6Next: Phase 2 boundary baseline and A/B attribution on the canonical G1 scenes.
perf(motrix): read only selected packed rows (#340)
TATP-233 commentedon Oct 4, 2026
Phase 2 Path C merged: selected packed publication no longer computes unselected rows.
unisim PR #340 →
roadmap/issue-1811-tensor-manager, mergebc61643.Before:
After:
Transfer counts and stable views are unchanged. This is a host-boundary compute reduction, not an additional or hidden transfer.
New contract tests:
get_state/get_body_state_ware rejected during selected publication;Remote CI: Linux/macOS/Windows Python 3.10, type checks, and source distribution all passed.
Current unisim head:
bc61643.TATP-233 commentedon Oct 4, 2026
Phase 2 boundary attribution on canonical G1 flat scene (2048 envs).
Local diagnostic measurements after unisim #339/#340. Values are mean over 30 iterations after 5 warmups, unless noted.
Selected packed reset + selected publication
At the canonical 1/16 reset cadence, Motrix selected reset/publication is already at parity with mjbatch. Full-batch reset is faster on Motrix, while sparse resets are comparable.
Control + 3 physics substeps + one packed full read
Ratio: 53.4% of MuJoCo/mjbatch.
This cycle dominates every control step, while selected reset/publication is a small phase at the 1/16 cadence. Therefore the remaining Motrix gap is primarily in MotrixSim CPU physics/native full-read work, not D2H/H2D or selected reset orchestration.
Host reset sub-attribution (Motrix, 2048 envs, 128 reset rows)
Mean selected
set_statewall time: 0.346 ms.Largest sub-phases:
Interpretation
Next
write_control, nativestep_n, state/sensor pack, and H2D.TATP-233 commentedon Oct 4, 2026
Phase 2 control-cycle attribution refinement.
The same canonical G1 flat scene, 2048 envs, three physics substeps, and one packed full read were decomposed after the aggregate measurement.
Motrix
step_n(..., 3)MuJoCo/mjbatch comparable boundary
Conclusion
Transfer boundaries are already negligible:
The aggregate Motrix/MuJoCo ratio of ~53.4% is overwhelmingly explained by native MotrixSim CPU physics: ~24.8 ms vs the complete MuJoCo backend step boundary of ~13.8 ms. Python orchestration and explicit D2H/H2D account for under 1 ms.
The remaining host-pack cost is ~0.74 ms. It is the only non-native optimization candidate, and even eliminating it entirely would change the ratio by roughly 2%, below the measured native physics gap.
Boundary decision: continue contract-first UniLab owner enablement rather than speculative host rewrite. Further Motrix throughput work belongs primarily in MotrixSim native physics, not the packed HOST_BRIDGE contract.
feat(motrix): restore canonical packed host-bridge owners (#2056)
TATP-233 commentedon Oct 4, 2026
Phase 3 complete: canonical Motrix owners merged.
PR #2056 →
dev/issue-2054-motrix-tensor-runtime, merge60ab8473.Enabled surface
Exactly two canonical workloads:
Other Motrix owners remain out of scope and stay
Registered, not production claims.Owner decisions
motrixsimis absent.G1WalkResetOwner;events.pd_gains: null.TensorMotionCommandCfg;MotionResetOwnerowning command/action/observation/metric reset;scoped_device_event_tensor_with_host_commitfor tensor reset events.Validation
make check: pass.Current heads
dev/issue-2054-motrix-tensor-runtime60ab8473roadmap/issue-1811-tensor-managerbc61643Next: Phase 4 local 160×2048 ×3 interleaved throughput gate against MuJoCo/mjbatch.
TATP-233 commentedon Oct 4, 2026
Phase 4 first measured baseline.
Both Motrix runs completed 160/160 at 2048 envs on the local workstation, with no play/ONNX. MuJoCo/mjbatch reference runs used the same shape and measurement contract.
g1_walk_flatg1_walk_flatg1_motion_trackingg1_motion_trackingNo residual training/Motrix process remained after the runs.
Both Motrix workloads exceed the initial 35% floor and the 50% optimization target, and nearly reach the 60% stretch gate. This is consistent with the Phase 2 attribution: explicit D2H/H2D is ~0.1 ms/control step, while the remaining gap is dominated by MotrixSim native
step_nCPU physics.Final acceptance still requires three interleaved Motrix runs per workload, reporting every run. The MuJoCo references will also be interleaved with those acceptance runs.
TATP-233 commentedon Oct 4, 2026
Phase 4 interleaved acceptance evidence (3 Motrix runs per workload, MuJoCo/mjbatch references interleaved).
Interleaved order for each of rounds 1–3: SAC Motrix → SAC MuJoCo → FlashSAC Motrix → FlashSAC MuJoCo.
Every run: 160×2048, no play/ONNX,
tail_iteration_count=20, normal shutdown, collector exit 0.SAC /
g1_walk_flat2026-10-04_14-21-50_motrix2026-10-04_14-25-15_motrix2026-10-04_14-26-33_motrix2026-10-04_14-23-25_mujoco2026-10-04_14-25-34_mujoco2026-10-04_14-26-53_mujocoMotrix min/median/max: 61,117.173 / 63,204.377 / 63,328.873
MuJoCo min/median/max: 90,992.450 / 94,377.964 / 94,445.013
Worst-run ratio: 67.17%
FlashSAC /
g1_motion_tracking2026-10-04_14-22-36_motrix2026-10-04_14-25-50_motrix2026-10-04_14-27-09_motrix2026-10-04_14-23-54_mujoco2026-10-04_14-26-14_mujoco2026-10-04_14-27-32_mujocoMotrix min/median/max: 55,490.442 / 56,342.518 / 56,626.750
MuJoCo min/median/max: 87,084.407 / 87,622.321 / 90,448.674
Worst-run ratio: 63.72%
Gate result
All runs completed 160/160 with finite training state and no residual collector/Motrix process after the sequence.
Phase 2 attribution remains controlling: explicit D2H/H2D is approximately 0.1 ms/control step, selected reset is at parity at the canonical 1/16 cadence, and the remaining gap is dominated by MotrixSim native
step_nCPU physics. No contract weakening or speculative host rewrite is warranted.test(motrix): align flashsac owner scope with canonical runtime (#2057)
test(motrix): scope tensor event assertion to Motrix owner (#2058)
test(support): allow absent Motrix support cells (#2059)
TATP-233 commentedon Oct 4, 2026
Phase 5 closeout validation and integration state.
Final integration head:
dev/issue-2054-motrix-tensor-runtime@44207bfd.Commands and outcomes:
make check— pass.make test— 1,863 passed / 20 skipped / 0 failed.make test-all— pass, including benchmark import smoke (35/35 module mode, 36/36 script mode).MISSINGrather than absent from the generated matrix.Merged UniLab PRs on the integration branch:
feat(motrix): restore canonical packed host-bridge owners→60ab8473test(motrix): align flashsac owner scope with canonical runtime→1a468e0dtest(motrix): scope tensor event assertion to Motrix owner→84cb0bcbtest(support): allow absent Motrix support cells→44207bfdNo
mainmutation and no PyPI release.roadmap: restore Motrix canonical packed host-bridge runtime (#2060)
TATP-233 commentedon Oct 4, 2026
Completed by #2060. Final integration includes #2056, #2057, #2058, and #2059; unisim prerequisites #339/#340 are included through the pinned roadmap workspace. All phase evidence and gates are recorded in this issue.
merge: promote tensor-manager contracts to tensor runtime (#343)