Neuroevolution and MoRobtrol#
MoRobtrol evaluates PyTorch policies on multiple control objectives using Brax (the default) or Playground/MJX environments. Decision variables are network parameters, and outputs are objective rewards for each policy. This chapter requires optional dependencies; numerical optimization does not require a simulator.
Parameter vectors and policy inputs#
evox.utils.ParamsAndVector converts network parameter vectors to parameter dictionaries
with a population dimension. The algorithm searches parameter vectors, and the workflow
passes them to MoRobtrol through solution_transform=adapter. Rewards are maximized, so use
opt_direction="max". The algorithm’s internal fit contains negated rewards.
Brax example#
"""Search a small MoSwimmer policy with the optional Brax engine."""
import jax
import torch
from brax import envs
from evox.utils import ParamsAndVector
from evomo.algorithms import NSGA2
from evomo.problems.neuroevolution import MoRobtrol
from evomo.workflows import UnifiedWorkflow
def main():
jax_gpu = any(device.platform == "gpu" for device in jax.devices())
device = "cuda" if torch.cuda.is_available() and jax_gpu else "cpu"
torch.set_default_device(device)
torch.manual_seed(42)
env = envs.get_environment("mo_swimmer")
policy = torch.nn.Sequential(
torch.nn.Linear(env.observation_size, 16),
torch.nn.Tanh(),
torch.nn.Linear(16, env.action_size),
torch.nn.Tanh(),
).to(device)
adapter = ParamsAndVector(dummy_model=policy)
center = adapter.to_vector(dict(policy.named_parameters()))
population_size = 20
problem = MoRobtrol(
policy=policy,
env_name="mo_swimmer",
max_episode_length=30,
num_episodes=2,
seed=42,
pop_size=population_size,
rotate_key=False,
device=device,
)
algorithm = NSGA2(
pop_size=population_size,
n_objs=problem.num_obj,
lb=torch.full_like(center, -1),
ub=torch.full_like(center, 1),
device=device,
)
workflow = UnifiedWorkflow(algorithm, problem, opt_direction="max", solution_transform=adapter, device=device)
workflow.init_step()
for _ in range(3):
workflow.step()
print("Objective rewards:")
print(-workflow.algorithm.fit)
if __name__ == "__main__":
main()
python -m pip install "evomo[neuroevolution]"
python docs/examples/morobtrol.py
This script uses short episodes and a few generations to demonstrate the interface; it is not expected to produce a trained control policy. It uses CPU if either Torch or JAX lacks a usable GPU. PyTorch CUDA visibility does not mean JAX uses the same device; check both.
MoRobtrol’s pop_size must match the number of policies passed to each evaluation. This example
uses NSGA-II with a fixed population size. If switching to an algorithm that adjusts population
size, read the actual size before constructing the problem and verify that each evaluation
still passes the expected number of policies.
Using Playground#
After installing evomo[playground], query task metadata and construct a network with matching
dimensions:
from evomo.problems.neuroevolution import MoRobtrol
info = MoRobtrol.playground_task_info("CartpoleBalance")
policy = torch.nn.Sequential(
torch.nn.Linear(info["observation_size"], info["action_size"]),
torch.nn.Tanh(),
).to(device)
problem = MoRobtrol(
policy=policy,
env_name="CartpoleBalance",
engine="playground",
max_episode_length=100,
num_episodes=2,
pop_size=20,
seed=42,
device=device,
)
Use MoRobtrol.available_playground_tasks() to inspect tasks available in the installed
runtime. Native task assets may be downloaded on first use. Use the same env_config,
observation_key, and objectives when querying metadata and constructing the problem.
After changing the network, also recreate the parameter adapter, bounds, and algorithm.
Read n_objs from problem.num_obj.
Randomness and observation normalization#
Parameter |
Current behavior |
|---|---|
|
Initializes simulator randomness |
|
Updates the random key on each evaluation |
|
Reuses episode seeds; changing normalization state may still affect results |
|
Episodes per candidate policy |
|
Aggregates rewards across episodes; defaults to |
|
Historical name for disabling observation normalization, the default |
|
Enables shared running observation statistics and normalization |
|
|
num_obj and observation_shape are inferred from the environment by default. Explicit values
must match it. Rewards include the last valid terminal transition, and candidates share episode seeds.
Rewards from different physics engines or backends are not necessarily interchangeable.
Validate cross-engine comparisons separately. MoRobtrol’s outer vmap supports batches of
policy parameters, not independent problem states or nested HPO repetitions. The combined
policy count must match pop_size.