Skip to content

Examples

Every example config in hylo-bench and hylo-plugins, with the comment at its top. Paths are relative to the development workspace; commands run from any directory (simulator runtimes come from the machine profile, artifacts from the store).

Experiments

Example What it shows
libero10_critic SmolVLA on LIBERO-10: sample 4 chunks per decision, execute the one a learned chunk critic values most (hylo.agents.critic). Train the critic first: uv run python -m hylo.learn.critic --runs runs/... --task-prefix libero/libero_10/ \ --image-camera agentview --out runs/critics/ and reference it by id (hylo store ref runs/critics/<name>). l10-v1 predates manifests and was adopted without a record of its training runs: it runs here, but the RSI gate refuses it in a candidate (unknown provenance).
libero10_memory SmolVLA on LIBERO-10 steered by an action memory of its own verified successes (RFC-0003 M1): flow sampling starts from a retrieved prior at t0 in [t0_min, t0_max] instead of pure noise. Build the memory first (development splits only): envs/libero/.venv/bin/python -m hylo.policies.lerobot.memory_build --runs runs/ \ --splits search --out runs/memory/ then reference it by id: hylo store ref runs/memory/<name>
libero10_noise SmolVLA on LIBERO-10 with noise-space steering (RFC-0004 W3): the initial flow noise is biased towards the noise that produced similar successful chunks; the policy is unchanged. Build the bank first (search split only): envs/libero/.venv/bin/python -m hylo.policies.lerobot.memory_build --store noise --splits search \ --runs runs/ --out runs/memory/ then reference it by id: hylo store ref runs/memory/<name>
libero10_planner LIBERO-10 with an LLM/VLM subgoal planner (qwen3.5) in front of the steered SmolVLA. CPU only.
libero10_steered SmolVLA on LIBERO-10 behind SteeredAgent (seeded noise, sample-and-select, runtime rules). CPU only. Simulator and policy each run out of process in this machine's libero-sim / lerobot runtimes (hylo profile show; bench's envs/libero/setup.sh builds the venv and registers both).
libero_smolvla SmolVLA (lerobot/smolvla_libero, 450M) on LIBERO, CPU only. Simulator and policy each run out of process in this machine's libero-sim / lerobot runtimes (hylo profile show; bench's envs/libero/setup.sh builds the venv and registers both).
mujoco_policy_flow_steered A flow-matching policy (trained by examples/policies: compare.py, or hylo learn examples/policies/jobs/flow.json) steered: 4 seeded samples per decision, the medoid executed. Any registered policy can be the sampler: policy.bc, policy.tokens, lerobot, your own. The sampler is referenced by store id; without a digest, the one artifact of that name (retraining replaces it), pinned in the run's manifest.
mujoco_reach -
mujoco_reach_remote The MuJoCo reach experiment with the simulator in a separate process (EnvServer protocol). Set runtime: to a runtime from this machine's profile to run an environment whose dependencies conflict with yours (LIBERO does this).
mujoco_table_memory Language-conditioned reaching with spatial memory on the MuJoCo table: the pads are visible only for the first 5 control steps, so the agent must remember where the named pad was (see external_demo/ for the full study).

libero10_critic

uv run hylo run bench/examples/configs/libero10_critic.yaml
bench/examples/configs/libero10_critic.yaml
# SmolVLA on LIBERO-10: sample 4 chunks per decision, execute the one a learned chunk critic values most
# (hylo.agents.critic). Train the critic first:
#   uv run python -m hylo.learn.critic --runs runs/... --task-prefix libero/libero_10/ \
#       --image-camera agentview --out runs/critics/<name>
# and reference it by id (`hylo store ref runs/critics/<name>`). l10-v1 predates manifests and was adopted without
# a record of its training runs: it runs here, but the RSI gate refuses it in a candidate (unknown provenance).
schema_version: hylo.experiment/v0.1
name: libero10-critic
benchmark: {type: libero, split: search_val, params: {suite: libero_10, protocol: rsi, replicates: 1, gate_looks: 3}}
env:
  type: libero
  remote: {runtime: libero-sim}
agent:
  type: remote
  params:
    ref: lerobot.critic_steered
    runtime: lerobot
    params:
      repo_id: lerobot/smolvla_libero
      num_threads: 3
      steering: {n_samples: 4, selector: gripper_vote_medoid, execution_steps: 25, seed_noise: true}
      critic: {path: "artifact:l10-v1@7934d463d67ad242", beta: 0.5, within_vote: true}
trace: {record_privileged: true, image_every: 5}
workers: 12
output_dir: runs

libero10_memory

uv run hylo run bench/examples/configs/libero10_memory.yaml
bench/examples/configs/libero10_memory.yaml
# SmolVLA on LIBERO-10 steered by an action memory of its own verified successes (RFC-0003 M1):
# flow sampling starts from a retrieved prior at t0 in [t0_min, t0_max] instead of pure noise.
# Build the memory first (development splits only):
#   envs/libero/.venv/bin/python -m hylo.policies.lerobot.memory_build --runs runs/<search runs> \
#       --splits search --out runs/memory/<name>
# then reference it by id: `hylo store ref runs/memory/<name>`
schema_version: hylo.experiment/v0.1
name: libero10-memory
benchmark: {type: libero, split: search_val, params: {suite: libero_10, protocol: rsi, replicates: 1, gate_looks: 3}}
env:
  type: libero
  remote: {runtime: libero-sim}
agent:
  type: remote
  params:
    ref: lerobot.memory_steered
    runtime: lerobot
    params:
      repo_id: lerobot/smolvla_libero
      num_threads: 4
      steering: {n_samples: 1, seed_noise: true}
      memory: {type: action_bank, params: {path: "artifact:l10-search-v1@40a84dd1afab4a5c"}}
      memory_steer: {k: 10, same_task: true, sim_floor: 0.95, t0_min: 0.1, t0_max: 0.7}
    env: {TORCHDYNAMO_DISABLE: "1"}
trace: {record_privileged: true, image_every: 10}
workers: 24
output_dir: runs

libero10_noise

uv run hylo run bench/examples/configs/libero10_noise.yaml
bench/examples/configs/libero10_noise.yaml
# SmolVLA on LIBERO-10 with noise-space steering (RFC-0004 W3): the initial flow noise is biased towards the
# noise that produced similar successful chunks; the policy is unchanged. Build the bank first (search split only):
#   envs/libero/.venv/bin/python -m hylo.policies.lerobot.memory_build --store noise --splits search \
#       --runs runs/<search runs> --out runs/memory/<name>
# then reference it by id: `hylo store ref runs/memory/<name>`
schema_version: hylo.experiment/v0.1
name: libero10-noise
benchmark: {type: libero, split: search_val, params: {suite: libero_10, protocol: rsi, replicates: 1, gate_looks: 3}}
env:
  type: libero
  remote: {runtime: libero-sim}
agent:
  type: remote
  params:
    ref: lerobot.noise_steered
    runtime: lerobot
    params:
      repo_id: lerobot/smolvla_libero
      num_threads: 4
      steering: {n_samples: 1, seed_noise: true}
      memory: {type: action_bank, params: {path: "artifact:l10-noise-search-v1@2687768fc5102b06", frozen: true}}
      noise_steer: {weight: 0.5, k: 5, same_task: true, sim_floor: 0.95}
    env: {TORCHDYNAMO_DISABLE: "1"}
trace: {record_privileged: true, image_every: 10}
workers: 24
output_dir: runs

libero10_planner

uv run hylo run bench/examples/configs/libero10_planner.yaml
bench/examples/configs/libero10_planner.yaml
# LIBERO-10 with an LLM/VLM subgoal planner (qwen3.5) in front of the steered SmolVLA. CPU only.
schema_version: hylo.experiment/v0.1
name: libero10-planner
benchmark:
  type: libero
  split: validation
  params:
    suite: libero_10
    n_validation: 10
    n_heldout: 0
env:
  type: libero
  remote: {runtime: libero-sim}
agent:
  type: planner.subgoal
  params:
    inner:
      type: remote
      params:
        ref: lerobot.steered
        runtime: lerobot
        params:
          repo_id: lerobot/smolvla_libero
          num_threads: 4
          steering:
            n_samples: 1
            seed_noise: true
    planner:
      type: planner.llm
      params:
        model: qwen3.5:35b-a3b
        cache_dir: runs/llm-cache
        subgoal_max_steps: 260
    check_every: 1
trace:
  record_privileged: true
  image_every: 10
workers: 24
output_dir: runs

libero10_steered

uv run hylo run bench/examples/configs/libero10_steered.yaml
bench/examples/configs/libero10_steered.yaml
# SmolVLA on LIBERO-10 behind SteeredAgent (seeded noise, sample-and-select, runtime rules). CPU only.
# Simulator and policy each run out of process in this machine's libero-sim / lerobot runtimes (hylo profile show;
# bench's envs/libero/setup.sh builds the venv and registers both).
schema_version: hylo.experiment/v0.1
name: libero10-steered
benchmark: {type: libero, split: validation, params: {suite: libero_10, n_validation: 10, n_heldout: 0}}
env:
  type: libero
  remote: {runtime: libero-sim}
agent:
  type: remote
  params:
    ref: lerobot.steered
    runtime: lerobot
    params: {repo_id: lerobot/smolvla_libero, num_threads: 4, steering: {n_samples: 1, seed_noise: true}}
trace: {record_privileged: true, image_every: 10}   # keep every 10th camera frame (a frame pair is ~400 KB)
workers: 24        # thread budget: 24 x (4 policy + 2 render) ~ 144 threads on 128 cores
output_dir: runs

libero_smolvla

uv run hylo run bench/examples/configs/libero_smolvla.yaml
bench/examples/configs/libero_smolvla.yaml
# SmolVLA (lerobot/smolvla_libero, 450M) on LIBERO, CPU only.
# Simulator and policy each run out of process in this machine's libero-sim / lerobot runtimes (hylo profile show;
# bench's envs/libero/setup.sh builds the venv and registers both).
schema_version: hylo.experiment/v0.1
name: libero-smolvla
benchmark: {type: libero, split: validation, params: {suite: libero_spatial, n_validation: 5, n_heldout: 0}}
env:
  type: libero
  remote: {runtime: libero-sim, env: {OMP_NUM_THREADS: "2", LP_NUM_THREADS: "4"}}
agent:
  type: remote
  params:
    ref: hylo.policies.lerobot.agent:LeRobotAgent
    runtime: lerobot
    params: {repo_id: lerobot/smolvla_libero, num_threads: 6}
trace: {record_privileged: true, image_every: 10}   # keep every 10th camera frame (a frame pair is ~400 KB)
workers: 10
output_dir: runs

mujoco_policy_flow_steered

uv run hylo run bench/examples/configs/mujoco_policy_flow_steered.yaml
bench/examples/configs/mujoco_policy_flow_steered.yaml
# A flow-matching policy (trained by examples/policies: compare.py, or hylo learn examples/policies/jobs/flow.json)
# steered: 4 seeded samples per decision, the medoid executed. Any registered policy can be the sampler:
# policy.bc, policy.tokens, lerobot, your own. The sampler is referenced by store id; without a digest, the one
# artifact of that name (retraining replaces it), pinned in the run's manifest.
schema_version: hylo.experiment/v0.1
name: mujoco-policy-flow-steered
benchmark: {type: mujoco/reach-v0, split: heldout, params: {protocol: rsi}}
env: {type: mujoco.planar_reach}
agent:
  type: steered
  params:
    sampler: {type: policy.flow, params: {path: "artifact:policy.flow"}}
    steering: {n_samples: 4, selector: medoid}
workers: 8
output_dir: runs

mujoco_reach

uv run hylo run bench/examples/configs/mujoco_reach.yaml
bench/examples/configs/mujoco_reach.yaml
schema_version: hylo.experiment/v0.1
name: mujoco-reach
benchmark: {type: mujoco/reach-v0, split: validation}
env: {type: mujoco.planar_reach}
agent: {type: baseline.planar_ik, params: {kp: 0.3}}
output_dir: runs

mujoco_reach_remote

uv run hylo run bench/examples/configs/mujoco_reach_remote.yaml
bench/examples/configs/mujoco_reach_remote.yaml
# The MuJoCo reach experiment with the simulator in a separate process (EnvServer protocol). Set `runtime:` to a
# runtime from this machine's profile to run an environment whose dependencies conflict with yours (LIBERO does this).
schema_version: hylo.experiment/v0.1
name: mujoco-reach-remote
benchmark: {type: mujoco/reach-v0, split: validation}
env: {type: mujoco.planar_reach, remote: {}}
agent: {type: baseline.planar_ik, params: {kp: 0.3}}
output_dir: runs

mujoco_table_memory

uv run hylo run bench/examples/configs/mujoco_table_memory.yaml
bench/examples/configs/mujoco_table_memory.yaml
# Language-conditioned reaching with spatial memory on the MuJoCo table: the pads are visible only for the first
# 5 control steps, so the agent must remember where the named pad was (see external_demo/ for the full study).
schema_version: hylo.experiment/v0.1
name: mujoco-table-memory
benchmark: {type: mujoco/table-v0, split: validation}
env: {type: mujoco.table_reach}
agent:
  type: composable.object_reach
  params: {kp: 0.3, chooser: {type: chooser.rule_color}, memory: {type: sqlite, params: {path: ":memory:"}}}
safety: {max_delta: 0.5}
output_dir: runs

Improvement campaigns

Example What it shows
libero10_llm Frozen SmolVLA on LIBERO-10, steering candidates proposed by a local LLM (qwen3.5) from failure traces. CPU only. uv run hylo evolve run examples/evolve/libero10_llm.yaml uv run hylo evolve status runs/evolve/libero10-llm
libero10_selftrain Expert iteration on LIBERO-10 (RFC-0002 L4): each cycle fine-tunes SmolVLA's action expert on its own successful search-split episodes (CPU), then gates the new weights on unseen init states. uv run hylo evolve run examples/evolve/libero10_selftrain.yaml
libero10_steering Frozen SmolVLA on LIBERO-10: evolve steering (L0/L1) with the RFC-0002 protocol, CPU only. uv run hylo evolve run examples/evolve/libero10_steering.yaml uv run hylo evolve status runs/evolve/libero10-steering
mujoco_reach_gain A campaign on MuJoCo planar reach (about a minute): the IK controller starts with a weak gain, a rule generator proposes larger and smaller gains, and the RFC-0002 gate (search_val selection, group-sequential gate, held-out report) decides. uv run hylo evolve run examples/evolve/mujoco_reach_gain.yaml

libero10_llm

uv run hylo evolve run bench/examples/evolve/libero10_llm.yaml
bench/examples/evolve/libero10_llm.yaml
# Frozen SmolVLA on LIBERO-10, steering candidates proposed by a local LLM (qwen3.5) from failure traces. CPU only.
#   uv run hylo evolve run examples/evolve/libero10_llm.yaml
#   uv run hylo evolve status runs/evolve/libero10-llm
schema_version: hylo.evolve/v0.1
name: libero10-llm
experiment: ../configs/libero10_steered.yaml
experiment_overrides:
  benchmark.params: {suite: libero_10, protocol: rsi, replicates: 1, gate_looks: 3}
  workers: 24
proposer: {type: llm.steering, params: {model: "qwen3.5:35b-a3b", n_candidates: 4, cache_dir: runs/llm-cache}}
diagnosis: {stall_steps: 30, regrasp: 3}
protocol: {alpha: 0.05, gate_looks: 3, spending_rho: 3.0, stage1_pairs: 30, stage1_max_deficit: 4, gate_top_k: 1}
cycles: 3
stop_after_no_progress: 2
budget_s: 28800
approval: auto        # use manual for anything that runs on hardware
out_dir: runs/evolve/libero10-llm

libero10_selftrain

uv run hylo evolve run bench/examples/evolve/libero10_selftrain.yaml
bench/examples/evolve/libero10_selftrain.yaml
# Expert iteration on LIBERO-10 (RFC-0002 L4): each cycle fine-tunes SmolVLA's action expert on its own
# successful search-split episodes (CPU), then gates the new weights on unseen init states.
#   uv run hylo evolve run examples/evolve/libero10_selftrain.yaml
schema_version: hylo.evolve/v0.1
name: libero10-selftrain
experiment: ../configs/libero10_steered.yaml
experiment_overrides:
  benchmark.params: {suite: libero_10, protocol: rsi, replicates: 1, gate_looks: 3}
  agent.params.params.steering: {n_samples: 1, seed_noise: true, execution_steps: 25}
  workers: 24
proposer: {type: selftrain.expert_iteration, params: {out_root: runs/selftrain/libero10, steps: 300, batch_size: 8, lr: 1.0e-5}}
diagnosis: {stall_steps: 30, regrasp: 3}
protocol: {alpha: 0.05, gate_looks: 3, spending_rho: 3.0, stage1_pairs: 30, stage1_max_deficit: 4, gate_top_k: 1}
cycles: 2
stop_after_no_progress: 1
budget_s: 43200
approval: manual      # new weights: a person signs off before promotion
out_dir: runs/evolve/libero10-selftrain

libero10_steering

uv run hylo evolve run bench/examples/evolve/libero10_steering.yaml
bench/examples/evolve/libero10_steering.yaml
# Frozen SmolVLA on LIBERO-10: evolve steering (L0/L1) with the RFC-0002 protocol, CPU only.
#   uv run hylo evolve run examples/evolve/libero10_steering.yaml
#   uv run hylo evolve status runs/evolve/libero10-steering
schema_version: hylo.evolve/v0.1
name: libero10-steering
experiment: ../configs/libero10_steered.yaml
experiment_overrides:
  benchmark.params: {suite: libero_10, protocol: rsi, replicates: 1, gate_looks: 3}
  workers: 24
proposer: {type: rule.steering, params: {}}
diagnosis: {stall_steps: 30, regrasp: 3}
protocol: {alpha: 0.05, gate_looks: 3, spending_rho: 3.0, stage1_pairs: 30, stage1_max_deficit: 4, gate_top_k: 1}
cycles: 3
stop_after_no_progress: 2
budget_s: 28800
approval: auto        # use manual for anything that runs on hardware
out_dir: runs/evolve/libero10-steering

mujoco_reach_gain

uv run hylo evolve run bench/examples/evolve/mujoco_reach_gain.yaml
bench/examples/evolve/mujoco_reach_gain.yaml
# A campaign on MuJoCo planar reach (about a minute): the IK controller starts with a weak gain, a rule generator
# proposes larger and smaller gains, and the RFC-0002 gate (search_val selection, group-sequential gate,
# held-out report) decides.   uv run hylo evolve run examples/evolve/mujoco_reach_gain.yaml
schema_version: hylo.evolve/v0.1
name: mujoco-reach-gain
experiment: ../configs/mujoco_reach.yaml
experiment_overrides: {agent.params.kp: 0.02, workers: 16, benchmark.params: {protocol: rsi, gate_looks: 2}}
proposer: {type: rule.gain, params: {path: agent.params.kp, factors: [2.0, 4.0]}}
protocol: {alpha: 0.05, gate_looks: 2, stage1_pairs: 20}
cycles: 3
stop_after_no_progress: 1
approval: auto
out_dir: runs/evolve/mujoco-reach-gain

Plugin examples: experiments

Example What it shows
mujoco_guarded Guard plugins from the safety config (they run between the shield's mandatory finite and bounds checks). examples.slow_zone comes from packages/hylo-plugin-examples: a third-party package, no Hylo change. Here the fingertip slows to a quarter of its speed within 10 cm of the fixed target.
mujoco_mixture A mixture of two policies, steered: each of 4 seeded samples comes from one member (examples.mixture), and the medoid proposal is executed. Members can be any registered policies (reference policies, LeRobot, your own).
mujoco_reach MuJoCo planar reach with the analytic IK controller: the experiment the plugin examples build on.

mujoco_guarded

uv run hylo run plugins/examples/configs/mujoco_guarded.yaml
plugins/examples/configs/mujoco_guarded.yaml
# Guard plugins from the safety config (they run between the shield's mandatory finite and bounds checks).
# examples.slow_zone comes from packages/hylo-plugin-examples: a third-party package, no Hylo change.
# Here the fingertip slows to a quarter of its speed within 10 cm of the fixed target.
schema_version: hylo.experiment/v0.1
name: mujoco-guarded
benchmark: {type: mujoco/reach-v0, split: validation}
env: {type: mujoco.planar_reach}
agent: {type: baseline.planar_ik, params: {kp: 0.3}}
safety:
  max_delta: 0.5
  guards:
    - {type: norm_limit, params: {max_norm: 1.2}}
    - {type: examples.slow_zone, params: {state_key: tip_pos, center: [0.35, 0.2], radius: 0.1, factor: 0.25}}
workers: 8
output_dir: runs

mujoco_mixture

uv run hylo run plugins/examples/configs/mujoco_mixture.yaml
plugins/examples/configs/mujoco_mixture.yaml
# A mixture of two policies, steered: each of 4 seeded samples comes from one member (examples.mixture), and the
# medoid proposal is executed. Members can be any registered policies (reference policies, LeRobot, your own).
schema_version: hylo.experiment/v0.1
name: mujoco-mixture
benchmark: {type: mujoco/reach-v0, split: validation}
env: {type: mujoco.planar_reach}
agent:
  type: steered
  params:
    sampler:
      type: examples.mixture
      params:
        members:
          - {type: examples.ik_reacher, params: {gain: 0.3, noise: 0.05}}
          - {type: examples.ik_reacher, params: {gain: 0.1, noise: 0.3}}
        weights: [0.5, 0.5]
    steering: {n_samples: 4, selector: medoid}
workers: 8
output_dir: runs

mujoco_reach

uv run hylo run plugins/examples/configs/mujoco_reach.yaml
plugins/examples/configs/mujoco_reach.yaml
# MuJoCo planar reach with the analytic IK controller: the experiment the plugin examples build on.
schema_version: hylo.experiment/v0.1
name: mujoco-reach
benchmark: {type: mujoco/reach-v0, split: validation}
env: {type: mujoco.planar_reach}
agent: {type: baseline.planar_ik, params: {kp: 0.3}}
workers: 8
output_dir: runs

Plugin examples: campaigns

Example What it shows
mujoco_online_cem Online learning as a candidate generator: the CEM learner (packages/hylo-plugin-examples) searches the controller gain by rolling out on the search split, seals its result with every rollout run in the provenance, and the proposer turns the best gain into a candidate that the gate judges on unseen init states.
mujoco_steps_gate The gate's statistic is a plugin. Both gains always reach the target, so a success-based gate (McNemar) has no discordant pairs; a sign test on steps (lower is better) promotes the faster one. Vetoes stay on success and safety. Try {type: examples.min_effect, params: {min_effect: 0.1}} from packages/hylo-plugin-examples as well.

mujoco_online_cem

uv run hylo evolve run plugins/examples/evolve/mujoco_online_cem.yaml
plugins/examples/evolve/mujoco_online_cem.yaml
# Online learning as a candidate generator: the CEM learner (packages/hylo-plugin-examples) searches the controller
# gain by rolling out on the search split, seals its result with every rollout run in the provenance, and the
# proposer turns the best gain into a candidate that the gate judges on unseen init states.
schema_version: hylo.evolve/v0.1
name: mujoco-online-cem
experiment: ../configs/mujoco_reach.yaml
experiment_overrides: {benchmark.params: {protocol: rsi}, agent.params.kp: 0.02}
proposer:
  type: learn
  params:
    learner: examples.cem
    online: true
    out_root: runs/evolve/mujoco-online-cem/learned
    params: {space: {kp: {path: params.kp, low: 0.01, high: 1.0, init: 0.05}}, population: 5, iterations: 3}
    patch: {agent.params.kp: "{metrics.best.kp}"}
    hypothesis: "a gain of {metrics.best.kp} found by CEM on the search split reaches more targets in time"
cycles: 1
out_dir: runs/evolve/mujoco-online-cem

mujoco_steps_gate

uv run hylo evolve run plugins/examples/evolve/mujoco_steps_gate.yaml
plugins/examples/evolve/mujoco_steps_gate.yaml
# The gate's statistic is a plugin. Both gains always reach the target, so a success-based gate (McNemar) has no
# discordant pairs; a sign test on steps (lower is better) promotes the faster one. Vetoes stay on success and
# safety. Try {type: examples.min_effect, params: {min_effect: 0.1}} from packages/hylo-plugin-examples as well.
schema_version: hylo.evolve/v0.1
name: mujoco-steps-gate
experiment: ../configs/mujoco_reach.yaml
experiment_overrides: {benchmark.params: {protocol: rsi}, agent.params.kp: 0.1}
proposer: {type: rule.gain, params: {path: agent.params.kp, factors: [2.0]}}
protocol: {gate_test: {type: sign, params: {metric: steps, direction: lower}}}
cycles: 1
out_dir: runs/evolve/mujoco-steps-gate

Scripts

Script What it does
bench/examples/policies/compare.py Every reference policy family on MuJoCo planar reach: demonstrations -> learners -> scorecards -> leaderboard.
bench/examples/rsi_demo.py End-to-end offline improvement loop on MuJoCo planar reach.