marlbench
Setting up MARL environments can take a surprising amount of time. Each one has its own API, and using more than one environment in the same codebase usually means writing additional code to provide a common API.
marlbench does this work for you. It provides a common interface for several MARL environments, together with environment wrappers and vectorized environments.
This repository contains environment tools only. It does not include MARL algorithms.
What is included?
| Part | Available components |
|---|---|
| Environments | LBF, RWARE, SMAClite, PettingZoo, MaMuJoCo, MAgent2, SMAC, SMACv2 |
| Observation wrappers | Transform observations, normalize observations, add agent IDs |
| Reward wrappers | Transform rewards, clip rewards, normalize rewards |
| Other wrappers | Time limits |
| Vector environments | Run multiple environments sequentially or in separate processes |
Supported environments
| Environment | Interface | Action space | Installation |
|---|---|---|---|
| Level-Based Foraging | LBFInterface |
Discrete | pip install lbforaging |
| Multi-Robot Warehouse | RWAREInterface |
Discrete | pip install rware |
| SMAClite | SMACliteInterface |
Discrete | Install it from its GitHub repository |
| PettingZoo | PettingZooInterface |
Discrete or continuous | pip install pettingzoo and install the extra dependencies for the family you use |
| MaMuJoCo | MAmujocoInterface |
Continuous | pip install gymnasium-robotics |
| MAgent2 | MAgent2Interface |
Discrete | pip install magent2 |
| SMAC | SMACInterface |
Discrete | Follow the instructions in the SMAC repository |
| SMACv2 | SMACv2Interface |
Discrete | Follow the instructions in the SMACv2 repository |
The SMACv2 scenario configuration files are already included in marlbench/configs/smacv2/.
The supported MAgent2 environments are: adversarial_pursuit_v4,battle_v4, battlefield_v5, combined_arms_v6, gather_v5,tiger_deer_v4.
Add marlbench to your project
Copy the marlbench folder from this repository into your own project. ( using git clone).
Your project should then look similar to this:
your_project/
├── train.py
├── algorithms/
└── marlbench/
├── configs/
├── interfaces/
├── vec_envs/
└── wrappers/
The tests, README.md, .gitignore, and pyproject.toml files belong to this repository. You do not need to copy them into your project.
Common API
All interfaces provide the following methods:
obs, info = env.reset(seed=42)
obs, reward, terminated, truncated, info = env.step(actions)
state = env.get_state()
avail_actions = env.get_avail_actions()
agent_mask = env.get_agent_mask() # which agents are active
obs_size = env.get_obs_size()
state_size = env.get_state_size()
action_size = env.get_action_size()
actions = env.sample()
env.close()
Example
The following example runs one LBF episode:
from marlbench.interfaces.lbf import LBFInterface
env = LBFInterface(
env_name="Foraging-2s-10x10-3p-3f-coop-v3",
max_episode_steps=150,
reward_aggr="sum",
disable_env_checker=True,
)
obs, info = env.reset()
done = False
while not done:
actions = env.sample()
obs, reward, terminated, truncated, info = env.step(actions)
state = env.get_state()
avail_actions = env.get_avail_actions()
agent_mask = env.get_agent_mask()
done = terminated or truncated
env.close()
Other environments can be created in the same way:
from marlbench.interfaces.magent import MAgent2Interface
from marlbench.interfaces.mamujoco import MAmujocoInterface
from marlbench.interfaces.pz import PettingZooInterface
from marlbench.interfaces.rware import RWAREInterface
from marlbench.interfaces.smac import SMACInterface
from marlbench.interfaces.smaclite import SMACliteInterface
from marlbench.interfaces.smacv2 import SMACv2Interface
rware_env = RWAREInterface(
env_name="rware-tiny-2ag-v2",
reward_aggr="sum",
)
smaclite_env = SMACliteInterface(env_name="MMM2")
pettingzoo_env = PettingZooInterface(env_name="pursuit_v4",family="sisl")
mamujoco_env = MAmujocoInterface(env_name="Ant-p2x4")
magent_env = MAgent2Interface(env_name="adversarial_pursuit_v4")
smac_env = SMACInterface(env_name="3m")
smacv2_env = SMACv2Interface(env_name="terran_5_vs_5")
Wrappers
The following wrappers are available:
| Wrapper | Description |
|---|---|
TimeLimit |
Truncates an episode after a fixed number of steps |
TransformObservation |
Applies a function to observations and state, such as clipping |
NormalizeObservation |
Normalizes observations and optionally the global state |
AddAgentID |
Adds a one-hot agent ID to each observation |
TransformReward |
Applies a function to rewards, such as clipping |
NormalizeReward |
Normalizes shared or individual rewards |
Wrappers can be combined:
import numpy as np
from marlbench.interfaces.lbf import LBFInterface
from marlbench.wrappers.obs_wrappers import AddAgentID, NormalizeObservation
from marlbench.wrappers.reward_wrappers import NormalizeReward, TransformReward
from marlbench.wrappers.common import TimeLimit
env = LBFInterface(
env_name="Foraging-2s-10x10-3p-3f-coop-v3",
reward_aggr="none",
)
env = TimeLimit(env, max_episode_steps=150)
env = NormalizeObservation(env, normalize_state=True)
env = AddAgentID(env)
env = TransformReward(env, lambda reward: np.clip(reward, -1.0, 1.0))
env = NormalizeReward(env, gamma=0.99)
The order matters. Observations are normalized before the agent IDs are added, so the IDs remain zero or one. Rewards are clipped before they are normalized.
Vector environments
Two vector environment classes are available:
| Class | Description |
|---|---|
SyncVectorEnv |
Runs multiple environments sequentially |
SubprocVectorEnv |
Runs each environment in a separate process |
Both classes receive a list of functions that create environments.
SyncVectorEnv
from marlbench.interfaces.lbf import LBFInterface
from marlbench.vec_envs.sync_vec import SyncVectorEnv
N_ENVS = 4
def make_lbf():
return LBFInterface(
env_name="Foraging-2s-10x10-3p-3f-coop-v3",
max_episode_steps=150,
reward_aggr="sum")
env_fns = [make_lbf for _ in range(N_ENVS)]
env = SyncVectorEnv(env_fns,auto_reset=False)
observations, infos = env.reset(seed=42)
observations, rewards, dones, truncated, infos = env.step(env.sample())
env.close()
SubprocVectorEnv
SubprocVectorEnv uses the same API:
from marlbench.vec_envs.subproc_vec import SubprocVectorEnv
N_ENVS = 4
def make_lbf():
return LBFInterface(
env_name="Foraging-2s-10x10-3p-3f-coop-v3",
max_episode_steps=150,
reward_aggr="sum")
if __name__ == "__main__":
env_fns = [make_lbf for _ in range(N_ENVS)]
env = SubprocVectorEnv(
env_fns,
start_method="spawn",
auto_reset=False)
observations, infos = env.reset(seed=42)
observations, rewards, dones, truncated, infos = env.step(env.sample())
env.close()
Every returned array has n_envs as its first dimension:
| Value | Shape |
|---|---|
observations |
(n_envs, n_agents, obs_size) |
rewards |
(n_envs,) shared, (n_envs, n_agents) individual |
dones, truncated |
(n_envs,) |
get_state() |
(n_envs, state_size) |
get_avail_actions() |
(n_envs, n_agents, action_size) |
get_agent_mask() |
(n_envs, n_agents) |
get_env_mask() |
(n_envs,) |
infos |
list of n_envs dicts |
How to handle episodes with different lengths
Episodes do not end at the same time. With parallel environments running, one may terminate before the others.
When auto_reset=False, an environment that returns done or truncated becomes inactive. It is not stepped again: it keeps returning its final observation with a reward of zero until you reset it. env.get_env_mask() tells you which environments are still running. You can use reset(indices=...) to reset a subset of environment. It accepts an integer, a list of integers, or a boolean mask of shape (n_envs,).
In constrast auto_reset=True automatically resets a finished environment inside step(), and the observation returned by step() then belongs to the new episode. The terminal data is moved into that environment's info dict. It can be accessed using final_obs,final_state, final_avail_actions, final_agent_mask,final_info. dones, truncated and rewards still describe the step that ended the episode, so episode statistics keep working.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file marlbench-0.0.1.tar.gz.
File metadata
- Download URL: marlbench-0.0.1.tar.gz
- Upload date:
- Size: 9.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.9.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bd0b09a84beb4dbd90f75ad9d39fc52107757537607c49b13b80ed5d5e10f2f5
|
|
| MD5 |
6000e4df75f263de2bb7b38223bdc663
|
|
| BLAKE2b-256 |
34757b8a3f43e6f9d8e8ea327c3d9d63aa6634c9029e2ec395c341a94ba3c8f7
|
File details
Details for the file marlbench-0.0.1-py3-none-any.whl.
File metadata
- Download URL: marlbench-0.0.1-py3-none-any.whl
- Upload date:
- Size: 5.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.9.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
602264309f167585045e4d851da6576a3c81900400b1f81038b5c12ace3629b0
|
|
| MD5 |
60b87812830dfbafb039028c7b76c71f
|
|
| BLAKE2b-256 |
9f576ffbfae5c42bd24a64055c861caa4dd90c413997e27d20f6ed46cbc44b19
|