Skip to content

Variable Morphology

Use this guide when candidate bodies change, rather than only controller parameters. Read Evolutionary Search for the evaluation contract. The sections below explain which build path supports each kind of body change.

jax-evogym supports two mechanisms for varying robot morphology:

  1. robot_override — swap the robot’s body within a fixed template frame
  2. Dense grid pipeline — build simulation data from scratch in pure JAX, vmap-able across populations

Compile a template once, then instantiate it with different morphologies:

import numpy as np
from jax_evogym import (
EvoWorld, FIXED, H_ACT, SOFT, V_ACT, CONTRACTILE, EMPTY, RIGID,
compile_world_template, instantiate_world,
)
# Compile once
world = EvoWorld()
world.add_from_array("ground", np.array([[FIXED, FIXED, FIXED, FIXED]]), 0, 0)
world.add_from_array("robot", np.array([[H_ACT, SOFT, V_ACT]]), 1, 2)
template = compile_world_template(world, robot_name="robot")
# Instantiate with different bodies
dense = np.array([[H_ACT, SOFT, V_ACT]])
sparse = np.array([[H_ACT, EMPTY, V_ACT]])
dense_built = instantiate_world(template, robot_override=dense)
sparse_built = instantiate_world(template, robot_override=sparse)
  • Override shape must match the robot template grid exactly
  • Same array orientation as add_from_array
  • EMPTY disables cells within the frame
  • Any dynamic voxel type is allowed (RIGID, SOFT, H_ACT, V_ACT, CONTRACTILE)
  • Slope values are rejected
  • Terrain objects are never affected

instantiate_world does not auto-mirror robot_override. Pass the override explicitly to each template:

from jax_evogym import compile_world_templates
template_set = compile_world_templates(world, robot_name="robot", mirror_mode="paired")
built_primary = instantiate_world(template_set.primary, robot_override=dense)
built_mirror = instantiate_world(template_set.mirror, robot_override=dense)

If you want a truly mirrored body, construct the mirrored array yourself.

For evolutionary search where you need to evaluate thousands of morphologies in parallel, the dense grid pipeline provides pure JAX, vmap-able construction:

import jax.numpy as jnp
from jax_evogym import EMPTY, H_ACT, SOFT, V_ACT
from jax_evogym import precompute_grid, jax_build_sim_state, jax_build_collision_data
# Dense grids use y-up rows. This example contains only a robot.
body_array = jnp.array([[H_ACT, SOFT, V_ACT]], dtype=jnp.int32)
grid_data = precompute_grid(H=1, W=3)
robot_mask = body_array != EMPTY
sim_state, topology = jax_build_sim_state(body_array, grid_data)
collision_data = jax_build_collision_data(body_array, robot_mask, grid_data)

See Dense Grid Pipeline for the full guide.

ScenarioUse
Standard environment usageBuilt-in environments (handle everything internally)
Fixed terrain, varying robot bodyrobot_override on a compiled template
Terrain with multiple objectsObject-separated build (compile_world_template)
Population-level morphology search with vmapDense grid pipeline
Custom terrain + custom morphologyBuild the EvoWorld programmatically, compile, instantiate