Skip to content

Commit 646757c

Browse files
committed
fix(runtime): require explicit host-bridge manager devices
1 parent 3798969 commit 646757c

20 files changed

Lines changed: 340 additions & 97 deletions

‎docs/sphinx/source/adr/ADR-0012-sole-tensor-manager-and-scoped-backends.md‎

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,16 @@ owns its explicit packed conversion boundary. MuJoCo remains a production backen
6161
canonical in-process host-bridge implementation; it is not a test-only fallback.
6262

6363
`env.tensor_runtime` and `env.tensor_runtime_device` are removed. Tensor execution is an invariant
64-
of the Manager runtime. Placement is derived from declared backend data plane and rank topology.
65-
No compatibility alias, dual-mode field, or hidden fallback is retained.
66-
67-
For a `HOST_BRIDGE` backend, CPU-authoritative physics does not imply CPU Manager tensors.
68-
When PyTorch exposes an available GPU and the backend accepts its current `cuda` ordinal,
69-
the Manager uses that GPU, including ROCm PyTorch's `cuda` namespace. CPU-only capabilities
70-
or hosts without an available GPU retain CPU placement. `DEVICE_RESIDENT` adapters keep
71-
their own CUDA-only platform requirements; accepting HIP Torch buffers on a host bridge
64+
of the Manager runtime. Placement is derived from the declared backend data plane and, for a
65+
`HOST_BRIDGE`, the process's explicit carrier request. No NumPy/dual-runtime compatibility alias
66+
or hidden fallback is retained.
67+
68+
For a `HOST_BRIDGE` backend, CPU-authoritative physics keeps the default Manager/TorchEnv
69+
carriers on CPU. A training process may explicitly request `cuda`/`cuda:<ordinal>` through
70+
`manager_torch_device` (routed from its resolved learner device), including ROCm PyTorch's
71+
`cuda` namespace; UniLab validates the request against the backend's declared Torch devices.
72+
GPU visibility alone is not a placement request. `DEVICE_RESIDENT` adapters keep their own
73+
CUDA-only placement and platform requirements; accepting HIP Torch buffers on a host bridge
7274
does not make those physics engines ROCm-compatible.
7375

7476
### Manager-owned RNG

‎docs/sphinx/source/en/1-getting_started/2-installation.md‎

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -243,10 +243,13 @@ ROCm notes:
243243
profile, use `uv run --no-sync` (or `UV_NO_SYNC=1 make ...`) for validation;
244244
automatic synchronization would reinstall that profile's CUDA wheels.
245245
- With ROCm PyTorch and an available GPU, a `HOST_BRIDGE` backend accepting
246-
`cuda` buffers runs the Manager/TorchEnv tensors on the current GPU. MuJoCo
247-
physics remains on CPU; packed transfers connect it to GPU observations,
248-
actions, rewards, and resets. CUDA-only physics backends remain unsupported
249-
on ROCm. See {doc}`/adr/ADR-0012-sole-tensor-manager-and-scoped-backends`.
246+
`cuda` buffers can use the current GPU for Manager/TorchEnv tensors when the
247+
training process explicitly requests that learner device (which routes
248+
`manager_torch_device`). The direct environment default remains CPU. MuJoCo
249+
physics stays on CPU; packed transfers connect it to explicitly requested GPU
250+
observations, actions, rewards, and resets. CUDA-only physics backends remain
251+
unsupported on ROCm. See
252+
{doc}`/adr/ADR-0012-sole-tensor-manager-and-scoped-backends`.
250253
- When installing from PyPI instead of a source checkout, `make sync-rocm` does
251254
not apply. Install the torch build validated by the repository from the
252255
PyTorch ROCm index first, then `unilab`. The published dependency range is

‎docs/sphinx/source/en/5-reference/5-support_matrix.md‎

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,12 @@ This table is derived from UniSim's SDK-free public static inventory. It describ
7474

7575
| Backend | Execution / process / data plane | Torch devices | CUDA runtime | Linux+CUDA | macOS | ROCm | Worker | Reset randomization | Fixed variants | Host callbacks | Packed bridge |
7676
|---|---|---|---|---|---|---|---|---|---|---|---|
77-
| `mujoco` | Host bridge / in-process / host bridge | CPU / CUDA | Required only when the learner requests CUDA state/control buffers | Supported: CPU-authoritative physics with optional CUDA Torch buffers | CPU-authoritative host bridge only; no CUDA physics claim | Supported: CPU-authoritative physics with ROCm Torch buffers (`cuda`) | In-process; no external Python worker | unknown | unknown | Unsupported | Exact |
78-
| `motrix` | Host bridge / in-process / host bridge | CPU / CUDA | Required only when the learner requests CUDA state/control buffers | Supported: CPU-authoritative physics with optional CUDA Torch buffers | CPU-authoritative host bridge only; no CUDA physics claim | Supported: CPU-authoritative physics with ROCm Torch buffers (`cuda`) | In-process; no external Python worker | Unsupported | Unsupported | Unsupported | Exact |
79-
| `drake` | Host bridge / in-process / host bridge | CPU / CUDA | Required only when the learner requests CUDA state/control buffers | Supported: CPU-authoritative physics with optional CUDA Torch buffers | CPU-authoritative host bridge only; no CUDA physics claim | Supported: CPU-authoritative physics with ROCm Torch buffers (`cuda`) | In-process; no external Python worker | Unsupported | Unsupported | Unsupported | Exact |
77+
| `mujoco` | Host bridge / in-process / host bridge | CPU / CUDA | Required only when the learner requests CUDA state/control buffers | Supported: CPU-authoritative physics with optional CUDA Torch buffers | CPU-authoritative host bridge only; no CUDA physics claim | CPU-authoritative host bridge with explicit ROCm Torch buffers (`cuda`) | In-process; no external Python worker | unknown | unknown | Unsupported | Exact |
78+
| `motrix` | Host bridge / in-process / host bridge | CPU / CUDA | Required only when the learner requests CUDA state/control buffers | Supported: CPU-authoritative physics with optional CUDA Torch buffers | CPU-authoritative host bridge only; no CUDA physics claim | CPU-authoritative host bridge with explicit ROCm Torch buffers (`cuda`) | In-process; no external Python worker | Unsupported | Unsupported | Unsupported | Exact |
79+
| `drake` | Host bridge / in-process / host bridge | CPU / CUDA | Required only when the learner requests CUDA state/control buffers | Supported: CPU-authoritative physics with optional CUDA Torch buffers | CPU-authoritative host bridge only; no CUDA physics claim | CPU-authoritative host bridge with explicit ROCm Torch buffers (`cuda`) | In-process; no external Python worker | Unsupported | Unsupported | Unsupported | Exact |
8080
| `mjwarp` | Device-resident / in-process / direct | CUDA | Required for the entire tensor lifecycle | Supported: Linux CUDA only | Unsupported; no CPU, MPS, or ROCm fallback | Unsupported; no CPU, MPS, or ROCm fallback | In-process; no external Python worker | Unsupported | Unsupported | Unsupported | Unsupported |
8181
| `newton` | Device-resident / in-process / direct | CUDA | Required for the entire tensor lifecycle | Supported: Linux CUDA only | Unsupported; no CPU, MPS, or ROCm fallback | Unsupported; no CPU, MPS, or ROCm fallback | In-process; no external Python worker | Unsupported | Unsupported | Unsupported | Unsupported |
82-
| `superdex` | Host bridge / in-process / host bridge | CPU / CUDA | Required only when the learner requests CUDA state/control buffers | Supported: CPU-authoritative physics with optional CUDA Torch buffers | CPU-authoritative host bridge only; no CUDA physics claim | Supported: CPU-authoritative physics with ROCm Torch buffers (`cuda`) | In-process; no external Python worker | Unsupported | Unsupported | Unsupported | Exact |
82+
| `superdex` | Host bridge / in-process / host bridge | CPU / CUDA | Required only when the learner requests CUDA state/control buffers | Supported: CPU-authoritative physics with optional CUDA Torch buffers | CPU-authoritative host bridge only; no CUDA physics claim | CPU-authoritative host bridge with explicit ROCm Torch buffers (`cuda`) | In-process; no external Python worker | Unsupported | Unsupported | Unsupported | Exact |
8383
| `genesis` | Device-resident / in-process / direct | CUDA | Required for the entire tensor lifecycle | Supported: Linux CUDA only | Unsupported; no CPU, MPS, or ROCm fallback | Unsupported; no CPU, MPS, or ROCm fallback | In-process; no external Python worker | Unsupported | Unsupported | Unsupported | Unsupported |
8484

8585
### Entrypoint x Task Owner
@@ -125,7 +125,8 @@ configuration. If `CUDA_VISIBLE_DEVICES` is set, backend ordinals address that
125125
remapped namespace, not host-global physical indices.
126126

127127
On macOS and ROCm, use a CPU-authoritative host-bridge backend. On ROCm,
128-
Manager/TorchEnv uses the current GPU when ROCm PyTorch is available and the
129-
backend accepts that `cuda` device; CPU physics does not require CPU Manager
130-
tensors. GPU Torch buffers on a host bridge do not imply GPU physics or a
131-
device-resident backend lifecycle.
128+
Manager/TorchEnv uses the current GPU only when the training process explicitly
129+
requests that learner device and the backend accepts the routed `cuda`
130+
`manager_torch_device`; direct environment construction remains CPU. GPU Torch
131+
buffers on a host bridge do not imply GPU physics or a device-resident backend
132+
lifecycle.

‎docs/sphinx/source/zh_CN/1-getting_started/2-installation.md‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -218,10 +218,12 @@ ROCm 说明:
218218
- 如果手工安装了 ROCm wheel,但项目仍使用默认依赖配置档,验证时使用
219219
`uv run --no-sync`(或 `UV_NO_SYNC=1 make ...`);自动同步会重新安装默认配置档的
220220
CUDA wheel。
221-
- 安装 ROCm PyTorch 且 GPU 可用时,接受 `cuda` buffer 的 `HOST_BRIDGE` 后端会让
222-
Manager/TorchEnv tensor 使用当前 GPU。MuJoCo 物理仿真仍在 CPU 上,通过 packed
223-
传输连接 GPU 上的 observation、action、reward 与 reset。CUDA-only 物理后端仍不
224-
支持 ROCm。见 {doc}`/adr/ADR-0012-sole-tensor-manager-and-scoped-backends`。
221+
- 安装 ROCm PyTorch 且 GPU 可用时,接受 `cuda` buffer 的 `HOST_BRIDGE` 后端只有在
222+
训练进程显式请求该 learner 设备(并路由 `manager_torch_device`)时才会让
223+
Manager/TorchEnv tensor 使用当前 GPU;直接构造环境的默认仍是 CPU。MuJoCo 物理
224+
仿真仍在 CPU 上,通过 packed 传输连接显式请求的 GPU observation、action、reward
225+
与 reset。CUDA-only 物理后端仍不支持 ROCm。见
226+
{doc}`/adr/ADR-0012-sole-tensor-manager-and-scoped-backends`。
225227
- 从 PyPI 安装(不克隆仓库)时,`make sync-rocm` 不适用;先从 PyTorch ROCm 索引
226228
安装仓库验证过的 torch build,再安装 `unilab`。发布的依赖范围是
227229
`torch>=2.9,<2.15`,pip 会保留已安装的 ROCm build,不会替换为 CUDA wheel:

‎docs/sphinx/source/zh_CN/5-reference/5-support_matrix.md‎

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,12 @@ uv run scripts/generate_support_matrix.py --write
6565

6666
| Backend | Execution / process / data plane | Torch devices | CUDA runtime | Linux+CUDA | macOS | ROCm | Worker | Reset randomization | Fixed variants | Host callbacks | Packed bridge |
6767
|---|---|---|---|---|---|---|---|---|---|---|---|
68-
| `mujoco` | Host bridge / in-process / host bridge | CPU / CUDA | Required only when the learner requests CUDA state/control buffers | Supported: CPU-authoritative physics with optional CUDA Torch buffers | CPU-authoritative host bridge only; no CUDA physics claim | Supported: CPU-authoritative physics with ROCm Torch buffers (`cuda`) | In-process; no external Python worker | unknown | unknown | 不支持 | 支持 |
69-
| `motrix` | Host bridge / in-process / host bridge | CPU / CUDA | Required only when the learner requests CUDA state/control buffers | Supported: CPU-authoritative physics with optional CUDA Torch buffers | CPU-authoritative host bridge only; no CUDA physics claim | Supported: CPU-authoritative physics with ROCm Torch buffers (`cuda`) | In-process; no external Python worker | 不支持 | 不支持 | 不支持 | 支持 |
70-
| `drake` | Host bridge / in-process / host bridge | CPU / CUDA | Required only when the learner requests CUDA state/control buffers | Supported: CPU-authoritative physics with optional CUDA Torch buffers | CPU-authoritative host bridge only; no CUDA physics claim | Supported: CPU-authoritative physics with ROCm Torch buffers (`cuda`) | In-process; no external Python worker | 不支持 | 不支持 | 不支持 | 支持 |
68+
| `mujoco` | Host bridge / in-process / host bridge | CPU / CUDA | Required only when the learner requests CUDA state/control buffers | Supported: CPU-authoritative physics with optional CUDA Torch buffers | CPU-authoritative host bridge only; no CUDA physics claim | CPU-authoritative host bridge with explicit ROCm Torch buffers (`cuda`) | In-process; no external Python worker | unknown | unknown | 不支持 | 支持 |
69+
| `motrix` | Host bridge / in-process / host bridge | CPU / CUDA | Required only when the learner requests CUDA state/control buffers | Supported: CPU-authoritative physics with optional CUDA Torch buffers | CPU-authoritative host bridge only; no CUDA physics claim | CPU-authoritative host bridge with explicit ROCm Torch buffers (`cuda`) | In-process; no external Python worker | 不支持 | 不支持 | 不支持 | 支持 |
70+
| `drake` | Host bridge / in-process / host bridge | CPU / CUDA | Required only when the learner requests CUDA state/control buffers | Supported: CPU-authoritative physics with optional CUDA Torch buffers | CPU-authoritative host bridge only; no CUDA physics claim | CPU-authoritative host bridge with explicit ROCm Torch buffers (`cuda`) | In-process; no external Python worker | 不支持 | 不支持 | 不支持 | 支持 |
7171
| `mjwarp` | Device-resident / in-process / direct | CUDA | Required for the entire tensor lifecycle | Supported: Linux CUDA only | Unsupported; no CPU, MPS, or ROCm fallback | Unsupported; no CPU, MPS, or ROCm fallback | In-process; no external Python worker | 不支持 | 不支持 | 不支持 | 不支持 |
7272
| `newton` | Device-resident / in-process / direct | CUDA | Required for the entire tensor lifecycle | Supported: Linux CUDA only | Unsupported; no CPU, MPS, or ROCm fallback | Unsupported; no CPU, MPS, or ROCm fallback | In-process; no external Python worker | 不支持 | 不支持 | 不支持 | 不支持 |
73-
| `superdex` | Host bridge / in-process / host bridge | CPU / CUDA | Required only when the learner requests CUDA state/control buffers | Supported: CPU-authoritative physics with optional CUDA Torch buffers | CPU-authoritative host bridge only; no CUDA physics claim | Supported: CPU-authoritative physics with ROCm Torch buffers (`cuda`) | In-process; no external Python worker | 不支持 | 不支持 | 不支持 | 支持 |
73+
| `superdex` | Host bridge / in-process / host bridge | CPU / CUDA | Required only when the learner requests CUDA state/control buffers | Supported: CPU-authoritative physics with optional CUDA Torch buffers | CPU-authoritative host bridge only; no CUDA physics claim | CPU-authoritative host bridge with explicit ROCm Torch buffers (`cuda`) | In-process; no external Python worker | 不支持 | 不支持 | 不支持 | 支持 |
7474
| `genesis` | Device-resident / in-process / direct | CUDA | Required for the entire tensor lifecycle | Supported: Linux CUDA only | Unsupported; no CPU, MPS, or ROCm fallback | Unsupported; no CPU, MPS, or ROCm fallback | In-process; no external Python worker | 不支持 | 不支持 | 不支持 | 不支持 |
7575

7676
### Entrypoint x Task Owner
@@ -114,7 +114,8 @@ NVIDIA 驱动与容器运行时不匹配,再调整任务配置。设置了
114114
`CUDA_VISIBLE_DEVICES` 时,后端序号指向重映射后的命名空间,而不是宿主机
115115
全局物理索引。
116116

117-
在 macOS 与 ROCm 上请使用 CPU-authoritative host-bridge 后端。在 ROCm 上,
118-
ROCm PyTorch 的 GPU 可用且后端接受当前 `cuda` 设备时,Manager/TorchEnv 使用
119-
当前 GPU;CPU 物理仿真不要求 Manager tensor 使用 CPU。host bridge 使用 GPU
120-
Torch buffer 不代表 GPU physics,也不代表 device-resident 后端生命周期。
117+
在 macOS 与 ROCm 上请使用 CPU-authoritative host-bridge 后端。在 ROCm 上,只有
118+
训练进程显式请求该 learner 设备且后端接受被路由的 `cuda`
119+
`manager_torch_device` 时,Manager/TorchEnv 才使用当前 GPU;直接构造环境仍为
120+
CPU。host bridge 使用 GPU Torch buffer 不代表 GPU physics,也不代表
121+
device-resident 后端生命周期。

‎scripts/generate_support_matrix.py‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,17 @@
2525

2626
_SUPPORT_LEVEL_ENUM = re.compile(r"\bSupportLevel\.([A-Z][A-Z_]+)\b")
2727

28-
_ROCM_HOST_BRIDGE_LABEL = "Supported: CPU-authoritative physics with ROCm Torch buffers (`cuda`)"
28+
_ROCM_HOST_BRIDGE_LABEL = "CPU-authoritative host bridge with explicit ROCm Torch buffers (`cuda`)"
2929

3030

3131
def _with_support_level_values(content: str) -> str:
3232
"""Render hardened UniSim lifecycle enum values as their public strings."""
3333
rendered = _SUPPORT_LEVEL_ENUM.sub(lambda match: match.group(1).lower(), content)
3434
# UniSim's static inventory is deliberately conservative: it only states
3535
# that no ROCm-native CUDA-only fallback exists. Host bridges accepting
36-
# `cuda` Torch buffers also accept ROCm PyTorch's CUDA-namespace API while
37-
# physics remains CPU-authoritative; make that repository guarantee visible.
36+
# `cuda` Torch buffers also accept ROCm PyTorch's CUDA-namespace API,
37+
# but only when the process explicitly requests those Manager carriers;
38+
# make that opt-in repository guarantee visible.
3839
return rendered.replace(
3940
"CPU-authoritative host bridge only; no ROCm CUDA-only fallback",
4041
_ROCM_HOST_BRIDGE_LABEL,

‎src/unilab/base/process_device.py‎

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@
1616
from typing import Any, cast
1717

1818
# These backends consume an explicit integer device id while materializing
19-
# their simulator. MuJoCo/Motrix/Drake either run on the host or own their
20-
# device selection internally and must not receive a synthetic override.
19+
# their simulator. MuJoCo/Motrix/Drake run CPU-authoritative physics; their
20+
# optional accelerator Manager buffers are selected separately through
21+
# ``manager_torch_device``.
2122
BACKEND_ENV_DEVICE_FIELDS: dict[str, str] = {
2223
"isaacgym": "isaacgym_device_id",
2324
"isaacsim": "isaacsim_device_id",
@@ -41,6 +42,11 @@
4142
# integer payload sent to that worker before construction.
4243
_EXTERNAL_CUDA_IPC_BACKENDS = {"isaacgym", "isaacsim"}
4344

45+
# HOST_BRIDGE backends accept optional accelerator Torch carriers across their packed
46+
# boundary. A process may explicitly request those carriers on its learner Torch
47+
# device; unset non-accelerator requests retain the owner/default CPU placement.
48+
_HOST_BRIDGE_TORCH_BACKENDS = {"mujoco", "motrix", "drake", "superdex"}
49+
4450

4551
# Set once ``bind_genesis_process_device`` has pinned CUDA_VISIBLE_DEVICES for
4652
# this process. Genesis/Quadrants binds its CUDA runtime to the first visible
@@ -170,6 +176,35 @@ def apply_backend_env_device_override(
170176
return result
171177

172178

179+
def apply_manager_torch_device_override(
180+
env_cfg_override: Mapping[str, Any] | None,
181+
backend_type: str,
182+
*,
183+
learner_device: str | None = None,
184+
) -> dict[str, Any]:
185+
"""Return an env override selecting optional HOST_BRIDGE Torch carriers.
186+
187+
The input mapping is never mutated. Explicit CPU requests force CPU carriers.
188+
Unset and non-CUDA learner requests retain owner/default placement. A CUDA request
189+
is validated against backend capabilities when the environment binds.
190+
DEVICE_RESIDENT backends own placement and never receive this synthetic field.
191+
"""
192+
193+
result = dict(env_cfg_override) if env_cfg_override is not None else {}
194+
if _normalize_backend(backend_type) not in _HOST_BRIDGE_TORCH_BACKENDS:
195+
return result
196+
if learner_device is None:
197+
return result
198+
device = str(learner_device).strip()
199+
if not device or device.lower() == "cpu":
200+
result["manager_torch_device"] = "cpu"
201+
return result
202+
if device.split(":", 1)[0].lower() != "cuda":
203+
return result
204+
result["manager_torch_device"] = device
205+
return result
206+
207+
173208
def resolve_backend_process_device(backend_type: str, learner_device: str | None) -> str | None:
174209
backend = _normalize_backend(backend_type)
175210
if (
@@ -399,6 +434,7 @@ def _reset_genesis_device_pin_for_tests() -> None:
399434
__all__ = [
400435
"BACKEND_ENV_DEVICE_FIELDS",
401436
"apply_backend_env_device_override",
437+
"apply_manager_torch_device_override",
402438
"bind_backend_process_device",
403439
"bind_backend_process_device_for_backend",
404440
"bind_genesis_process_device",

0 commit comments

Comments
 (0)