Skip to content

Commit d3d5cae

Browse files
natepaynefbmeta-codesync[bot]
authored andcommitted
Create website snapshot v1.0.1
fbshipit-source-id: 6c017c5ab414ae5f16bbe3881abdb935e3b7e8e7
1 parent 7706f40 commit d3d5cae

128 files changed

Lines changed: 20961 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
---
2+
title: Overview
3+
slug: /overview
4+
---
5+
6+
import ProjectSuperdexLink from '@site/src/components/ProjectSuperdexLink';
7+
8+
# SuperDex Lab
9+
10+
SuperDex Lab connects simulation and policy development through a Gymnasium-style
11+
API for reinforcement learning.
12+
13+
:::info Early Preview
14+
SuperDex Lab is currently in early preview and will receive substantial
15+
improvements.
16+
:::
17+
18+
## What's Included
19+
20+
### SuperDex Gym
21+
22+
A [Gymnasium](https://gymnasium.farama.org/)-compatible reinforcement-learning
23+
framework built on the <ProjectSuperdexLink site="physics">SuperDex Physics</ProjectSuperdexLink>
24+
engine. Train agent controllers in high-fidelity simulation with a suite of
25+
manipulation and locomotion environments.
26+
27+
Start with the [SuperDex Gym introduction](./superdex_gym/intro.md), then
28+
[install and set it up](./superdex_gym/setup.md).
29+
30+
### RLlib Training
31+
32+
An off-the-shelf [Ray/RLlib](https://docs.ray.io/en/latest/rllib/index.html)
33+
integration for RL training straight from the Gym environments. See
34+
[Training with Ray/RLlib](./superdex_gym/rllib.md).
35+
36+
### Benchmarking
37+
38+
Benchmark control and simulation throughput and approximate memory usage across
39+
worker and environment configurations. See [Benchmarking](./superdex_gym/benchmarking.md).
Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
---
2+
title: Training with Large Batches
3+
---
4+
5+
# Training with Large Batches
6+
7+
SuperDex Gym scales RL training by combining two-tier vectorization with scene sharing
8+
within each worker. See
9+
[`run_with_gymnasium_vectorization.py`](./running_examples.md#2-run_with_gymnasium_vectorizationpy)
10+
for a runnable example and [Benchmarking](./benchmarking.md) for measuring throughput
11+
on your hardware.
12+
13+
## Example Pattern
14+
15+
This minimal example shows how to batch training safely with `HybridVectorEnv`.
16+
17+
```python
18+
from superdex.lab.gym.envs.benchmarks.cartpole_env import CartPoleEnv
19+
from superdex.lab.gym.utils.vector import HybridVectorEnv
20+
21+
num_training_steps = 1000
22+
23+
24+
def main():
25+
# Environment factory functions, one per environment.
26+
env_creators = [lambda: CartPoleEnv({"render_mode": None}) for _ in range(1000)]
27+
28+
# 8 async workers, each stepping 125 environments sequentially.
29+
with HybridVectorEnv(env_creators, num_envs_per_worker=125) as env:
30+
print(f"Created {env.num_envs} environments")
31+
print(f"Using {env.num_workers} async workers")
32+
print(f"Each worker manages {env.num_envs_per_worker} environments")
33+
34+
observations, _ = env.reset(seed=list(range(env.num_envs)))
35+
for step in range(num_training_steps):
36+
actions = ... # your policy
37+
observations, rewards, terminated, truncated, info = env.step(actions)
38+
39+
40+
# Required: the wrapper spawns its workers, so each child re-imports this module.
41+
if __name__ == "__main__":
42+
main()
43+
```
44+
45+
Using `HybridVectorEnv` as a context manager closes all workers if any worker raises
46+
an exception during the training loop.
47+
48+
:::warning The `__main__` guard is not optional
49+
Construct `HybridVectorEnv` inside a function called from an
50+
`if __name__ == "__main__":` block, or spawned workers may fail to start.
51+
:::
52+
53+
### Output and batch shapes
54+
55+
The example prints:
56+
57+
```text
58+
Created 1000 environments
59+
Using 8 async workers
60+
Each worker manages 125 environments
61+
```
62+
63+
Inside the wrapper, the batch shape is
64+
`(num_workers, num_envs_per_worker, ...)`; the public API flattens it to
65+
`(num_envs, ...)`. For the example above, the internal leading dimensions are
66+
`(8, 125, ...)`, and the public batch dimension is `1000`:
67+
68+
| Value | Expected shape or result |
69+
| --- | --- |
70+
| `observations` | `env.observation_space.shape`, beginning with `(1000, ...)` |
71+
| `actions` | Exactly `env.action_space.shape`, beginning with `(1000, ...)` |
72+
| `rewards` | `(1000,)` |
73+
| `terminated` | `(1000,)` |
74+
| `truncated` | `(1000,)` |
75+
| `info` | A dict of per-environment arrays each indexed by environment ID, keyed by information type, such as truncation or termination reasons. |
76+
77+
### Environment properties
78+
79+
| Property | Meaning |
80+
| --- | --- |
81+
| `num_envs` | Total environments (`num_workers × num_envs_per_worker`) |
82+
| `num_workers` | Async subprocesses |
83+
| `num_envs_per_worker` | Environments stepped sequentially per worker |
84+
| `observation_space` / `action_space` | Batched, shape `(num_envs, ...)` |
85+
| `single_observation_space` / `single_action_space` | Per-environment shape |
86+
87+
`step()` requires a NumPy action array with shape `action_space.shape` and raises
88+
`ValueError` for any other input. It returns a `BatchedStepResult` named tuple of
89+
`(observation, reward, terminated, truncated, info)`.
90+
91+
`info` includes arrays keyed by "terminated_reason" and "truncated_reason", and each array is indexed by global environment index. To read termination or truncation reasons from `info`:
92+
93+
```python
94+
observations, rewards, terminated, truncated, info = env.step(actions)
95+
for i in range(env.num_envs):
96+
if terminated[i] or truncated[i]:
97+
key = "terminated_reason" if terminated[i] else "truncated_reason"
98+
print(i, info[key][i])
99+
```
100+
101+
## How Batching Works
102+
103+
Large-scale training uses two mechanisms:
104+
105+
1. **`HybridVectorEnv`** combines async and sync execution.
106+
2. **Scene sharing** reduces memory use and startup time through shared physics
107+
scenes.
108+
109+
### Two-tier vectorization
110+
111+
`HybridVectorEnv` has two execution layers:
112+
113+
- **Outer layer**: an `AsyncVectorEnv` distributes work across `num_workers`
114+
subprocesses.
115+
- **Inner layer**: each worker runs a `SyncVectorEnv` that steps
116+
`num_envs_per_worker` environments sequentially in-process.
117+
118+
The wrapper derives the worker count as
119+
`num_envs // num_envs_per_worker`; it does not choose a count based on available CPU
120+
cores. More workers increase parallelism and inter-process communication, while more
121+
environments per worker can reduce load imbalance and improve scene sharing.
122+
123+
### Common constructor options
124+
125+
```python
126+
HybridVectorEnv(
127+
env_fns, # Sequence[Callable[[], Env]], one per environment
128+
num_envs_per_worker=1,
129+
autoreset_mode=AutoresetMode.NEXT_STEP,
130+
)
131+
```
132+
133+
`env_fns` contains one factory per environment. Autoreset defaults to `NEXT_STEP` and
134+
runs at the inner layer. The outer layer disables autoreset so the two layers do not
135+
compound it.
136+
137+
### Shared-scene state isolation
138+
139+
Within each process, `SceneManager` keeps one instance per environment class name. Each
140+
instance holds scenes by name with a reference count. Two environment classes never
141+
share a scene, even when both use the same scene name.
142+
143+
Each environment that shares a scene keeps its own state snapshot. Before that
144+
environment steps, it restores its snapshot into the shared scene; afterward, it
145+
captures the updated state. Sequential stepping prevents one environment's state from
146+
leaking into another.
147+
148+
`Scene.capture_state()` captures dynamic state only: poses, velocities, controller
149+
and constraint targets, external forces, the scene clock, and the step counter.
150+
Restoring a snapshot also rewinds simulation time. It does not capture topology,
151+
materials, joint limits, or gravity. Every configuration field that changes scene
152+
construction must be part of the scene name. Otherwise, environments with different
153+
structures incorrectly share one scene.
154+
155+
Enable sharing through the environment configuration:
156+
157+
```python
158+
config = {
159+
"use_shared_scenes": True, # Default.
160+
"control_frequency": 20,
161+
"simulation_frequency": 100,
162+
# ... other config options
163+
}
164+
165+
env_creators = [lambda: YourCustomEnv(config) for _ in range(1000)]
166+
```
167+
168+
### Scene sharing in custom environments
169+
170+
Route scene creation through `_load_scene()`. It reuses a scene with the requested
171+
name or registers a new scene. Include every configuration field that changes scene
172+
construction in that name. See
173+
[Authoring a Custom Environment](./environments.mdx#building-a-scene-from-a-prefab)
174+
for a complete example.
175+
176+
The three built-in benchmark environments use this pattern. `CartPoleEnv` uses
177+
`uid_fields = (cfg.use_damping, cfg.use_gravity, cfg.free_pole)`, the three fields
178+
that alter its prefab. It excludes fields such as `actuate_on_pole` that change only
179+
how actions are applied.
180+
181+
## Failure Modes
182+
183+
### Construction and reset inputs
184+
185+
- **Divisibility is required.** `len(env_fns) % num_envs_per_worker != 0` raises
186+
`ValueError: The total number of environments must be divisible by the number of
187+
environments per process.`
188+
- **The multiprocessing context is always `"spawn"`.** Passing another explicit
189+
context emits a `RuntimeWarning`, and the wrapper uses `"spawn"` instead.
190+
- **Spaces must be `Box`.** The wrapper reshapes `low` and `high` arrays directly. A
191+
`Dict` or `Discrete` space fails during space flattening. Every SuperDex Gym
192+
environment satisfies this contract because `MochiEnv` flattens its structured
193+
spaces to a single `Box`.
194+
- **There are no reset masks.** Passing `options={"reset_mask": ...}` raises
195+
`ValueError: Reset mask is not supported for this vectorization.` Use Gymnasium's
196+
`AsyncVectorEnv` or `SyncVectorEnv` directly for selective resets.
197+
198+
:::warning Use per-environment seeds for independent rollouts
199+
A scalar `int` seed is expanded twice: once by the outer `AsyncVectorEnv` and again by
200+
each inner `SyncVectorEnv`. Environment `(w, k)` receives `seed + w + k`. Therefore,
201+
`W × K` environments receive only `W + K - 1` distinct seeds. With 8 workers and 125
202+
environments per worker, 1000 environments receive only 132 distinct seeds. The
203+
wrapper emits no error or warning, and rollouts become silently correlated.
204+
205+
```python
206+
# Wrong: 1000 environments, 132 distinct seeds.
207+
env.reset(seed=42)
208+
209+
# Right: one seed per environment.
210+
env.reset(seed=list(range(env.num_envs)))
211+
```
212+
213+
Pass a list or tuple containing one seed per environment. `reset()` validates its
214+
length and distributes the values across workers so global environment `i` receives
215+
`seed[i]`. NumPy arrays do not take this sequence-chunking path and are unsupported.
216+
:::
217+
218+
### Shared-scene correctness
219+
220+
:::caution Scene sharing assumes sequential stepping
221+
`use_shared_scenes` defaults to `True`, so every environment opts in. Sharing is
222+
correct only when environments in the same process run one at a time.
223+
`HybridVectorEnv` provides this guarantee through each worker's inner `SyncVectorEnv`.
224+
Do not use shared scenes with a vectorizer that steps multiple environments
225+
concurrently in the same process; their state will be corrupted.
226+
:::
227+
228+
State capture does not support scenes containing ROM actors. Attempting to capture
229+
one raises `"State capture is not supported for scenes with ROM actors."`
230+
231+
If scene sharing is enabled but the scene was not created through `_load_scene()`,
232+
the environment emits a `RuntimeWarning` on reset.
233+
234+
## Performance
235+
236+
### Choose the async/sync split
237+
238+
For a fixed total environment count, the worker split has two main tradeoffs:
239+
240+
- **More workers with fewer environments each** increase CPU parallelism. Each worker
241+
adds a subprocess and a full physics context. Because every worker builds its own
242+
scene, this split reduces scene-sharing benefits.
243+
- **Fewer workers with more environments each** amortize scene construction and
244+
memory across each worker. Each worker steps environments sequentially, so its wall
245+
time generally increases with `num_envs_per_worker`.
246+
247+
Use [the benchmark script](./benchmarking.md) to tune the split for the target
248+
environment and hardware. Start `num_workers` at up to the physical CPU core count,
249+
then optionally sweep up to the logical CPU count. Increase `num_envs_per_worker` from
250+
1 until throughput plateaus. The script sweeps `--num_workers` and
251+
`--num_envs_per_worker` combinations and reports control and simulation FPS.
252+
253+
### Scene-sharing savings
254+
255+
When environments use an identical scene configuration, sharing reduces memory use
256+
and startup time. Sequential stepping within each worker makes the optimization safe.
257+
Within each worker process, the SuperDex Physics file cache also avoids repeated
258+
model-loading work.

0 commit comments

Comments
 (0)