From 76d911d4f2a2784de981e714d5339bb5830a3464 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Castro=20Garc=C3=ADa?= Date: Tue, 8 Oct 2024 17:52:07 -0400 Subject: [PATCH 01/16] added MAgent2 task --- .../conf/task/magent/adversarial_pursuit.yaml | 9 ++ benchmarl/conf/task/magent/battle.yaml | 12 ++ benchmarl/conf/task/magent/battlefield.yaml | 12 ++ benchmarl/conf/task/magent/combined_arms.yaml | 12 ++ benchmarl/conf/task/magent/gather.yaml | 11 ++ benchmarl/conf/task/magent/tiger_deer.yaml | 10 ++ benchmarl/environments/__init__.py | 3 +- benchmarl/environments/magent/__init__.py | 0 .../magent/adversarial_pursuit.py | 16 +++ benchmarl/environments/magent/battle.py | 19 +++ benchmarl/environments/magent/battlefield.py | 19 +++ .../environments/magent/combined_arms.py | 19 +++ benchmarl/environments/magent/common.py | 125 ++++++++++++++++++ benchmarl/environments/magent/gather.py | 18 +++ benchmarl/environments/magent/tiger_deer.py | 17 +++ 15 files changed, 301 insertions(+), 1 deletion(-) create mode 100644 benchmarl/conf/task/magent/adversarial_pursuit.yaml create mode 100644 benchmarl/conf/task/magent/battle.yaml create mode 100644 benchmarl/conf/task/magent/battlefield.yaml create mode 100644 benchmarl/conf/task/magent/combined_arms.yaml create mode 100644 benchmarl/conf/task/magent/gather.yaml create mode 100644 benchmarl/conf/task/magent/tiger_deer.yaml create mode 100644 benchmarl/environments/magent/__init__.py create mode 100644 benchmarl/environments/magent/adversarial_pursuit.py create mode 100644 benchmarl/environments/magent/battle.py create mode 100644 benchmarl/environments/magent/battlefield.py create mode 100644 benchmarl/environments/magent/combined_arms.py create mode 100644 benchmarl/environments/magent/common.py create mode 100644 benchmarl/environments/magent/gather.py create mode 100644 benchmarl/environments/magent/tiger_deer.py diff --git a/benchmarl/conf/task/magent/adversarial_pursuit.yaml b/benchmarl/conf/task/magent/adversarial_pursuit.yaml new file mode 100644 index 00000000..85b46a00 --- /dev/null +++ b/benchmarl/conf/task/magent/adversarial_pursuit.yaml @@ -0,0 +1,9 @@ +defaults: + - adversarial_pursuit_config + - _self_ + +map_size: 45 +minimap_mode: False +tag_penalty: -0.2 +max_cycles: 500 +extra_features: False diff --git a/benchmarl/conf/task/magent/battle.yaml b/benchmarl/conf/task/magent/battle.yaml new file mode 100644 index 00000000..bb5e9f40 --- /dev/null +++ b/benchmarl/conf/task/magent/battle.yaml @@ -0,0 +1,12 @@ +defaults: + - battle_config + - _self_ + +map_size: 45 +minimap_mode: False +step_reward: -0.005 +dead_penalty: -0.1 +attack_penalty: -0.1 +attack_opponent_reward: 0.2 +max_cycles: 1000 +extra_features: False \ No newline at end of file diff --git a/benchmarl/conf/task/magent/battlefield.yaml b/benchmarl/conf/task/magent/battlefield.yaml new file mode 100644 index 00000000..ba2dab17 --- /dev/null +++ b/benchmarl/conf/task/magent/battlefield.yaml @@ -0,0 +1,12 @@ +defaults: + - battlefield_config + - _self_ + +map_size: 80 +minimap_mode: False +step_reward: -0.005 +dead_penalty: -0.1 +attack_penalty: -0.1 +attack_opponent_reward: 0.2 +max_cycles: 1000 +extra_features: False \ No newline at end of file diff --git a/benchmarl/conf/task/magent/combined_arms.yaml b/benchmarl/conf/task/magent/combined_arms.yaml new file mode 100644 index 00000000..09d2133c --- /dev/null +++ b/benchmarl/conf/task/magent/combined_arms.yaml @@ -0,0 +1,12 @@ +defaults: + - combined_arms_config + - _self_ + +map_size: 45 +minimap_mode: False +step_reward: -0.005 +dead_penalty: -0.1 +attack_penalty: -0.1 +attack_opponent_reward: 0.2 +max_cycles: 1000 +extra_features: False \ No newline at end of file diff --git a/benchmarl/conf/task/magent/gather.yaml b/benchmarl/conf/task/magent/gather.yaml new file mode 100644 index 00000000..8bf0d9b8 --- /dev/null +++ b/benchmarl/conf/task/magent/gather.yaml @@ -0,0 +1,11 @@ +defaults: + - gather_config + - _self_ + +minimap_mode: False +step_reward: -0.01 +attack_penalty: -0.1 +dead_penalty: -1 +attack_food_reward: 0.5 +max_cycles: 500 +extra_features: False \ No newline at end of file diff --git a/benchmarl/conf/task/magent/tiger_deer.yaml b/benchmarl/conf/task/magent/tiger_deer.yaml new file mode 100644 index 00000000..9b162a3f --- /dev/null +++ b/benchmarl/conf/task/magent/tiger_deer.yaml @@ -0,0 +1,10 @@ +defaults: + - tiger_deer_config + - _self_ + +map_size: 45 +minimap_mode: False +tiger_step_recover: -0.1 +deer_attacked: -0.1 +max_cycles: 500 +extra_features: False \ No newline at end of file diff --git a/benchmarl/environments/__init__.py b/benchmarl/environments/__init__.py index 4648cc0b..d3e4689d 100644 --- a/benchmarl/environments/__init__.py +++ b/benchmarl/environments/__init__.py @@ -9,10 +9,11 @@ from .pettingzoo.common import PettingZooTask from .smacv2.common import Smacv2Task from .vmas.common import VmasTask +from .magent.common import MAgentTask # The enum classes for the environments available. # This is the only object in this file you need to modify when adding a new environment. -tasks = [VmasTask, Smacv2Task, PettingZooTask, MeltingPotTask] +tasks = [VmasTask, Smacv2Task, PettingZooTask, MeltingPotTask, MAgentTask] # This is a registry mapping "envname/task_name" to the EnvNameTask.TASK_NAME enum # It is used by automatically load task enums from yaml files. diff --git a/benchmarl/environments/magent/__init__.py b/benchmarl/environments/magent/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/benchmarl/environments/magent/adversarial_pursuit.py b/benchmarl/environments/magent/adversarial_pursuit.py new file mode 100644 index 00000000..93a56858 --- /dev/null +++ b/benchmarl/environments/magent/adversarial_pursuit.py @@ -0,0 +1,16 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# This source code is licensed under the license found in the +# LICENSE file in the root directory of this source tree. +# + +from dataclasses import dataclass, MISSING + + +@dataclass +class TaskConfig: + map_size: int = MISSING + minimap_mode: bool = MISSING + tag_penalty: float = MISSING + max_cycles: int = MISSING + extra_features: bool = MISSING diff --git a/benchmarl/environments/magent/battle.py b/benchmarl/environments/magent/battle.py new file mode 100644 index 00000000..ee3d35c7 --- /dev/null +++ b/benchmarl/environments/magent/battle.py @@ -0,0 +1,19 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# This source code is licensed under the license found in the +# LICENSE file in the root directory of this source tree. +# + +from dataclasses import dataclass, MISSING + + +@dataclass +class TaskConfig: + map_size: int = MISSING + minimap_mode: bool = MISSING + step_reward: float = MISSING + dead_penalty: float = MISSING + attack_penalty: float = MISSING + attack_opponent_reward: float = MISSING + max_cycles: int = MISSING + extra_features: bool = MISSING diff --git a/benchmarl/environments/magent/battlefield.py b/benchmarl/environments/magent/battlefield.py new file mode 100644 index 00000000..ee3d35c7 --- /dev/null +++ b/benchmarl/environments/magent/battlefield.py @@ -0,0 +1,19 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# This source code is licensed under the license found in the +# LICENSE file in the root directory of this source tree. +# + +from dataclasses import dataclass, MISSING + + +@dataclass +class TaskConfig: + map_size: int = MISSING + minimap_mode: bool = MISSING + step_reward: float = MISSING + dead_penalty: float = MISSING + attack_penalty: float = MISSING + attack_opponent_reward: float = MISSING + max_cycles: int = MISSING + extra_features: bool = MISSING diff --git a/benchmarl/environments/magent/combined_arms.py b/benchmarl/environments/magent/combined_arms.py new file mode 100644 index 00000000..ee3d35c7 --- /dev/null +++ b/benchmarl/environments/magent/combined_arms.py @@ -0,0 +1,19 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# This source code is licensed under the license found in the +# LICENSE file in the root directory of this source tree. +# + +from dataclasses import dataclass, MISSING + + +@dataclass +class TaskConfig: + map_size: int = MISSING + minimap_mode: bool = MISSING + step_reward: float = MISSING + dead_penalty: float = MISSING + attack_penalty: float = MISSING + attack_opponent_reward: float = MISSING + max_cycles: int = MISSING + extra_features: bool = MISSING diff --git a/benchmarl/environments/magent/common.py b/benchmarl/environments/magent/common.py new file mode 100644 index 00000000..8b6977e4 --- /dev/null +++ b/benchmarl/environments/magent/common.py @@ -0,0 +1,125 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# This source code is licensed under the license found in the +# LICENSE file in the root directory of this source tree. +# + +from typing import Callable, Dict, List, Optional + +from torchrl.data import CompositeSpec +from torchrl.envs import EnvBase, PettingZooWrapper + +from magent2.environments import ( + adversarial_pursuit_v4, + battle_v4, + battlefield_v5, + combined_arms_v6, + gather_v5, + tiger_deer_v4 +) + +from benchmarl.environments.common import Task + +from benchmarl.utils import DEVICE_TYPING + + +class MAgentTask(Task): + """Enum for MAgent2 tasks.""" + + ADVERSARIAL_PURSUIT = None + BATTLE = None + BATTLEFIELD = None + COMBINED_ARMS = None + GATHER = None + TIGER_DEER = None + + def get_env_fun( + self, + num_envs: int, + continuous_actions: bool, + seed: Optional[int], + device: DEVICE_TYPING, + ) -> Callable[[], EnvBase]: + + return lambda: PettingZooWrapper( + env=self.__get_env(), + return_state=True, + seed=seed, + done_on_any=True, + device=device + ) + + def __get_env(self) -> EnvBase: + envs = { + "ADVERSARIAL_PURSUIT": adversarial_pursuit_v4, + "BATTLE": battle_v4, + "BATTLEFIELD": battlefield_v5, + "COMBINED_ARMS": combined_arms_v6, + "GATHER": gather_v5, + "TIGER_DEER": tiger_deer_v4 + } + if self.name not in envs: + raise Exception(f"{self.name} is not an environment of MAgent2") + return envs[self.name].parallel_env(**self.config, render_mode="rgb_array") + + def supports_continuous_actions(self) -> bool: + return False + + def supports_discrete_actions(self) -> bool: + return True + + def has_state(self) -> bool: + return True + + def has_render(self, env: EnvBase) -> bool: + return True + + def max_steps(self, env: EnvBase) -> int: + return self.config["max_cycles"] + + def group_map(self, env: EnvBase) -> Dict[str, List[str]]: + return env.group_map + + def state_spec(self, env: EnvBase) -> Optional[CompositeSpec]: + return CompositeSpec({"state": env.observation_spec["state"].clone()}) + + def action_mask_spec(self, env: EnvBase) -> Optional[CompositeSpec]: + observation_spec = env.observation_spec.clone() + for group in self.group_map(env): + group_obs_spec = observation_spec[group] + for key in list(group_obs_spec.keys()): + if key != "action_mask": + del group_obs_spec[key] + if group_obs_spec.is_empty(): + del observation_spec[group] + del observation_spec["state"] + if observation_spec.is_empty(): + return None + return observation_spec + + def observation_spec(self, env: EnvBase) -> CompositeSpec: + observation_spec = env.observation_spec.clone() + for group in self.group_map(env): + group_obs_spec = observation_spec[group] + for key in list(group_obs_spec.keys()): + if key != "observation": + del group_obs_spec[key] + del observation_spec["state"] + return observation_spec + + def info_spec(self, env: EnvBase) -> Optional[CompositeSpec]: + observation_spec = env.observation_spec.clone() + for group in self.group_map(env): + group_obs_spec = observation_spec[group] + for key in list(group_obs_spec.keys()): + if key != "info": + del group_obs_spec[key] + del observation_spec["state"] + return observation_spec + + def action_spec(self, env: EnvBase) -> CompositeSpec: + return env.full_action_spec + + @staticmethod + def env_name() -> str: + return "magent" diff --git a/benchmarl/environments/magent/gather.py b/benchmarl/environments/magent/gather.py new file mode 100644 index 00000000..cf13b5c8 --- /dev/null +++ b/benchmarl/environments/magent/gather.py @@ -0,0 +1,18 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# This source code is licensed under the license found in the +# LICENSE file in the root directory of this source tree. +# + +from dataclasses import dataclass, MISSING + + +@dataclass +class TaskConfig: + minimap_mode: bool = MISSING + step_reward: float = MISSING + attack_penalty: float = MISSING + dead_penalty: float = MISSING + attack_food_reward: float = MISSING + max_cycles: int = MISSING + extra_features: bool = MISSING diff --git a/benchmarl/environments/magent/tiger_deer.py b/benchmarl/environments/magent/tiger_deer.py new file mode 100644 index 00000000..55f5e367 --- /dev/null +++ b/benchmarl/environments/magent/tiger_deer.py @@ -0,0 +1,17 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# This source code is licensed under the license found in the +# LICENSE file in the root directory of this source tree. +# + +from dataclasses import dataclass, MISSING + + +@dataclass +class TaskConfig: + map_size: int = MISSING + minimap_mode: bool = MISSING + tiger_step_recover: float = MISSING + deer_attacked: float = MISSING + max_cycles: int = MISSING + extra_features: bool = MISSING From 8d397ff96f6b15358abf82d3ee58df5f47078375 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Castro=20Garc=C3=ADa?= Date: Wed, 9 Oct 2024 18:30:06 -0400 Subject: [PATCH 02/16] unset don on any --- benchmarl/environments/magent/common.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/benchmarl/environments/magent/common.py b/benchmarl/environments/magent/common.py index 8b6977e4..e75473d5 100644 --- a/benchmarl/environments/magent/common.py +++ b/benchmarl/environments/magent/common.py @@ -45,7 +45,8 @@ def get_env_fun( env=self.__get_env(), return_state=True, seed=seed, - done_on_any=True, + done_on_any=False, + use_mask=True, device=device ) From 41afc3ff0dceeb0396bbcf519277123ce05a9840 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Castro=20Garc=C3=ADa?= Date: Mon, 28 Oct 2024 20:41:57 -0400 Subject: [PATCH 03/16] dependencies moved to function --- benchmarl/environments/magent/common.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/benchmarl/environments/magent/common.py b/benchmarl/environments/magent/common.py index e75473d5..d1f4111c 100644 --- a/benchmarl/environments/magent/common.py +++ b/benchmarl/environments/magent/common.py @@ -9,15 +9,6 @@ from torchrl.data import CompositeSpec from torchrl.envs import EnvBase, PettingZooWrapper -from magent2.environments import ( - adversarial_pursuit_v4, - battle_v4, - battlefield_v5, - combined_arms_v6, - gather_v5, - tiger_deer_v4 -) - from benchmarl.environments.common import Task from benchmarl.utils import DEVICE_TYPING @@ -51,6 +42,19 @@ def get_env_fun( ) def __get_env(self) -> EnvBase: + try: + from magent2.environments import ( + adversarial_pursuit_v4, + battle_v4, + battlefield_v5, + combined_arms_v6, + gather_v5, + tiger_deer_v4 + ) + except ImportError as e: + print("Module 'magent2' not found, install it using `pip install magent2`") + raise e + envs = { "ADVERSARIAL_PURSUIT": adversarial_pursuit_v4, "BATTLE": battle_v4, From b70002bf5d7a0436c122ebe75d736f6103543ea8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Castro=20Garc=C3=ADa?= Date: Tue, 29 Oct 2024 16:43:24 -0400 Subject: [PATCH 04/16] tasks with dead agents eliminated --- benchmarl/conf/task/magent/battle.yaml | 12 ------------ benchmarl/conf/task/magent/battlefield.yaml | 12 ------------ benchmarl/conf/task/magent/combined_arms.yaml | 12 ------------ benchmarl/conf/task/magent/gather.yaml | 11 ----------- benchmarl/conf/task/magent/tiger_deer.yaml | 10 ---------- benchmarl/environments/magent/battle.py | 19 ------------------- benchmarl/environments/magent/battlefield.py | 19 ------------------- .../environments/magent/combined_arms.py | 19 ------------------- benchmarl/environments/magent/common.py | 12 ++++++------ benchmarl/environments/magent/gather.py | 18 ------------------ benchmarl/environments/magent/tiger_deer.py | 17 ----------------- 11 files changed, 6 insertions(+), 155 deletions(-) delete mode 100644 benchmarl/conf/task/magent/battle.yaml delete mode 100644 benchmarl/conf/task/magent/battlefield.yaml delete mode 100644 benchmarl/conf/task/magent/combined_arms.yaml delete mode 100644 benchmarl/conf/task/magent/gather.yaml delete mode 100644 benchmarl/conf/task/magent/tiger_deer.yaml delete mode 100644 benchmarl/environments/magent/battle.py delete mode 100644 benchmarl/environments/magent/battlefield.py delete mode 100644 benchmarl/environments/magent/combined_arms.py delete mode 100644 benchmarl/environments/magent/gather.py delete mode 100644 benchmarl/environments/magent/tiger_deer.py diff --git a/benchmarl/conf/task/magent/battle.yaml b/benchmarl/conf/task/magent/battle.yaml deleted file mode 100644 index bb5e9f40..00000000 --- a/benchmarl/conf/task/magent/battle.yaml +++ /dev/null @@ -1,12 +0,0 @@ -defaults: - - battle_config - - _self_ - -map_size: 45 -minimap_mode: False -step_reward: -0.005 -dead_penalty: -0.1 -attack_penalty: -0.1 -attack_opponent_reward: 0.2 -max_cycles: 1000 -extra_features: False \ No newline at end of file diff --git a/benchmarl/conf/task/magent/battlefield.yaml b/benchmarl/conf/task/magent/battlefield.yaml deleted file mode 100644 index ba2dab17..00000000 --- a/benchmarl/conf/task/magent/battlefield.yaml +++ /dev/null @@ -1,12 +0,0 @@ -defaults: - - battlefield_config - - _self_ - -map_size: 80 -minimap_mode: False -step_reward: -0.005 -dead_penalty: -0.1 -attack_penalty: -0.1 -attack_opponent_reward: 0.2 -max_cycles: 1000 -extra_features: False \ No newline at end of file diff --git a/benchmarl/conf/task/magent/combined_arms.yaml b/benchmarl/conf/task/magent/combined_arms.yaml deleted file mode 100644 index 09d2133c..00000000 --- a/benchmarl/conf/task/magent/combined_arms.yaml +++ /dev/null @@ -1,12 +0,0 @@ -defaults: - - combined_arms_config - - _self_ - -map_size: 45 -minimap_mode: False -step_reward: -0.005 -dead_penalty: -0.1 -attack_penalty: -0.1 -attack_opponent_reward: 0.2 -max_cycles: 1000 -extra_features: False \ No newline at end of file diff --git a/benchmarl/conf/task/magent/gather.yaml b/benchmarl/conf/task/magent/gather.yaml deleted file mode 100644 index 8bf0d9b8..00000000 --- a/benchmarl/conf/task/magent/gather.yaml +++ /dev/null @@ -1,11 +0,0 @@ -defaults: - - gather_config - - _self_ - -minimap_mode: False -step_reward: -0.01 -attack_penalty: -0.1 -dead_penalty: -1 -attack_food_reward: 0.5 -max_cycles: 500 -extra_features: False \ No newline at end of file diff --git a/benchmarl/conf/task/magent/tiger_deer.yaml b/benchmarl/conf/task/magent/tiger_deer.yaml deleted file mode 100644 index 9b162a3f..00000000 --- a/benchmarl/conf/task/magent/tiger_deer.yaml +++ /dev/null @@ -1,10 +0,0 @@ -defaults: - - tiger_deer_config - - _self_ - -map_size: 45 -minimap_mode: False -tiger_step_recover: -0.1 -deer_attacked: -0.1 -max_cycles: 500 -extra_features: False \ No newline at end of file diff --git a/benchmarl/environments/magent/battle.py b/benchmarl/environments/magent/battle.py deleted file mode 100644 index ee3d35c7..00000000 --- a/benchmarl/environments/magent/battle.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) Meta Platforms, Inc. and affiliates. -# -# This source code is licensed under the license found in the -# LICENSE file in the root directory of this source tree. -# - -from dataclasses import dataclass, MISSING - - -@dataclass -class TaskConfig: - map_size: int = MISSING - minimap_mode: bool = MISSING - step_reward: float = MISSING - dead_penalty: float = MISSING - attack_penalty: float = MISSING - attack_opponent_reward: float = MISSING - max_cycles: int = MISSING - extra_features: bool = MISSING diff --git a/benchmarl/environments/magent/battlefield.py b/benchmarl/environments/magent/battlefield.py deleted file mode 100644 index ee3d35c7..00000000 --- a/benchmarl/environments/magent/battlefield.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) Meta Platforms, Inc. and affiliates. -# -# This source code is licensed under the license found in the -# LICENSE file in the root directory of this source tree. -# - -from dataclasses import dataclass, MISSING - - -@dataclass -class TaskConfig: - map_size: int = MISSING - minimap_mode: bool = MISSING - step_reward: float = MISSING - dead_penalty: float = MISSING - attack_penalty: float = MISSING - attack_opponent_reward: float = MISSING - max_cycles: int = MISSING - extra_features: bool = MISSING diff --git a/benchmarl/environments/magent/combined_arms.py b/benchmarl/environments/magent/combined_arms.py deleted file mode 100644 index ee3d35c7..00000000 --- a/benchmarl/environments/magent/combined_arms.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) Meta Platforms, Inc. and affiliates. -# -# This source code is licensed under the license found in the -# LICENSE file in the root directory of this source tree. -# - -from dataclasses import dataclass, MISSING - - -@dataclass -class TaskConfig: - map_size: int = MISSING - minimap_mode: bool = MISSING - step_reward: float = MISSING - dead_penalty: float = MISSING - attack_penalty: float = MISSING - attack_opponent_reward: float = MISSING - max_cycles: int = MISSING - extra_features: bool = MISSING diff --git a/benchmarl/environments/magent/common.py b/benchmarl/environments/magent/common.py index d1f4111c..11d9acd7 100644 --- a/benchmarl/environments/magent/common.py +++ b/benchmarl/environments/magent/common.py @@ -18,11 +18,11 @@ class MAgentTask(Task): """Enum for MAgent2 tasks.""" ADVERSARIAL_PURSUIT = None - BATTLE = None - BATTLEFIELD = None - COMBINED_ARMS = None - GATHER = None - TIGER_DEER = None + # BATTLE = None + # BATTLEFIELD = None + # COMBINED_ARMS = None + # GATHER = None + # TIGER_DEER = None def get_env_fun( self, @@ -37,7 +37,7 @@ def get_env_fun( return_state=True, seed=seed, done_on_any=False, - use_mask=True, + use_mask=False, device=device ) diff --git a/benchmarl/environments/magent/gather.py b/benchmarl/environments/magent/gather.py deleted file mode 100644 index cf13b5c8..00000000 --- a/benchmarl/environments/magent/gather.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) Meta Platforms, Inc. and affiliates. -# -# This source code is licensed under the license found in the -# LICENSE file in the root directory of this source tree. -# - -from dataclasses import dataclass, MISSING - - -@dataclass -class TaskConfig: - minimap_mode: bool = MISSING - step_reward: float = MISSING - attack_penalty: float = MISSING - dead_penalty: float = MISSING - attack_food_reward: float = MISSING - max_cycles: int = MISSING - extra_features: bool = MISSING diff --git a/benchmarl/environments/magent/tiger_deer.py b/benchmarl/environments/magent/tiger_deer.py deleted file mode 100644 index 55f5e367..00000000 --- a/benchmarl/environments/magent/tiger_deer.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (c) Meta Platforms, Inc. and affiliates. -# -# This source code is licensed under the license found in the -# LICENSE file in the root directory of this source tree. -# - -from dataclasses import dataclass, MISSING - - -@dataclass -class TaskConfig: - map_size: int = MISSING - minimap_mode: bool = MISSING - tiger_step_recover: float = MISSING - deer_attacked: float = MISSING - max_cycles: int = MISSING - extra_features: bool = MISSING From 8df759044ae21b49eb5d7a6c14e12f41b639b71e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Castro=20Garc=C3=ADa?= Date: Tue, 29 Oct 2024 16:56:57 -0400 Subject: [PATCH 05/16] unused references removed --- benchmarl/environments/magent/common.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/benchmarl/environments/magent/common.py b/benchmarl/environments/magent/common.py index 11d9acd7..c0d28690 100644 --- a/benchmarl/environments/magent/common.py +++ b/benchmarl/environments/magent/common.py @@ -45,11 +45,11 @@ def __get_env(self) -> EnvBase: try: from magent2.environments import ( adversarial_pursuit_v4, - battle_v4, - battlefield_v5, - combined_arms_v6, - gather_v5, - tiger_deer_v4 + # battle_v4, + # battlefield_v5, + # combined_arms_v6, + # gather_v5, + # tiger_deer_v4 ) except ImportError as e: print("Module 'magent2' not found, install it using `pip install magent2`") @@ -57,11 +57,11 @@ def __get_env(self) -> EnvBase: envs = { "ADVERSARIAL_PURSUIT": adversarial_pursuit_v4, - "BATTLE": battle_v4, - "BATTLEFIELD": battlefield_v5, - "COMBINED_ARMS": combined_arms_v6, - "GATHER": gather_v5, - "TIGER_DEER": tiger_deer_v4 + # "BATTLE": battle_v4, + # "BATTLEFIELD": battlefield_v5, + # "COMBINED_ARMS": combined_arms_v6, + # "GATHER": gather_v5, + # "TIGER_DEER": tiger_deer_v4 } if self.name not in envs: raise Exception(f"{self.name} is not an environment of MAgent2") From 931ea1d01a5b4a46eac25b36ea9a866202b3d445 Mon Sep 17 00:00:00 2001 From: Matteo Bettini <55539777+matteobettini@users.noreply.github.com> Date: Thu, 14 Nov 2024 07:42:35 +0000 Subject: [PATCH 06/16] Update benchmarl/environments/magent/common.py --- benchmarl/environments/magent/common.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/benchmarl/environments/magent/common.py b/benchmarl/environments/magent/common.py index c0d28690..4ed6ae20 100644 --- a/benchmarl/environments/magent/common.py +++ b/benchmarl/environments/magent/common.py @@ -52,8 +52,7 @@ def __get_env(self) -> EnvBase: # tiger_deer_v4 ) except ImportError as e: - print("Module 'magent2' not found, install it using `pip install magent2`") - raise e + raise ImportError("Module `magent2` not found, install it using `pip install magent2`") envs = { "ADVERSARIAL_PURSUIT": adversarial_pursuit_v4, From 0b2a12812f77c4a25e6aee22918225842abd2d2f Mon Sep 17 00:00:00 2001 From: Matteo Bettini Date: Sun, 24 Nov 2024 14:41:45 +0000 Subject: [PATCH 07/16] empty From 8d72affe1ca3862b68ae5af825a781ad35a7bebb Mon Sep 17 00:00:00 2001 From: Matteo Bettini Date: Sun, 24 Nov 2024 15:13:45 +0000 Subject: [PATCH 08/16] rename schema --- benchmarl/conf/task/magent/adversarial_pursuit.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarl/conf/task/magent/adversarial_pursuit.yaml b/benchmarl/conf/task/magent/adversarial_pursuit.yaml index 85b46a00..cfd5f402 100644 --- a/benchmarl/conf/task/magent/adversarial_pursuit.yaml +++ b/benchmarl/conf/task/magent/adversarial_pursuit.yaml @@ -1,5 +1,5 @@ defaults: - - adversarial_pursuit_config + - magent_adversarial_pursuit_config - _self_ map_size: 45 From 65de1348e71d56ef9b6d8c9442bc78e6beec91d6 Mon Sep 17 00:00:00 2001 From: Matteo Bettini Date: Sun, 24 Nov 2024 15:13:56 +0000 Subject: [PATCH 09/16] rename schema --- benchmarl/conf/config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/benchmarl/conf/config.yaml b/benchmarl/conf/config.yaml index 02343b24..1353498d 100644 --- a/benchmarl/conf/config.yaml +++ b/benchmarl/conf/config.yaml @@ -1,7 +1,7 @@ defaults: - experiment: base_experiment - - algorithm: ??? - - task: ??? + - algorithm: mappo + - task: magent/adversarial_pursuit - model: layers/mlp - model@critic_model: layers/mlp - _self_ From 04622ab14a501d3b3b736d7256a2ce87aaef1001 Mon Sep 17 00:00:00 2001 From: Matteo Bettini Date: Sun, 24 Nov 2024 15:14:17 +0000 Subject: [PATCH 10/16] Revert "rename schema" This reverts commit 65de1348e71d56ef9b6d8c9442bc78e6beec91d6. --- benchmarl/conf/config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/benchmarl/conf/config.yaml b/benchmarl/conf/config.yaml index 1353498d..02343b24 100644 --- a/benchmarl/conf/config.yaml +++ b/benchmarl/conf/config.yaml @@ -1,7 +1,7 @@ defaults: - experiment: base_experiment - - algorithm: mappo - - task: magent/adversarial_pursuit + - algorithm: ??? + - task: ??? - model: layers/mlp - model@critic_model: layers/mlp - _self_ From 4beb8f7cad42b3e5b342b53d4b9f8d6b191ab8e5 Mon Sep 17 00:00:00 2001 From: Matteo Bettini Date: Sun, 24 Nov 2024 15:46:58 +0000 Subject: [PATCH 11/16] ci --- .github/unittest/install_magent2.sh | 6 ++ .github/workflows/magent_tests.yml | 43 +++++++++ test/conftest.py | 54 +++++++++++ test/test_magent.py | 135 ++++++++++++++++++++++++++++ test/utils.py | 1 + 5 files changed, 239 insertions(+) create mode 100644 .github/unittest/install_magent2.sh create mode 100644 .github/workflows/magent_tests.yml create mode 100644 test/test_magent.py diff --git a/.github/unittest/install_magent2.sh b/.github/unittest/install_magent2.sh new file mode 100644 index 00000000..9dac7daa --- /dev/null +++ b/.github/unittest/install_magent2.sh @@ -0,0 +1,6 @@ + +pip install "pettingzoo[all]==1.24.3" +pip install git+https://github.com/Farama-Foundation/MAgent2 + +sudo apt-get update +sudo apt-get install python3-opengl xvfb diff --git a/.github/workflows/magent_tests.yml b/.github/workflows/magent_tests.yml new file mode 100644 index 00000000..b7afdbd2 --- /dev/null +++ b/.github/workflows/magent_tests.yml @@ -0,0 +1,43 @@ +# This workflow will install Python dependencies, run tests and lint with a single version of Python +# For more information see: +# https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions + + +name: magent_tests + +on: + push: + branches: [ $default-branch , "main" ] + pull_request: + branches: [ $default-branch , "main" ] + +permissions: + contents: read + +jobs: + tests: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.11"] + + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + bash .github/unittest/install_dependencies_nightly.sh + - name: Install pettingzoo + run: | + bash .github/unittest/install_magent2.sh + - name: Test with pytest + run: | + xvfb-run -s "-screen 0 1024x768x24" pytest test/test_magent.py --doctest-modules --junitxml=junit/test-results.xml --cov=. --cov-report=xml --cov-report=html + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 + with: + fail_ci_if_error: false diff --git a/test/conftest.py b/test/conftest.py index 4ef505bc..3f53416e 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -90,6 +90,31 @@ def mlp_gnn_sequence_config() -> ModelConfig: ) +@pytest.fixture +def cnn_gnn_sequence_config() -> ModelConfig: + return SequenceModelConfig( + model_configs=[ + CnnConfig( + cnn_num_cells=[4, 3], + cnn_kernel_sizes=[3, 2], + cnn_strides=1, + cnn_paddings=0, + cnn_activation_class=nn.Tanh, + mlp_num_cells=[4], + mlp_activation_class=nn.Tanh, + mlp_layer_class=nn.Linear, + ), + GnnConfig( + topology="full", + self_loops=False, + gnn_class=torch_geometric.nn.conv.GATv2Conv, + ), + MlpConfig(num_cells=[4], activation_class=nn.Tanh, layer_class=nn.Linear), + ], + intermediate_sizes=[5, 3], + ) + + @pytest.fixture def gru_mlp_sequence_config() -> ModelConfig: return SequenceModelConfig( @@ -128,3 +153,32 @@ def lstm_mlp_sequence_config() -> ModelConfig: ], intermediate_sizes=[5], ) + + +@pytest.fixture +def cnn_lstm_sequence_config() -> ModelConfig: + return SequenceModelConfig( + model_configs=[ + CnnConfig( + cnn_num_cells=[4, 3], + cnn_kernel_sizes=[3, 2], + cnn_strides=1, + cnn_paddings=0, + cnn_activation_class=nn.Tanh, + mlp_num_cells=[4], + mlp_activation_class=nn.Tanh, + mlp_layer_class=nn.Linear, + ), + LstmConfig( + hidden_size=13, + mlp_num_cells=[], + mlp_activation_class=nn.Tanh, + mlp_layer_class=nn.Linear, + n_layers=1, + bias=True, + dropout=0, + compile=False, + ), + ], + intermediate_sizes=[5], + ) diff --git a/test/test_magent.py b/test/test_magent.py new file mode 100644 index 00000000..2a05a198 --- /dev/null +++ b/test/test_magent.py @@ -0,0 +1,135 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# This source code is licensed under the license found in the +# LICENSE file in the root directory of this source tree. +# + + +import pytest +from benchmarl.algorithms import ( + algorithm_config_registry, + IppoConfig, + IsacConfig, + MasacConfig, + QmixConfig, +) +from benchmarl.algorithms.common import AlgorithmConfig +from benchmarl.environments import MAgentTask, Task +from benchmarl.experiment import Experiment + +from utils import _has_magent2 +from utils_experiment import ExperimentUtils + + +@pytest.mark.skipif(not _has_magent2, reason="magent2 not found") +class TestMagent: + @pytest.mark.parametrize("algo_config", algorithm_config_registry.values()) + @pytest.mark.parametrize("task", [MAgentTask.ADVERSARIAL_PURSUIT]) + def test_all_algos( + self, + algo_config: AlgorithmConfig, + task: Task, + experiment_config, + cnn_sequence_config, + ): + + # To not run unsupported algo-task pairs + if not algo_config.supports_discrete_actions(): + pytest.skip() + + task = task.get_from_yaml() + experiment = Experiment( + algorithm_config=algo_config.get_from_yaml(), + model_config=cnn_sequence_config, + seed=0, + config=experiment_config, + task=task, + ) + experiment.run() + + @pytest.mark.parametrize("algo_config", [IppoConfig, QmixConfig, IsacConfig]) + @pytest.mark.parametrize("task", [MAgentTask.ADVERSARIAL_PURSUIT]) + def test_gnn( + self, + algo_config: AlgorithmConfig, + task: Task, + experiment_config, + cnn_gnn_sequence_config, + ): + task = task.get_from_yaml() + experiment = Experiment( + algorithm_config=algo_config.get_from_yaml(), + model_config=cnn_gnn_sequence_config, + critic_model_config=cnn_gnn_sequence_config, + seed=0, + config=experiment_config, + task=task, + ) + experiment.run() + + @pytest.mark.parametrize("algo_config", [IppoConfig, QmixConfig, MasacConfig]) + @pytest.mark.parametrize("task", [MAgentTask.ADVERSARIAL_PURSUIT]) + def test_lstm( + self, + algo_config: AlgorithmConfig, + task: Task, + experiment_config, + cnn_lstm_sequence_config, + ): + algo_config = algo_config.get_from_yaml() + if algo_config.has_critic(): + algo_config.share_param_critic = False + experiment_config.share_policy_params = False + task = task.get_from_yaml() + experiment = Experiment( + algorithm_config=algo_config, + model_config=cnn_lstm_sequence_config, + critic_model_config=cnn_lstm_sequence_config, + seed=0, + config=experiment_config, + task=task, + ) + experiment.run() + + @pytest.mark.parametrize("algo_config", algorithm_config_registry.values()) + @pytest.mark.parametrize("task", [MAgentTask.ADVERSARIAL_PURSUIT]) + def test_reloading_trainer( + self, + algo_config: AlgorithmConfig, + task: Task, + experiment_config, + cnn_sequence_config, + ): + # To not run unsupported algo-task pairs + if not algo_config.supports_discrete_actions(): + pytest.skip() + algo_config = algo_config.get_from_yaml() + + ExperimentUtils.check_experiment_loading( + algo_config=algo_config, + model_config=cnn_sequence_config, + experiment_config=experiment_config, + task=task.get_from_yaml(), + ) + + @pytest.mark.parametrize("algo_config", [QmixConfig, IppoConfig, MasacConfig]) + @pytest.mark.parametrize("task", [MAgentTask.ADVERSARIAL_PURSUIT]) + @pytest.mark.parametrize("share_params", [True, False]) + def test_share_policy_params( + self, + algo_config: AlgorithmConfig, + task: Task, + share_params, + experiment_config, + cnn_sequence_config, + ): + experiment_config.share_policy_params = share_params + task = task.get_from_yaml() + experiment = Experiment( + algorithm_config=algo_config.get_from_yaml(), + model_config=cnn_sequence_config, + seed=0, + config=experiment_config, + task=task, + ) + experiment.run() diff --git a/test/utils.py b/test/utils.py index 848b149b..1b5f5b41 100644 --- a/test/utils.py +++ b/test/utils.py @@ -10,3 +10,4 @@ _has_smacv2 = importlib.util.find_spec("smacv2") is not None _has_pettingzoo = importlib.util.find_spec("pettingzoo") is not None _has_meltingpot = importlib.util.find_spec("meltingpot") is not None +_has_magent2 = importlib.util.find_spec("magent2") is not None From 7a277e6a02bc71f1f5404bceef022bf2b5b353e3 Mon Sep 17 00:00:00 2001 From: Matteo Bettini Date: Sun, 24 Nov 2024 15:49:44 +0000 Subject: [PATCH 12/16] ci --- test/test_magent.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/test_magent.py b/test/test_magent.py index 2a05a198..e04ec08d 100644 --- a/test/test_magent.py +++ b/test/test_magent.py @@ -6,10 +6,12 @@ import pytest + from benchmarl.algorithms import ( algorithm_config_registry, IppoConfig, IsacConfig, + MappoConfig, MasacConfig, QmixConfig, ) @@ -47,7 +49,7 @@ def test_all_algos( ) experiment.run() - @pytest.mark.parametrize("algo_config", [IppoConfig, QmixConfig, IsacConfig]) + @pytest.mark.parametrize("algo_config", [MappoConfig, QmixConfig, IsacConfig]) @pytest.mark.parametrize("task", [MAgentTask.ADVERSARIAL_PURSUIT]) def test_gnn( self, From 62bfcb67da5b248f3fb55fb31b496cb89279b46a Mon Sep 17 00:00:00 2001 From: Matteo Bettini Date: Sun, 24 Nov 2024 16:04:50 +0000 Subject: [PATCH 13/16] docs --- README.md | 15 ++++++++------- benchmarl/environments/magent/common.py | 18 ++++++++++-------- docs/source/concepts/components.rst | 2 ++ 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 3e515132..aed9635c 100644 --- a/README.md +++ b/README.md @@ -239,13 +239,14 @@ determine the training strategy. Here is a table with the currently implemented challenge to solve. They differ based on many aspects, here is a table with the current environments in BenchMARL -| Environment | Tasks | Cooperation | Global state | Reward function | Action space | Vectorized | -|--------------------------------------------------------------------|--------------------------------------|---------------------------|--------------|-------------------------------|-----------------------|:----------------:| -| [VMAS](https://github.com/proroklab/VectorizedMultiAgentSimulator) | [27](benchmarl/conf/task/vmas) | Cooperative + Competitive | No | Shared + Independent + Global | Continuous + Discrete | Yes | -| [SMACv2](https://github.com/oxwhirl/smacv2) | [15](benchmarl/conf/task/smacv2) | Cooperative | Yes | Global | Discrete | No | -| [MPE](https://github.com/openai/multiagent-particle-envs) | [8](benchmarl/conf/task/pettingzoo) | Cooperative + Competitive | Yes | Shared + Independent | Continuous + Discrete | No | -| [SISL](https://github.com/sisl/MADRL) | [2](benchmarl/conf/task/pettingzoo) | Cooperative | No | Shared | Continuous | No | -| [MeltingPot](https://github.com/google-deepmind/meltingpot) | [49](benchmarl/conf/task/meltingpot) | Cooperative + Competitive | Yes | Independent | Discrete | No | +| Environment | Tasks | Cooperation | Global state | Reward function | Action space | Vectorized | +|---------------------------------------------------------------------|--------------------------------------|---------------------------|--------------|-------------------------------|-----------------------|:----------------:| +| [VMAS](https://github.com/proroklab/VectorizedMultiAgentSimulator) | [27](benchmarl/conf/task/vmas) | Cooperative + Competitive | No | Shared + Independent + Global | Continuous + Discrete | Yes | +| [SMACv2](https://github.com/oxwhirl/smacv2) | [15](benchmarl/conf/task/smacv2) | Cooperative | Yes | Global | Discrete | No | +| [MPE](https://github.com/openai/multiagent-particle-envs) | [8](benchmarl/conf/task/pettingzoo) | Cooperative + Competitive | Yes | Shared + Independent | Continuous + Discrete | No | +| [SISL](https://github.com/sisl/MADRL) | [2](benchmarl/conf/task/pettingzoo) | Cooperative | No | Shared | Continuous | No | +| [MeltingPot](https://github.com/google-deepmind/meltingpot) | [49](benchmarl/conf/task/meltingpot) | Cooperative + Competitive | Yes | Independent | Discrete | No | +| [MAgent2](https://github.com/Farama-Foundation/magent2) | [1](benchmarl/conf/task/magent) | Cooperative + Competitive | Yes | Global in groups | Discrete | No | > [!NOTE] diff --git a/benchmarl/environments/magent/common.py b/benchmarl/environments/magent/common.py index 4ed6ae20..9a3d9ee4 100644 --- a/benchmarl/environments/magent/common.py +++ b/benchmarl/environments/magent/common.py @@ -25,11 +25,11 @@ class MAgentTask(Task): # TIGER_DEER = None def get_env_fun( - self, - num_envs: int, - continuous_actions: bool, - seed: Optional[int], - device: DEVICE_TYPING, + self, + num_envs: int, + continuous_actions: bool, + seed: Optional[int], + device: DEVICE_TYPING, ) -> Callable[[], EnvBase]: return lambda: PettingZooWrapper( @@ -38,7 +38,7 @@ def get_env_fun( seed=seed, done_on_any=False, use_mask=False, - device=device + device=device, ) def __get_env(self) -> EnvBase: @@ -51,8 +51,10 @@ def __get_env(self) -> EnvBase: # gather_v5, # tiger_deer_v4 ) - except ImportError as e: - raise ImportError("Module `magent2` not found, install it using `pip install magent2`") + except ImportError: + raise ImportError( + "Module `magent2` not found, install it using `pip install magent2`" + ) envs = { "ADVERSARIAL_PURSUIT": adversarial_pursuit_v4, diff --git a/docs/source/concepts/components.rst b/docs/source/concepts/components.rst index 4c718abb..cb8903d0 100644 --- a/docs/source/concepts/components.rst +++ b/docs/source/concepts/components.rst @@ -91,6 +91,8 @@ They differ based on many aspects, here is a table with the current environments +-------------------------------------------------+-------+---------------------------+--------------+-------------------------------+-----------------------+------------+ | :class:`~benchmarl.environments.MeltingPotTask` | 49 | Cooperative + Competitive | Yes | Independent | Discrete | No | +-------------------------------------------------+-------+---------------------------+--------------+-------------------------------+-----------------------+------------+ + | :class:`~benchmarl.environments.MAgentTask` | 1 | Cooperative + Competitive | Yes | Global in groups | Discrete | No | + +-------------------------------------------------+-------+---------------------------+--------------+-------------------------------+-----------------------+------------+ From 163703a63258c24b371dc8b9e1e55b74cedfa511 Mon Sep 17 00:00:00 2001 From: Matteo Bettini Date: Mon, 25 Nov 2024 20:53:07 +0000 Subject: [PATCH 14/16] empty From 1162ad1c8a5e241ca03033f12fc6b062b383a5d6 Mon Sep 17 00:00:00 2001 From: Matteo Bettini Date: Mon, 25 Nov 2024 20:59:13 +0000 Subject: [PATCH 15/16] fixes --- benchmarl/environments/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/benchmarl/environments/__init__.py b/benchmarl/environments/__init__.py index d3e4689d..55f070e5 100644 --- a/benchmarl/environments/__init__.py +++ b/benchmarl/environments/__init__.py @@ -5,11 +5,12 @@ # from .common import _get_task_config_class, Task + +from .magent.common import MAgentTask from .meltingpot.common import MeltingPotTask from .pettingzoo.common import PettingZooTask from .smacv2.common import Smacv2Task from .vmas.common import VmasTask -from .magent.common import MAgentTask # The enum classes for the environments available. # This is the only object in this file you need to modify when adding a new environment. From ce4043da90b23cdbf5c0c55a086b5bdffc95b881 Mon Sep 17 00:00:00 2001 From: Matteo Bettini Date: Mon, 25 Nov 2024 21:38:49 +0000 Subject: [PATCH 16/16] fixes --- .github/workflows/magent_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/magent_tests.yml b/.github/workflows/magent_tests.yml index b7afdbd2..5b10f6c5 100644 --- a/.github/workflows/magent_tests.yml +++ b/.github/workflows/magent_tests.yml @@ -31,7 +31,7 @@ jobs: - name: Install dependencies run: | bash .github/unittest/install_dependencies_nightly.sh - - name: Install pettingzoo + - name: Install magent2 run: | bash .github/unittest/install_magent2.sh - name: Test with pytest