Skip to content

Commit 54f2135

Browse files
authored
Merge pull request #519 from LLNL/abmarl-9-incoming-outgoing-wrappers
Abmarl 9 incoming outgoing wrappers
2 parents 0a41cba + bbcdd56 commit 54f2135

File tree

6 files changed

+511
-4
lines changed

6 files changed

+511
-4
lines changed

.github/workflows/lint.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
runs-on: ubuntu-latest
1515
strategy:
1616
matrix:
17-
python-version: ["3.7", "3.8", "3.9", "3.10"]
17+
python-version: ["3.8", "3.9", "3.10", "3.11"]
1818

1919
steps:
2020
- uses: actions/checkout@v3

abmarl/external/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
from .gym_env_wrapper import GymWrapper
2-
from .rllib_multiagentenv_wrapper import MultiAgentWrapper
1+
from .gym_env_wrapper import GymWrapper, gym_to_abmarl
2+
from .rllib_multiagentenv_wrapper import MultiAgentWrapper, multi_agent_to_abmarl
33
from .open_spiel_env_wrapper import OpenSpielWrapper

abmarl/external/gym_env_wrapper.py

+100
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
12
from gymnasium import Env as GymEnv
23

34
from abmarl.sim import is_agent
5+
from abmarl.sim import Agent, AgentBasedSimulation
46

57

68
class GymWrapper(GymEnv):
@@ -69,3 +71,101 @@ def render(self, **kwargs):
6971
Forward render calls to the composed simulation.
7072
"""
7173
self.sim.render(**kwargs)
74+
75+
76+
class GymABS(AgentBasedSimulation):
77+
"""
78+
Wraps a GymEnv and leverages it for implementing the ABS interface.
79+
80+
Args:
81+
gym_env: The GymEnv to convert to an AgentBasedSimulation.
82+
null_observation: Optional null observation, should be in the observation space.
83+
null_action: Optional null action, should be in the action space.
84+
"""
85+
def __init__(self, gym_env, null_observation, null_action, **kwargs):
86+
assert isinstance(gym_env, GymEnv), "gym_env must be a GymEnv."
87+
self._env = gym_env
88+
agents = {
89+
'agent': Agent(
90+
id='agent',
91+
observation_space=gym_env.observation_space,
92+
null_observation=null_observation,
93+
action_space=gym_env.action_space,
94+
null_action=null_action
95+
)
96+
}
97+
super().__init__(agents=agents, **kwargs)
98+
# ABS storage
99+
self._obs = None
100+
self._reward = None
101+
self._done = None
102+
self._info = None
103+
104+
def reset(self, **kwargs):
105+
"""
106+
Reset the simulation and store the observation and info.
107+
"""
108+
self._obs, self._info = self._env.reset()
109+
110+
def step(self, action, *args, **kwargs):
111+
"""
112+
Step the simulation and store the relevant data.
113+
114+
Args:
115+
action: The agent's action. Because this is an AgentBasedSimulation,
116+
the action will come in the form of a dictionary mapping the agent's
117+
id to its action.
118+
"""
119+
self._obs, self._reward, term, trunc, self._info = self._env.step(
120+
action['agent'], *args, **kwargs
121+
)
122+
self._done = term or trunc
123+
124+
def render(self, **kwargs):
125+
self._env.render(**kwargs)
126+
127+
def get_obs(self, *args, **kwargs):
128+
"""
129+
Return the stored observation, either from reset or step, whichever was last called.
130+
"""
131+
return self._obs
132+
133+
def get_reward(self, *args, **kwargs):
134+
"""
135+
Return the stored reward, either from reset or step, whichever was last called.
136+
"""
137+
return self._reward
138+
139+
def get_done(self, *args, **kwargs):
140+
"""
141+
Return the stored done status, either from reset or step, whichever was last called.
142+
"""
143+
return self._done
144+
145+
def get_all_done(self, **kwargs):
146+
"""
147+
Same thing as get done.
148+
"""
149+
return self.get_done()
150+
151+
def get_info(self, *args, **kwargs):
152+
"""
153+
Return the stored info, either from reset or step, whichever was last called.
154+
"""
155+
return self._info
156+
157+
158+
def gym_to_abmarl(gym_env, null_observation=None, null_action=None):
159+
"""
160+
Convert a GymEnv to an AgentBasedSimulation.
161+
162+
Args:
163+
gym_env: The GymEnv to be converted.
164+
null_observation: Optional null observation, should be in the observation space.
165+
null_action: Optional null action, should be in the action space.
166+
"""
167+
return GymABS(
168+
gym_env,
169+
null_observation,
170+
null_action
171+
)

abmarl/external/rllib_multiagentenv_wrapper.py

+131-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11

22
from gymnasium.spaces import Dict
33

4-
from abmarl.sim.agent_based_simulation import ActingAgent, ObservingAgent, is_agent
4+
from abmarl.sim.agent_based_simulation import ActingAgent, ObservingAgent, Agent, \
5+
is_agent, AgentBasedSimulation
56

67
try:
78
from ray.rllib import MultiAgentEnv
@@ -61,6 +62,116 @@ def render(self, *args, **kwargs):
6162
"""See SimulationManager."""
6263
return self.sim.render(*args, **kwargs)
6364

65+
class MultiAgentABS(AgentBasedSimulation):
66+
"""
67+
Wraps an RLlib MultiAgentEnv and leverages it for implementing the ABS interface.
68+
69+
Args:
70+
multi_agent_env: The MultiAgentEnv to convert to an AgentBasedSimulation.
71+
null_observation: Optional null observation, should be a dictionary of
72+
agents mapping to each one's observation space.
73+
null_action: Optional null action, should be a dictionary of
74+
agents mapping to each one's action space.
75+
"""
76+
def __init__(self, multi_agent_env, null_observation=None, null_action=None, **kwargs):
77+
assert isinstance(multi_agent_env, MultiAgentEnv), \
78+
"multi_agent_env must be a MultiAgentEnv."
79+
assert multi_agent_env._action_space_in_preferred_format and \
80+
multi_agent_env._obs_space_in_preferred_format, \
81+
"The action and observation spaces must be in the preferred format."
82+
self._env = multi_agent_env
83+
if not null_action:
84+
null_action = {}
85+
if not null_observation:
86+
null_observation = {}
87+
agents = {
88+
agent_id: Agent(
89+
id=agent_id,
90+
observation_space=multi_agent_env.observation_space[agent_id],
91+
null_observation=null_observation.get(agent_id),
92+
action_space=multi_agent_env.action_space[agent_id],
93+
null_action=null_action.get(agent_id),
94+
) for agent_id in multi_agent_env._agent_ids
95+
}
96+
super().__init__(agents=agents, **kwargs)
97+
# ABS storage
98+
self._obs = None
99+
self._reward = None
100+
self._done = None
101+
self._info = None
102+
103+
def reset(self, **kwargs):
104+
"""
105+
Reset the simulation and store the observation and info.
106+
"""
107+
self._obs, self._info = self._env.reset()
108+
109+
def step(self, action_dict, *args, **kwargs):
110+
"""
111+
Step the simulation and store the relevant data.
112+
113+
Args:
114+
action_dict: The agents' actions. Because this is an AgentBasedSimulation,
115+
the action will come in the form of a dictionary mapping the agents'
116+
ids to their actions.
117+
"""
118+
self._obs, self._reward, term, trunc, self._info = self._env.step(
119+
action_dict, *args, **kwargs
120+
)
121+
self._done = {**term, **trunc}
122+
for agent in self._done:
123+
self._done[agent] = term.get(agent, False) or trunc.get(agent, False)
124+
125+
def render(self, **kwargs):
126+
self._env.render(**kwargs)
127+
128+
def get_obs(self, agent_id, **kwargs):
129+
"""
130+
Return the stored observation, either from reset or step, whichever was last called.
131+
"""
132+
return self._obs[agent_id]
133+
134+
def get_reward(self, agent_id, **kwargs):
135+
"""
136+
Return the stored reward, either from reset or step, whichever was last called.
137+
"""
138+
return self._reward[agent_id]
139+
140+
def get_done(self, agent_id, **kwargs):
141+
"""
142+
Return the stored done status, either from reset or step, whichever was last called.
143+
"""
144+
return self._done[agent_id]
145+
146+
def get_all_done(self, **kwargs):
147+
"""
148+
Return the stored done status under "__all__".
149+
"""
150+
return self._done['__all__']
151+
152+
def get_info(self, agent_id, **kwargs):
153+
"""
154+
Return the stored info, either from reset or step, whichever was last called.
155+
"""
156+
return self._info[agent_id]
157+
158+
def multi_agent_to_abmarl(multi_agent_env, null_observation=None, null_action=None):
159+
"""
160+
Convert a MultiAgentEnv to an AgentBasedSimulation.
161+
162+
Args:
163+
multi_agent_env: The MultiAgentEnv to be converted.
164+
null_observation: Optional null observation, should be a dictionary of
165+
agents mapping to each one's observation space.
166+
null_action: Optional null action, should be a dictionary of
167+
agents mapping to each one's action space.
168+
"""
169+
return MultiAgentABS(
170+
multi_agent_env,
171+
null_observation,
172+
null_action
173+
)
174+
64175
except ImportError:
65176
class MultiAgentWrapper:
66177
"""
@@ -71,3 +182,22 @@ def __init__(self, sim):
71182
"Cannot use MultiAgentWrapper without RLlib. Please install the "
72183
"RLlib extra with, for example, pip install abmarl[rllib]."
73184
)
185+
186+
class MultiAgentABS(AgentBasedSimulation):
187+
"""
188+
Stub for MultiAgentABS class, which is not implemented without RLlib.
189+
"""
190+
def __init__(self, sim):
191+
raise NotImplementedError(
192+
"Cannot use MultiAgentABS without RLlib. Please install the "
193+
"RLlib extra with, for example, pip install abmarl[rllib]."
194+
)
195+
196+
def multi_agent_to_abmarl(*args, **kwargs):
197+
"""
198+
Stub for multi_agent_to_abmarl function, which is not implemented without RLlib.
199+
"""
200+
NotImplementedError(
201+
"Cannot use multi_agent_to_abmarl without RLlib. Please install the "
202+
"RLlib extra with, for example, pip install abmarl[rllib]."
203+
)

0 commit comments

Comments
 (0)