PettingZoo Wrappers¶
PettingZoo includes the following types of wrappers:
Conversion Wrappers: wrappers for converting environments between the AEC and Parallel APIs
Utility Wrappers: a set of wrappers which provide convenient reusable logic, such as enforcing turn order or clipping out-of-bounds actions.
Conversion wrappers¶
AEC to Parallel¶
- pettingzoo.utils.conversions.aec_to_parallel(aec_env: AECEnv[AgentID, ObsType, ActionType]) ParallelEnv[AgentID, ObsType, ActionType][source]¶
Converts an AEC environment to a Parallel environment.
In the case of an existing Parallel environment wrapped using a parallel_to_aec_wrapper, this function will return the original Parallel environment. Otherwise, it will apply the aec_to_parallel_wrapper to convert the environment.
An environment can be converted from an AEC environment to a parallel environment with the aec_to_parallel wrapper shown below. Note that this wrapper makes the following assumptions about the underlying environment:
The environment steps in a cycle, i.e. it steps through every live agent in order.
The environment does not update the observations of the agents except at the end of a cycle.
Most parallel environments in PettingZoo only allocate rewards at the end of a cycle. In these environments, the reward scheme of the AEC API an the parallel API is equivalent. If an AEC environment does allocate rewards within a cycle, then the rewards will be allocated at different timesteps in the AEC environment an the Parallel environment. In particular, the AEC environment will allocate all rewards from one time the agent steps to the next time, while the Parallel environment will allocate all rewards from when the first agent stepped to the last agent stepped.
To convert an AEC environment into a parallel environment:
from pettingzoo import make
from pettingzoo.utils.conversions import aec_to_parallel
env = make("aec", "butterfly/pistonball-v6")
env = aec_to_parallel(env)
Parallel to AEC¶
- pettingzoo.utils.conversions.parallel_to_aec(par_env: ParallelEnv[AgentID, ObsType, ActionType | None]) AECEnv[AgentID, ObsType, ActionType | None][source]¶
Converts a Parallel environment to an AEC environment.
In the case of an existing AEC environment wrapped using a aec_to_parallel_wrapper, this function will return the original AEC environment. Otherwise, it will apply the parallel_to_aec_wrapper to convert the environment.
Any parallel environment can be efficiently converted to an AEC environment with the parallel_to_aec wrapper.
To convert a parallel environment into an AEC environment:
from pettingzoo import make
from pettingzoo.utils import parallel_to_aec
env = make("parallel", "butterfly/pistonball-v6")
env = parallel_to_aec(env)
Utility Wrappers¶
We wanted our pettingzoo environments to be both easy to use and easy to implement. To combine these, we have a set of simple wrappers which provide input validation and other convenient reusable logic.
You can apply these wrappers to your environment in a similar manner to the below examples:
To wrap an AEC environment:
from pettingzoo import make
from pettingzoo.utils import TerminateIllegalWrapper
env = make("aec", "classic/tictactoe-v3")
env = TerminateIllegalWrapper(env, illegal_reward=-1)
env.reset()
for agent in env.agent_iter():
observation, reward, termination, truncation, info = env.last()
if termination or truncation:
action = None
else:
action = env.action_space(agent).sample() # this is where you would insert your policy
env.step(action)
env.close()
Note: Most AEC environments include TerminateIllegalWrapper in their initialization, so this code does not change the environment’s behavior.
To wrap a Parallel environment.
from pettingzoo import make
from pettingzoo.utils import BaseParallelWrapper
parallel_env = make("parallel", "butterfly/pistonball-v6", render_mode="human")
parallel_env = BaseParallelWrapper(parallel_env)
observations, infos = parallel_env.reset()
while parallel_env.agents:
actions = {agent: parallel_env.action_space(agent).sample() for agent in parallel_env.agents} # this is where you would insert your policy
observations, rewards, terminations, truncations, infos = parallel_env.step(actions)
Note
Wrappers are specific to either the AEC or Parallel API unless documented
otherwise. Parallel variants include Parallel in their name, such as
AgentIndicatorParallelV1. To apply an AEC-only wrapper to a Parallel
environment, convert it to AEC, apply the wrapper, and convert it back.
from pettingzoo import make
from pettingzoo.utils import ClipOutOfBoundsWrapper
from pettingzoo.utils import aec_to_parallel
parallel_env = make("aec", "sisl/multiwalker-v9", render_mode="human")
parallel_env = ClipOutOfBoundsWrapper(parallel_env)
parallel_env = aec_to_parallel(parallel_env)
observations, infos = parallel_env.reset()
while parallel_env.agents:
actions = {agent: parallel_env.action_space(agent).sample() for agent in parallel_env.agents} # this is where you would insert your policy
observations, rewards, terminations, truncations, infos = parallel_env.step(actions)
- class pettingzoo.utils.wrappers.BaseWrapper(env: AECEnv[AgentID, ObsType, ActionType])[source]¶
Creates a wrapper around env parameter.
All AECEnv wrappers should inherit from this base class
- class pettingzoo.utils.wrappers.TerminateIllegalWrapper(env: AECEnv[AgentID, ObsType, ActionType], illegal_reward: float)[source]¶
This wrapper terminates the game with the current player losing in case of illegal values.
- Parameters:
illegal_reward – number that is the value of the player making an illegal move.
- class pettingzoo.utils.wrappers.CaptureStdoutWrapper(env: AECEnv[AgentID, ObsType, ActionType])[source]¶
Takes an environment which prints to terminal, and gives it an ansi render mode where it captures the terminal output and returns it as a string instead.
- class pettingzoo.utils.wrappers.AssertOutOfBoundsWrapper(env: AECEnv[AgentID, ObsType, ActionType])[source]¶
Asserts if the action given to step is outside of the action space.
- class pettingzoo.utils.wrappers.ClipOutOfBoundsWrapper(env: AECEnv[Any, Any, Any])[source]¶
Clips the input action to fit in the continuous action space (emitting a warning if it does so).
Applied to continuous environments in pettingzoo.
- class pettingzoo.utils.wrappers.ClipRewardV1(env: AECEnv[AgentID, ObsType, ActionType], min_reward: float = -1, max_reward: float = 1)[source]¶
Clips each agent’s per-step reward into
[min_reward, max_reward].After every
resetandstepthe innerrewardsdict is clipped and the running total is rebuilt from that, solast()reports the sum of clipped steps. That is SuperSuit’sclip_reward_v0.- Parameters:
env – The AEC environment to wrap.
min_reward – Lower bound, inclusive. Default
-1.max_reward – Upper bound, inclusive. Default
1.
- class pettingzoo.utils.wrappers.ClipRewardParallelV1(env: ParallelEnv[AgentID, ObsType, ActionType], min_reward: float = -1, max_reward: float = 1)[source]¶
Clips each agent’s per-step reward into
[min_reward, max_reward].SuperSuit’s
clip_reward_v0has no Parallel implementation of its own. This one clips the reward dict thatstep()returns.- Parameters:
env – The parallel environment to wrap.
min_reward – Lower bound, inclusive. Default
-1.max_reward – Upper bound, inclusive. Default
1.
- class pettingzoo.utils.wrappers.OrderEnforcingWrapper(env: AECEnv[AgentID, ObsType, ActionType])[source]¶
Checks if function calls or attribute access are in a disallowed order.
The following are raised: * AttributeError if any of the following are accessed before reset():
rewards, terminations, truncations, infos, agent_selection, num_agents, agents.
An error if any of the following are called before reset: render(), step(), observe(), state(), agent_iter()
A warning if step() is called when there are no agents remaining.
- class pettingzoo.utils.wrappers.AgentIndicatorV1(env: AECEnv[AgentID, ObsType, ActionType], type_only: bool = False)[source]¶
Adds an agent indicator to each observation.
With
type_only=True, agents named<type>_<n>share an indicator for their type.- Parameters:
env – The AEC environment to wrap.
type_only – Whether to indicate agent types instead of individual agents.
- class pettingzoo.utils.wrappers.AgentIndicatorParallelV1(env: ParallelEnv[AgentID, ObsType, ActionType], type_only: bool = False)[source]¶
Adds an agent indicator to each observation.
With
type_only=True, agents named<type>_<n>share an indicator for their type.- Parameters:
env – The parallel environment to wrap.
type_only – Whether to indicate agent types instead of individual agents.
- class pettingzoo.utils.wrappers.ColorReductionObservationV1(env: AECEnv[AgentID, ObsType, ActionType], mode: str = 'full')[source]¶
Reduces an image observation to a single channel.
"full"converts to grayscale with the luminance weights [0.299, 0.587, 0.114] and returns uint8 whatever the input dtype was."R","G"and"B"take the named channel and keep the input dtype. Either way the trailing channel axis is dropped, so an (H, W, 3) observation becomes (H, W).- Parameters:
env – The AEC environment to wrap.
mode – One of “full”, “R”, “G”, “B”.
- class pettingzoo.utils.wrappers.ColorReductionObservationParallelV1(env: ParallelEnv[AgentID, ObsType, ActionType], mode: str = 'full')[source]¶
Reduces an image observation to a single channel.
"full"converts to grayscale with the luminance weights [0.299, 0.587, 0.114] and returns uint8 whatever the input dtype was."R","G"and"B"take the named channel and keep the input dtype. Either way the trailing channel axis is dropped, so an (H, W, 3) observation becomes (H, W).- Parameters:
env – The parallel environment to wrap.
mode – One of “full”, “R”, “G”, “B”.
- class pettingzoo.utils.wrappers.DtypeObservationV1(env: AECEnv[AgentID, ObsType, ActionType], dtype: Any)[source]¶
Recasts each agent’s observation to
dtype.The observation space becomes a Box with the same shape and the new dtype, with the old bounds put through the same cast. Every space in
possible_agentsis built and checked when the wrapper is created.Bounds that do not fit the new dtype wrap around, and casting an infinite bound to an integer dtype is undefined in numpy, so an unbounded Box cast to an integer dtype collapses to a single point that will not contain the observations. Both match SuperSuit’s
dtype_v0.- Parameters:
env – The AEC environment to wrap.
dtype – Anything
numpy.dtypeaccepts, e.g.np.float32.
- class pettingzoo.utils.wrappers.DtypeObservationParallelV1(env: ParallelEnv[AgentID, ObsType, ActionType], dtype: Any)[source]¶
Recasts each agent’s observation to
dtype.The observation space becomes a Box with the same shape and the new dtype, with the old bounds put through the same cast. Every space in
possible_agentsis built and checked when the wrapper is created.Bounds that do not fit the new dtype wrap around, and casting an infinite bound to an integer dtype is undefined in numpy, so an unbounded Box cast to an integer dtype collapses to a single point that will not contain the observations. Both match SuperSuit’s
dtype_v0.- Parameters:
env – The parallel environment to wrap.
dtype – Anything
numpy.dtypeaccepts, e.g.np.float32.
- class pettingzoo.utils.wrappers.MaxObservationV1(env: AECEnv[AgentID, ObsType, ActionType], memory: int)[source]¶
Replaces each agent’s observation with the elementwise max over its last
memoryobservations.This removes the flicker in environments that draw sprites on alternate frames. Histories are per agent and cleared on
reset, and the observation space is unchanged. Only array observation spaces work: Box, MultiBinary, MultiDiscrete. Ported from SuperSuit’s max_observation_v0; the version suffix continues that numbering.- Parameters:
env – The AEC environment to wrap.
memory – How many observations to take the max over. Must be a positive int.
- class pettingzoo.utils.wrappers.MaxObservationParallelV1(env: ParallelEnv[AgentID, ObsType, ActionType], memory: int)[source]¶
Replaces each agent’s observation with the elementwise max over its last
memoryobservations.This removes the flicker in environments that draw sprites on alternate frames. Histories are per agent and cleared on
reset, and the observation space is unchanged. Only array observation spaces work: Box, MultiBinary, MultiDiscrete. Ported from SuperSuit’s max_observation_v0; the version suffix continues that numbering.- Parameters:
env – The parallel environment to wrap.
memory – How many observations to take the max over. Must be a positive int.
- class pettingzoo.utils.wrappers.PadObservationsV1(env: AECEnv[AgentID, ObsType, ActionType])[source]¶
Pads each agent’s observation up to one shared observation space.
The shared space covers the observation spaces of
possible_agents. For Box spaces it takes the largest size along each axis, and each observation is padded with zeros at the end of every axis, so the real values stay at the front. For Discrete spaces it takes the range covering all of them and observations pass through unchanged. Every agent then reports that same space.Box spaces have to agree on dtype and number of dimensions. Anything other than Box and Discrete is rejected.
An agent whose observation is already the full shape gets the array back without a copy, so writing into it writes into the environment.
Ported from SuperSuit’s pad_observations_v0; the version suffix continues that numbering.
- Parameters:
env – The AEC environment to wrap.
- class pettingzoo.utils.wrappers.PadObservationsParallelV1(env: ParallelEnv[AgentID, ObsType, ActionType])[source]¶
Pads each agent’s observation up to one shared observation space.
The shared space covers the observation spaces of
possible_agents. For Box spaces it takes the largest size along each axis, and each observation is padded with zeros at the end of every axis, so the real values stay at the front. For Discrete spaces it takes the range covering all of them and observations pass through unchanged. Every agent then reports that same space.Box spaces have to agree on dtype and number of dimensions. Anything other than Box and Discrete is rejected.
An agent whose observation is already the full shape gets the array back without a copy, so writing into it writes into the environment.
Ported from SuperSuit’s pad_observations_v0; the version suffix continues that numbering.
- Parameters:
env – The parallel environment to wrap.
- class pettingzoo.utils.wrappers.RescaleObservationV1(env: AECEnv[AgentID, ObsType, ActionType], min_obs: float = 0.0, max_obs: float = 1.0)[source]¶
Linearly rescales each agent’s Box observation into a fixed range.
Every element is mapped from its own
[low, high]in the wrapped space onto[min_obs, max_obs], and the result is clipped so it always sits inside the advertised space. The wrapped space has to be a float Box whose elements all have finitelowandhighwithhigh > low, since anything else gives nothing to scale from. Every space inpossible_agentsis checked when the wrapper is built.Ported from SuperSuit’s normalize_obs_v0; the version suffix continues that numbering.
- Parameters:
env – The AEC environment to wrap.
min_obs – Lower bound of the rescaled observations.
max_obs – Upper bound of the rescaled observations.
- class pettingzoo.utils.wrappers.RescaleObservationParallelV1(env: ParallelEnv[AgentID, ObsType, ActionType], min_obs: float = 0.0, max_obs: float = 1.0)[source]¶
Linearly rescales each agent’s Box observation into a fixed range.
Every element is mapped from its own
[low, high]in the wrapped space onto[min_obs, max_obs], and the result is clipped so it always sits inside the advertised space. The wrapped space has to be a float Box whose elements all have finitelowandhighwithhigh > low, since anything else gives nothing to scale from. Every space inpossible_agentsis checked when the wrapper is built.Ported from SuperSuit’s normalize_obs_v0; the version suffix continues that numbering.
- Parameters:
env – The parallel environment to wrap.
min_obs – Lower bound of the rescaled observations.
max_obs – Upper bound of the rescaled observations.
- class pettingzoo.utils.wrappers.ReshapeObservationV1(env: AECEnv[AgentID, ObsType, ActionType], shape: tuple[int, ...])[source]¶
Reshapes each agent’s observation to the given shape.
The observation space is reshaped the same way, so per-element bounds are kept. Only works on Box observation spaces.
- Parameters:
env – The AEC environment to wrap.
shape – The new observation shape. Must have as many elements as the old one.
- class pettingzoo.utils.wrappers.ReshapeObservationParallelV1(env: ParallelEnv[AgentID, ObsType, ActionType], shape: tuple[int, ...])[source]¶
Reshapes each agent’s observation to the given shape.
The observation space is reshaped the same way, so per-element bounds are kept. Only works on Box observation spaces.
- Parameters:
env – The parallel environment to wrap.
shape – The new observation shape. Must have as many elements as the old one.
- class pettingzoo.utils.wrappers.ScaleActionV1(env: AECEnv[AgentID, ObsType, ActionType], scale: float)[source]¶
Scales the bounds of each agent’s Box action space by a constant factor.
The action space this wrapper advertises has
lowandhighmultiplied byscale. Actions are divided byscalebefore they reach the wrapped environment, so an action drawn from the advertised space is a valid action for the wrapped environment. Actions on a bound of the advertised space are clipped to the wrapped bound, since the round trip throughscaleis not exact in floating point. An action outside the advertised space is passed through as is.Scaling an integer Box narrows what can be reached: at
scale0.5, aBox(0, 10, dtype=int64)advertisesBox(0, 5)and only even actions are reachable. A bound large enough thatbound * scaleoverflows the dtype becomes infinite, and actions there stay infinite.- Parameters:
env – The AEC environment to wrap.
scale – Non-zero factor applied to the action space bounds. A negative scale flips the bounds, so they are swapped to keep the Box valid.
- class pettingzoo.utils.wrappers.ScaleActionParallelV1(env: ParallelEnv[AgentID, ObsType, ActionType], scale: float)[source]¶
Scales the bounds of each agent’s Box action space by a constant factor.
The action space this wrapper advertises has
lowandhighmultiplied byscale. Actions are divided byscalebefore they reach the wrapped environment, so an action drawn from the advertised space is a valid action for the wrapped environment. Actions on a bound of the advertised space are clipped to the wrapped bound, since the round trip throughscaleis not exact in floating point. An action outside the advertised space is passed through as is.Scaling an integer Box narrows what can be reached: at
scale0.5, aBox(0, 10, dtype=int64)advertisesBox(0, 5)and only even actions are reachable. A bound large enough thatbound * scaleoverflows the dtype becomes infinite, and actions there stay infinite.- Parameters:
env – The parallel environment to wrap.
scale – Non-zero factor applied to the action space bounds. A negative scale flips the bounds, so they are swapped to keep the Box valid.
- class pettingzoo.utils.wrappers.StickyActionV1(env: AECEnv[AgentID, ObsType, ActionType], repeat_action_probability: float)[source]¶
Repeats an agent’s previous action with probability
repeat_action_probability.Each agent keeps its own previous action, and an agent has nothing to repeat until it has acted once since the last
reset(), so the first action after a reset is always passed through. Only this wrapper’s own reset clears the memory: an environment that auto-resets underneath it, such asMultiEpisodeParallelEnvon the inside, can still have the first action of a later episode repeated. The remembered action is a deep copy, so reusing an action buffer between steps does not defeat the wrapper.Seeding follows the usual PettingZoo pattern:
reset()reseeds from the seed it is given, which meansreset(seed=None)starts a fresh unseeded stream. Pass a seed on every reset if you want a whole multi-episode run to be reproducible.- Parameters:
env – The AEC environment to wrap.
repeat_action_probability – Probability of using the previous action, in the interval [0, 1).
- class pettingzoo.utils.wrappers.StickyActionParallelV1(env: ParallelEnv[AgentID, ObsType, ActionType], repeat_action_probability: float)[source]¶
Repeats an agent’s previous action with probability
repeat_action_probability.Each agent keeps its own previous action, and an agent has nothing to repeat until it has acted once since the last
reset(), so the first action after a reset is always passed through. Only this wrapper’s own reset clears the memory: an environment that auto-resets underneath it, such asMultiEpisodeParallelEnvon the inside, can still have the first action of a later episode repeated. The remembered action is a deep copy, so reusing an action buffer between steps does not defeat the wrapper.Seeding follows the usual PettingZoo pattern:
reset()reseeds from the seed it is given, which meansreset(seed=None)starts a fresh unseeded stream. Pass a seed on every reset if you want a whole multi-episode run to be reproducible.- Parameters:
env – The parallel environment to wrap.
repeat_action_probability – Probability of using the previous action, in the interval [0, 1).