diff --git a/README.md b/README.md index d36d8a5e..3414e308 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,9 @@ An API conversion tool for popular external reinforcement learning environments to [Gymnasium](https://github.com/farama-Foundation/gymnasium) and [PettingZoo](https://github.com/farama-Foundation/pettingZoo/) APIs. -upported APIs for Gymnasium +Supported APIs for Gymnasium * OpenAI Gym +* Atari Environments * DMControl Supported APIs for PettingZoo @@ -13,10 +14,9 @@ Supported APIs for PettingZoo We are open to supporting more external APIs, please create an issue or ideally, a pull request implementing the new API. +## At a glance -## Get started - -The supported `dm_control` environments are as follows: +This is an example of using Shimmy to convert DM Control environments into a Gymnasium compatible environment: ```python import gymnasium as gym @@ -43,3 +43,159 @@ dm_control/acrobot-swingup-v0 ``` For most usage, we recommend applying the `gym.wrappers.FlattenObservation(env)` wrapper to reduce the `Dict` observation space to a `Box` observation space. + +## Installation and Usage + +To install Shimmy from PyPI: +``` +pip install shimmy +``` +Out of the box, Shimmy doesn't install any of the dependencies required for the environments it supports. +To install them, you'll have to install the optional extras. +All single agent environments have registration under the Gymnasium API, while all multiagent environments must be wrapped using the corresponding compatibility wrappers. + +### OpenAI Gym + +#### Installation +``` +pip install shimmy[gym] +``` + +#### Usage +```python +import gymnasium as gym + +env = gym.make("GymV22CompatibilityV0", env_name="...") +``` + +### Atari Environments + +#### Installation +``` +pip install shimmy[atari] +``` + +#### Usage +```python +import gymnasium as gym + +env = gym.make("ALE/Pong-v5") +``` + +### DM Control (both single and multiagent environments) + +#### Installation +``` +pip install shimmy[dm-control] +``` + +#### Usage (Multi agent) +```python +from dm_control.locomotion import soccer as dm_soccer +from shimmy.dm_control_multiagent_compatibility import ( + DmControlMultiAgentCompatibilityV0, +) + +walker_type = dm_soccer.WalkerType.BOXHEAD, + +env = dm_soccer.load( + team_size=2, + time_limit=10.0, + disable_walker_contacts=False, + enable_field_box=True, + terminate_on_goal=False, + walker_type=walker_type, +) + +env = DmControlMultiAgentCompatibilityV0(env) +``` + +#### Usage (Single agent) +```python +import gymnasium as gym + +env = gym.make("dm_control/acrobot_swingup_sparse-v0") +``` + +### OpenSpiel + +#### Installation +``` +pip install shimmy[pettingzoo] +``` + +#### Usage +```python +import pyspiel +from shimmy.openspiel_compatibility import OpenspielCompatibilityV0 + +env = pyspiel.load_game("2048") +env = OpenspielCompatibilityV0(game=env, render_mode=None) +``` + +### DM Lab + +#### Installation + +Courtesy to [Danijar Hafner](https://github.com/deepmind/lab/issues/242) for providing this install script. +```bash +#!/bin/sh +set -eu + +# Dependencies +apt-get update && apt-get install -y \ + build-essential curl freeglut3 gettext git libffi-dev libglu1-mesa \ + libglu1-mesa-dev libjpeg-dev liblua5.1-0-dev libosmesa6-dev \ + libsdl2-dev lua5.1 pkg-config python-setuptools python3-dev \ + software-properties-common unzip zip zlib1g-dev g++ +pip3 install numpy + +# Bazel +apt-get install -y apt-transport-https curl gnupg +curl -fsSL https://bazel.build/bazel-release.pub.gpg | gpg --dearmor > bazel.gpg +mv bazel.gpg /etc/apt/trusted.gpg.d/ +echo "deb [arch=amd64] https://storage.googleapis.com/bazel-apt stable jdk1.8" | tee /etc/apt/sources.list.d/bazel.list +apt-get update && apt-get install -y bazel + +# Build +git clone https://github.com/deepmind/lab.git +cd lab +echo 'build --cxxopt=-std=c++17' > .bazelrc +bazel build -c opt //python/pip_package:build_pip_package +./bazel-bin/python/pip_package/build_pip_package /tmp/dmlab_pkg +pip3 install --force-reinstall /tmp/dmlab_pkg/deepmind_lab-*.whl +cd .. +rm -rf lab +``` + +#### Usage +```python +import deepmind_lab + +from shimmy.dm_lab_compatibility import DmLabCompatibilityV0 + +observations = ["RGBD"] +config = {"width": "640", "height": "480", "botCount": "2"} +renderer = "hardware" + +env = deepmind_lab.Lab("lt_chasm", observations, config=config, renderer=renderer) +env = DmLabCompatibilityV0(env) +``` + +### For Developers and Testing Only +``` +pip install shimmy[testing] +``` + +### To just install everything +``` +pip install shimmy[all, testing] +``` + +## Citation + +If you use this in your research, please cite: +``` +TBD +``` + diff --git a/setup.py b/setup.py index dbb18220..e6e2fb4c 100644 --- a/setup.py +++ b/setup.py @@ -59,7 +59,7 @@ def get_version(): keywords=["Reinforcement Learning", "game", "RL", "AI"], python_requires=">=3.7", packages=find_packages(), - install_requires=["numpy>=1.18.0", "gymnasium>=0.26.0"], + install_requires=["numpy>=1.18.0", "gymnasium>=0.27.0"], tests_require=extras["testing"], extras_require=extras, classifiers=[ diff --git a/shimmy/__init__.py b/shimmy/__init__.py index 9ef5aedd..0a3ca8fc 100644 --- a/shimmy/__init__.py +++ b/shimmy/__init__.py @@ -26,8 +26,10 @@ pass __all__ = [ - "DmControlCompatibility", - "OpenspielWrapperV0", - "GymV22Compatibility", - "GymV26Compatibility", + "DmControlCompatibilityV0", + "DmControlMultiAgentCompatibilityV0", + "OpenspielCompatibilityV0", + "DmLabCompatibilityV0", + "GymV22CompatibilityV0", + "GymV26CompatibilityV0", ] diff --git a/shimmy/dm_control_compatibility.py b/shimmy/dm_control_compatibility.py index 67dd040b..90dd9956 100644 --- a/shimmy/dm_control_compatibility.py +++ b/shimmy/dm_control_compatibility.py @@ -15,7 +15,7 @@ from dm_control import composer from dm_control.rl import control from gymnasium.core import ObsType -from gymnasium.envs.mujoco.mujoco_rendering import Viewer +from gymnasium.envs.mujoco.mujoco_rendering import MujocoRenderer from shimmy.utils.dm_env import dm_control_step2gym_step, dm_spec2gym_space @@ -68,7 +68,7 @@ def __init__( if self.render_mode == "human": # We use the gymnasium mujoco rendering, dm-control provides more complex rendering options. - self.viewer = Viewer( + self.viewer = MujocoRenderer( self._env.physics.model.ptr, self._env.physics.data.ptr ) diff --git a/shimmy/dm_control_multiagent_compatibility.py b/shimmy/dm_control_multiagent_compatibility.py index c73c3cb5..a8a8cf12 100644 --- a/shimmy/dm_control_multiagent_compatibility.py +++ b/shimmy/dm_control_multiagent_compatibility.py @@ -8,7 +8,7 @@ import dm_control.composer import dm_env import gymnasium -from gymnasium.envs.mujoco.mujoco_rendering import Viewer +from gymnasium.envs.mujoco.mujoco_rendering import MujocoRenderer from pettingzoo import ParallelEnv from shimmy.utils.dm_env import dm_obs2gym_obs, dm_spec2gym_space @@ -100,7 +100,7 @@ def __init__( self.act_spaces = dict(zip(self.possible_agents, all_act_spaces)) if self.render_mode == "human": - self.viewer = Viewer( + self.viewer = MujocoRenderer( self._env.physics.model.ptr, self._env.physics.data.ptr ) diff --git a/tests/test_atari.py b/tests/test_atari.py index 116ee47d..0ccd3066 100644 --- a/tests/test_atari.py +++ b/tests/test_atari.py @@ -20,7 +20,8 @@ def test_all_atari_roms(): CHECK_ENV_IGNORE_WARNINGS = [ f"\x1b[33mWARN: {message}\x1b[0m" for message in [ - "Official support for the `seed` function is dropped. Standard practice is to reset gymnasium environments using `env.reset(seed=)`" + "Official support for the `seed` function is dropped. Standard practice is to reset gymnasium environments using `env.reset(seed=)`", + "No render fps was declared in the environment (env.metadata['render_fps'] is None or not defined), rendering may occur at inconsistent fps.", ] ] diff --git a/tests/test_dm_control.py b/tests/test_dm_control.py index eb80df35..71f86e79 100644 --- a/tests/test_dm_control.py +++ b/tests/test_dm_control.py @@ -53,8 +53,12 @@ def test_dm_control_suite_envs(): "A Box observation space has an unconventional shape (neither an image, nor a 1D vector). We recommend flattening the observation to have only a 1D vector or use a custom policy to properly process the data. Actual observation shape: (1, 5)", "It seems a Box observation space is an image but the `dtype` is not `np.uint8`, actual type: float64. If the Box observation space is not an image, we recommend flattening the observation to have only a 1D vector.", "It seems a Box observation space is an image but the upper and lower bounds are not in [0, 255]. Generally, CNN policies assume observations are within that range, so you may encounter an issue if the observation values are not.", + "arrays to stack must be passed as a 'sequence' type such as list or tuple. Support for non-sequence iterables such as generators is deprecated as of NumPy 1.16 and will raise an error in the future.", ] ] +CHECK_ENV_IGNORE_WARNINGS.append( + 'arrays to stack must be passed as a "sequence" type such as list or tuple. Support for non-sequence iterables such as generators is deprecated as of NumPy 1.16 and will raise an error in the future.', +) @pytest.mark.parametrize("env_id", DM_CONTROL_ENV_IDS) @@ -158,7 +162,7 @@ def test_dm_control_wrappers( env = DmControlCompatibilityV0(wrapped_env) with warnings.catch_warnings(record=True) as caught_warnings: - check_env(env) + check_env(env, skip_render_check=True) for warning_message in caught_warnings: assert isinstance(warning_message.message, Warning) @@ -166,5 +170,5 @@ def test_dm_control_wrappers( raise Error(f"Unexpected warning: {warning_message.message}") env = gym.make("dm_control/compatibility-env-v0", env=wrapped_env) - check_env(env) + check_env(env, skip_render_check=True) env.close() diff --git a/tests/test_dm_control_multiagent.py b/tests/test_dm_control_multiagent.py index d2eaaac8..923f6c64 100644 --- a/tests/test_dm_control_multiagent.py +++ b/tests/test_dm_control_multiagent.py @@ -1,9 +1,7 @@ """Tests the multi-agent dm-control soccer environment.""" -import gymnasium import pytest from dm_control.locomotion import soccer as dm_soccer -from gymnasium.utils.env_checker import data_equivalence from pettingzoo.test import parallel_api_test from shimmy.dm_control_multiagent_compatibility import ( diff --git a/tests/test_dm_lab.py b/tests/test_dm_lab.py new file mode 100644 index 00000000..27b3dfb4 --- /dev/null +++ b/tests/test_dm_lab.py @@ -0,0 +1,25 @@ +"""Tests the multi-agent dm-control soccer environment.""" + +import gymnasium +import pytest +from gymnasium.utils.env_checker import check_env + +from shimmy.dm_lab_compatibility import DmLabCompatibilityV0 + + +@pytest.mark.skip(reason="no way of currently testing this") +def test_check_env(): + """Check that environment pass the gym check_env.""" + import deepmind_lab + + observations = ["RGBD"] + config = {"width": "640", "height": "480", "botCount": "2"} + renderer = "hardware" + + env = deepmind_lab.Lab("lt_chasm", observations, config=config, renderer=renderer) + + env = DmLabCompatibilityV0(env) + + check_env(env) + + env.close() diff --git a/tests/test_gym.py b/tests/test_gym.py index 8caf79da..b06a469a 100644 --- a/tests/test_gym.py +++ b/tests/test_gym.py @@ -34,7 +34,7 @@ def test_gym_conversion_by_id(env_id): env = gymnasium.make("GymV26Environment-v0", env_id=env_id).unwrapped with warnings.catch_warnings(record=True) as caught_warnings: - check_env(env) + check_env(env, skip_render_check=True) for warning in caught_warnings: if ( @@ -55,7 +55,7 @@ def test_gym_conversion_instantiated(env_id): env = gymnasium.make("GymV26Environment-v0", env=env).unwrapped with warnings.catch_warnings(record=True) as caught_warnings: - check_env(env) + check_env(env, skip_render_check=True) for warning in caught_warnings: if (