diff --git a/.buildinfo b/.buildinfo index c40858d5c..2f9352bde 100644 --- a/.buildinfo +++ b/.buildinfo @@ -1,4 +1,4 @@ # Sphinx build info version 1 # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. -config: 309340cd494e6110e8779bfdf20952bc +config: c29ef785859083c891d7d61febe82c92 tags: d77d1c0d9ca2f4c8421862c7c5a0d620 diff --git a/404.html b/404.html index afa3c4407..0b7a7bc42 100644 --- a/404.html +++ b/404.html @@ -15,7 +15,11 @@ 404 - Page Not Found - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

Tutorials

Development

@@ -465,11 +477,11 @@ const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -639,7 +651,7 @@ githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/README/index.html b/README/index.html index 0560feb15..6d2e32338 100644 --- a/README/index.html +++ b/README/index.html @@ -15,7 +15,11 @@ Gymnasium-docs - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

Tutorials

Development

@@ -545,11 +557,11 @@ const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -719,7 +731,7 @@ githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/_downloads/3831a62128c6d96d80d039f936893259/environment_creation.ipynb b/_downloads/0f28446f9f426c9833f40d61857a6f21/environment_creation.ipynb similarity index 92% rename from _downloads/3831a62128c6d96d80d039f936893259/environment_creation.ipynb rename to _downloads/0f28446f9f426c9833f40d61857a6f21/environment_creation.ipynb index a9bc7416c..62d5c387d 100644 --- a/_downloads/3831a62128c6d96d80d039f936893259/environment_creation.ipynb +++ b/_downloads/0f28446f9f426c9833f40d61857a6f21/environment_creation.ipynb @@ -15,7 +15,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "\n# Make your own custom environment\n\nThis documentation overviews creating new environments and relevant\nuseful wrappers, utilities and tests included in Gymnasium designed for\nthe creation of new environments. You can clone gym-examples to play\nwith the code that is presented here. We recommend that you use a virtual environment:\n\n.. code:: console\n\n git clone https://github.com/Farama-Foundation/gym-examples\n cd gym-examples\n python -m venv .env\n source .env/bin/activate\n pip install -e .\n\n## Subclassing gymnasium.Env\n\nBefore learning how to create your own environment you should check out\n[the documentation of Gymnasium\u2019s API](/api/core)_.\n\nWe will be concerned with a subset of gym-examples that looks like this:\n\n.. code:: sh\n\n gym-examples/\n README.md\n setup.py\n gym_examples/\n __init__.py\n envs/\n __init__.py\n grid_world.py\n wrappers/\n __init__.py\n relative_position.py\n reacher_weighted_reward.py\n discrete_action.py\n clip_reward.py\n\nTo illustrate the process of subclassing ``gymnasium.Env``, we will\nimplement a very simplistic game, called ``GridWorldEnv``. We will write\nthe code for our custom environment in\n``gym-examples/gym_examples/envs/grid_world.py``. The environment\nconsists of a 2-dimensional square grid of fixed size (specified via the\n``size`` parameter during construction). The agent can move vertically\nor horizontally between grid cells in each timestep. The goal of the\nagent is to navigate to a target on the grid that has been placed\nrandomly at the beginning of the episode.\n\n- Observations provide the location of the target and agent.\n- There are 4 actions in our environment, corresponding to the\n movements \u201cright\u201d, \u201cup\u201d, \u201cleft\u201d, and \u201cdown\u201d.\n- A done signal is issued as soon as the agent has navigated to the\n grid cell where the target is located.\n- Rewards are binary and sparse, meaning that the immediate reward is\n always zero, unless the agent has reached the target, then it is 1.\n\nAn episode in this environment (with ``size=5``) might look like this:\n\nwhere the blue dot is the agent and the red square represents the\ntarget.\n\nLet us look at the source code of ``GridWorldEnv`` piece by piece:\n" + "\n# Make your own custom environment\n\nThis documentation overviews creating new environments and relevant\nuseful wrappers, utilities and tests included in Gymnasium designed for\nthe creation of new environments. You can clone gym-examples to play\nwith the code that is presented here. We recommend that you use a virtual environment:\n\n.. code:: console\n\n git clone https://github.com/Farama-Foundation/gym-examples\n cd gym-examples\n python -m venv .env\n source .env/bin/activate\n pip install -e .\n\n## Subclassing gymnasium.Env\n\nBefore learning how to create your own environment you should check out\n[the documentation of Gymnasium\u2019s API](/api/env)_.\n\nWe will be concerned with a subset of gym-examples that looks like this:\n\n.. code:: sh\n\n gym-examples/\n README.md\n setup.py\n gym_examples/\n __init__.py\n envs/\n __init__.py\n grid_world.py\n wrappers/\n __init__.py\n relative_position.py\n reacher_weighted_reward.py\n discrete_action.py\n clip_reward.py\n\nTo illustrate the process of subclassing ``gymnasium.Env``, we will\nimplement a very simplistic game, called ``GridWorldEnv``. We will write\nthe code for our custom environment in\n``gym-examples/gym_examples/envs/grid_world.py``. The environment\nconsists of a 2-dimensional square grid of fixed size (specified via the\n``size`` parameter during construction). The agent can move vertically\nor horizontally between grid cells in each timestep. The goal of the\nagent is to navigate to a target on the grid that has been placed\nrandomly at the beginning of the episode.\n\n- Observations provide the location of the target and agent.\n- There are 4 actions in our environment, corresponding to the\n movements \u201cright\u201d, \u201cup\u201d, \u201cleft\u201d, and \u201cdown\u201d.\n- A done signal is issued as soon as the agent has navigated to the\n grid cell where the target is located.\n- Rewards are binary and sparse, meaning that the immediate reward is\n always zero, unless the agent has reached the target, then it is 1.\n\nAn episode in this environment (with ``size=5``) might look like this:\n\nwhere the blue dot is the agent and the red square represents the\ntarget.\n\nLet us look at the source code of ``GridWorldEnv`` piece by piece:\n" ] }, { @@ -196,7 +196,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.15" + "version": "3.9.16" } }, "nbformat": 4, diff --git a/_downloads/315c4c52fb68082a731b192d944e2ede/tutorials_python.zip b/_downloads/315c4c52fb68082a731b192d944e2ede/tutorials_python.zip new file mode 100644 index 000000000..d859f3bcc Binary files /dev/null and b/_downloads/315c4c52fb68082a731b192d944e2ede/tutorials_python.zip differ diff --git a/_downloads/bee952c8ccdb09b22233577d70a62253/handling_time_limits.ipynb b/_downloads/46c1a6ff1aae3b48b3379fddfeca0650/handling_time_limits.ipynb similarity index 99% rename from _downloads/bee952c8ccdb09b22233577d70a62253/handling_time_limits.ipynb rename to _downloads/46c1a6ff1aae3b48b3379fddfeca0650/handling_time_limits.ipynb index 450213fca..4e6342272 100644 --- a/_downloads/bee952c8ccdb09b22233577d70a62253/handling_time_limits.ipynb +++ b/_downloads/46c1a6ff1aae3b48b3379fddfeca0650/handling_time_limits.ipynb @@ -49,7 +49,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.15" + "version": "3.9.16" } }, "nbformat": 4, diff --git a/_downloads/50e7c09c20b787d0a5bd70c4aeb0a515/vector_envs_tutorial.ipynb b/_downloads/50e7c09c20b787d0a5bd70c4aeb0a515/vector_envs_tutorial.ipynb new file mode 100644 index 000000000..498543f5e --- /dev/null +++ b/_downloads/50e7c09c20b787d0a5bd70c4aeb0a515/vector_envs_tutorial.ipynb @@ -0,0 +1,325 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "%matplotlib inline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# Training A2C with Vector Envs and Domain Randomization\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Introduction\n\nIn this tutorial, you'll learn how to use vectorized environments to train an Advantage Actor-Critic agent.\nWe are going to use A2C, which is the synchronous version of the A3C algorithm [1].\n\nVectorized environments [3] can help to achieve quicker and more robust training by allowing multiple instances\nof the same environment to run in parallel (on multiple CPUs). This can significantly reduce the variance and thus speeds up the training.\n\nWe will implement an Advantage Actor-Critic from scratch to look at how you can feed batched states into your networks to get a vector of actions\n(one action per environment) and calculate the losses for actor and critic on minibatches of transitions.\nEach minibatch contains the transitions of one sampling phase: `n_steps_per_update` steps are executed in `n_envs` environments in parallel\n(multiply the two to get the number of transitions in a minibatch). After each sampling phase, the losses are calculated and one gradient step is executed.\nTo calculate the advantages, we are going to use the Generalized Advantage Estimation (GAE) method [2], which balances the tradeoff\nbetween variance and bias of the advantage estimates.\n\nThe A2C agent class is initialized with the number of features of the input state, the number of actions the agent can take,\nthe learning rates and the number of environments that run in parallel to collect experiences. The actor and critic networks are defined\nand their respective optimizers are initialized. The forward pass of the networks takes in a batched vector of states and returns a tensor of state values\nand a tensor of action logits. The select_action method returns a tuple of the chosen actions, the log-probs of those actions, and the state values for each action.\nIn addition, it also returns the entropy of the policy distribution, which is subtracted from the loss later (with a weighting factor `ent_coef`) to encourage exploration.\n\nThe get_losses function calculates the losses for the actor and critic networks (using GAE), which are then updated using the update_parameters function.\n\n\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "------------------------------\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Author: Till Zemann\n# License: MIT License\n\nfrom __future__ import annotations\n\nimport os\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport torch\nimport torch.nn as nn\nfrom torch import optim\nfrom tqdm import tqdm\n\nimport gymnasium as gym" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Advantage Actor-Critic (A2C)\n\nThe Actor-Critic combines elements of value-based and policy-based methods. In A2C, the agent has two separate neural networks:\na critic network that estimates the state-value function, and an actor network that outputs logits for a categorical probability distribution over all actions.\nThe critic network is trained to minimize the mean squared error between the predicted state values and the actual returns received by the agent\n(this is equivalent to minimizing the squared advantages, because the advantage of an action is as the difference between the return and the state-value: A(s,a) = Q(s,a) - V(s).\nThe actor network is trained to maximize the expected return by selecting actions that have high expected values according to the critic network.\n\nThe focus of this tutorial will not be on the details of A2C itself. Instead, the tutorial will focus on how to use vectorized environments\nand domain randomization to accelerate the training process for A2C (and other reinforcement learning algorithms).\n\n\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "------------------------------\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "class A2C(nn.Module):\n \"\"\"\n (Synchronous) Advantage Actor-Critic agent class\n\n Args:\n n_features: The number of features of the input state.\n n_actions: The number of actions the agent can take.\n device: The device to run the computations on (running on a GPU might be quicker for larger Neural Nets,\n for this code CPU is totally fine).\n critic_lr: The learning rate for the critic network (should usually be larger than the actor_lr).\n actor_lr: The learning rate for the actor network.\n n_envs: The number of environments that run in parallel (on multiple CPUs) to collect experiences.\n \"\"\"\n\n def __init__(\n self,\n n_features: int,\n n_actions: int,\n device: torch.device,\n critic_lr: float,\n actor_lr: float,\n n_envs: int,\n ) -> None:\n \"\"\"Initializes the actor and critic networks and their respective optimizers.\"\"\"\n super().__init__()\n self.device = device\n self.n_envs = n_envs\n\n critic_layers = [\n nn.Linear(n_features, 32),\n nn.ReLU(),\n nn.Linear(32, 32),\n nn.ReLU(),\n nn.Linear(32, 1), # estimate V(s)\n ]\n\n actor_layers = [\n nn.Linear(n_features, 32),\n nn.ReLU(),\n nn.Linear(32, 32),\n nn.ReLU(),\n nn.Linear(\n 32, n_actions\n ), # estimate action logits (will be fed into a softmax later)\n ]\n\n # define actor and critic networks\n self.critic = nn.Sequential(*critic_layers).to(self.device)\n self.actor = nn.Sequential(*actor_layers).to(self.device)\n\n # define optimizers for actor and critic\n self.critic_optim = optim.RMSprop(self.critic.parameters(), lr=critic_lr)\n self.actor_optim = optim.RMSprop(self.actor.parameters(), lr=actor_lr)\n\n def forward(self, x: np.ndarray) -> tuple[torch.Tensor, torch.Tensor]:\n \"\"\"\n Forward pass of the networks.\n\n Args:\n x: A batched vector of states.\n\n Returns:\n state_values: A tensor with the state values, with shape [n_envs,].\n action_logits_vec: A tensor with the action logits, with shape [n_envs, n_actions].\n \"\"\"\n x = torch.Tensor(x).to(self.device)\n state_values = self.critic(x) # shape: [n_envs,]\n action_logits_vec = self.actor(x) # shape: [n_envs, n_actions]\n return (state_values, action_logits_vec)\n\n def select_action(\n self, x: np.ndarray\n ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:\n \"\"\"\n Returns a tuple of the chosen actions and the log-probs of those actions.\n\n Args:\n x: A batched vector of states.\n\n Returns:\n actions: A tensor with the actions, with shape [n_steps_per_update, n_envs].\n action_log_probs: A tensor with the log-probs of the actions, with shape [n_steps_per_update, n_envs].\n state_values: A tensor with the state values, with shape [n_steps_per_update, n_envs].\n \"\"\"\n state_values, action_logits = self.forward(x)\n action_pd = torch.distributions.Categorical(\n logits=action_logits\n ) # implicitly uses softmax\n actions = action_pd.sample()\n action_log_probs = action_pd.log_prob(actions)\n entropy = action_pd.entropy()\n return (actions, action_log_probs, state_values, entropy)\n\n def get_losses(\n self,\n rewards: torch.Tensor,\n action_log_probs: torch.Tensor,\n value_preds: torch.Tensor,\n entropy: torch.Tensor,\n masks: torch.Tensor,\n gamma: float,\n lam: float,\n ent_coef: float,\n device: torch.device,\n ) -> tuple[torch.Tensor, torch.Tensor]:\n \"\"\"\n Computes the loss of a minibatch (transitions collected in one sampling phase) for actor and critic\n using Generalized Advantage Estimation (GAE) to compute the advantages (https://arxiv.org/abs/1506.02438).\n\n Args:\n rewards: A tensor with the rewards for each time step in the episode, with shape [n_steps_per_update, n_envs].\n action_log_probs: A tensor with the log-probs of the actions taken at each time step in the episode, with shape [n_steps_per_update, n_envs].\n value_preds: A tensor with the state value predictions for each time step in the episode, with shape [n_steps_per_update, n_envs].\n masks: A tensor with the masks for each time step in the episode, with shape [n_steps_per_update, n_envs].\n gamma: The discount factor.\n lam: The GAE hyperparameter. (lam=1 corresponds to Monte-Carlo sampling with high variance and no bias,\n and lam=0 corresponds to normal TD-Learning that has a low variance but is biased\n because the estimates are generated by a Neural Net).\n device: The device to run the computations on (e.g. CPU or GPU).\n\n Returns:\n critic_loss: The critic loss for the minibatch.\n actor_loss: The actor loss for the minibatch.\n \"\"\"\n T = len(rewards)\n advantages = torch.zeros(T, self.n_envs, device=device)\n\n # compute the advantages using GAE\n gae = 0.0\n for t in reversed(range(T - 1)):\n td_error = (\n rewards[t] + gamma * masks[t] * value_preds[t + 1] - value_preds[t]\n )\n gae = td_error + gamma * lam * masks[t] * gae\n advantages[t] = gae\n\n # calculate the loss of the minibatch for actor and critic\n critic_loss = advantages.pow(2).mean()\n\n # give a bonus for higher entropy to encourage exploration\n actor_loss = (\n -(advantages.detach() * action_log_probs).mean() - ent_coef * entropy.mean()\n )\n return (critic_loss, actor_loss)\n\n def update_parameters(\n self, critic_loss: torch.Tensor, actor_loss: torch.Tensor\n ) -> None:\n \"\"\"\n Updates the parameters of the actor and critic networks.\n\n Args:\n critic_loss: The critic loss.\n actor_loss: The actor loss.\n \"\"\"\n self.critic_optim.zero_grad()\n critic_loss.backward()\n self.critic_optim.step()\n\n self.actor_optim.zero_grad()\n actor_loss.backward()\n self.actor_optim.step()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Using Vectorized Environments\n\nWhen you calculate the losses for the two Neural Networks over only one epoch, it might have a high variance. With vectorized environments,\nwe can play with `n_envs` in parallel and thus get up to a linear speedup (meaning that in theory, we collect samples `n_envs` times quicker)\nthat we can use to calculate the loss for the current policy and critic network. When we are using more samples to calculate the loss,\nit will have a lower variance and theirfore leads to quicker learning.\n\nA2C is a synchronous method, meaning that the parameter updates to Networks take place deterministically (after each sampling phase),\nbut we can still make use of asynchronous vector envs to spawn multiple processes for parallel environment execution.\n\nThe simplest way to create vector environments is by calling `gym.vector.make`, which creates multiple instances of the same environment:\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "envs = gym.vector.make(\"LunarLander-v2\", num_envs=3, max_episode_steps=600)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Domain Randomization\n\nIf we want to randomize the environment for training to get more robust agents (that can deal with different parameterizations of an environment\nand theirfore might have a higher degree of generalization), we can set the desired parameters manually or use a pseudo-random number generator to generate them.\n\nManually setting up 3 parallel 'LunarLander-v2' envs with different parameters:\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "envs = gym.vector.AsyncVectorEnv(\n [\n lambda: gym.make(\n \"LunarLander-v2\",\n gravity=-10.0,\n enable_wind=True,\n wind_power=15.0,\n turbulence_power=1.5,\n max_episode_steps=600,\n ),\n lambda: gym.make(\n \"LunarLander-v2\",\n gravity=-9.8,\n enable_wind=True,\n wind_power=10.0,\n turbulence_power=1.3,\n max_episode_steps=600,\n ),\n lambda: gym.make(\n \"LunarLander-v2\", gravity=-7.0, enable_wind=False, max_episode_steps=600\n ),\n ]\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "------------------------------\n\nRandomly generating the parameters for 3 parallel 'LunarLander-v2' envs, using `np.clip` to stay in the recommended parameter space:\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "envs = gym.vector.AsyncVectorEnv(\n [\n lambda: gym.make(\n \"LunarLander-v2\",\n gravity=np.clip(\n np.random.normal(loc=-10.0, scale=1.0), a_min=-11.99, a_max=-0.01\n ),\n enable_wind=np.random.choice([True, False]),\n wind_power=np.clip(\n np.random.normal(loc=15.0, scale=1.0), a_min=0.01, a_max=19.99\n ),\n turbulence_power=np.clip(\n np.random.normal(loc=1.5, scale=0.5), a_min=0.01, a_max=1.99\n ),\n max_episode_steps=600,\n )\n for i in range(3)\n ]\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "------------------------------\n\nHere we are using normal distributions with the standard parameterization of the environment as the mean and an arbitrary standard deviation (scale).\nDepending on the problem, you can experiment with higher variance and use different distributions as well.\n\nIf you are training on the same `n_envs` environments for the entire training time, and `n_envs` is a relatively low number\n(in proportion to how complex the environment is), you might still get some overfitting to the specific parameterizations that you picked.\nTo mitigate this, you can either pick a high number of randomly parameterized environments or remake your environments every couple of sampling phases\nto generate a new set of pseudo-random parameters.\n\n\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setup\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# environment hyperparams\nn_envs = 10\nn_updates = 1000\nn_steps_per_update = 128\nrandomize_domain = False\n\n# agent hyperparams\ngamma = 0.999\nlam = 0.95 # hyperparameter for GAE\nent_coef = 0.01 # coefficient for the entropy bonus (to encourage exploration)\nactor_lr = 0.001\ncritic_lr = 0.005\n\n# Note: the actor has a slower learning rate so that the value targets become\n# more stationary and are theirfore easier to estimate for the critic\n\n# environment setup\nif randomize_domain:\n envs = gym.vector.AsyncVectorEnv(\n [\n lambda: gym.make(\n \"LunarLander-v2\",\n gravity=np.clip(\n np.random.normal(loc=-10.0, scale=1.0), a_min=-11.99, a_max=-0.01\n ),\n enable_wind=np.random.choice([True, False]),\n wind_power=np.clip(\n np.random.normal(loc=15.0, scale=1.0), a_min=0.01, a_max=19.99\n ),\n turbulence_power=np.clip(\n np.random.normal(loc=1.5, scale=0.5), a_min=0.01, a_max=1.99\n ),\n max_episode_steps=600,\n )\n for i in range(n_envs)\n ]\n )\n\nelse:\n envs = gym.vector.make(\"LunarLander-v2\", num_envs=n_envs, max_episode_steps=600)\n\n\nobs_shape = envs.single_observation_space.shape[0]\naction_shape = envs.single_action_space.n\n\n# set the device\nuse_cuda = False\nif use_cuda:\n device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nelse:\n device = torch.device(\"cpu\")\n\n# init the agent\nagent = A2C(obs_shape, action_shape, device, critic_lr, actor_lr, n_envs)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Training the A2C Agent\n\nFor our training loop, we are using the `RecordEpisodeStatistics` wrapper to record the episode lengths and returns and we are also saving\nthe losses and entropies to plot them after the agent finished training.\n\nYou may notice that the don't reset the vectorized envs at the start of each episode like we would usually do.\nThis is because each environment resets automatically once the episode finishes (each environment takes a different number of timesteps to finish\nan episode because of the random seeds). As a result, we are also not collecting data in `episodes`, but rather just play a certain number of steps\n(`n_steps_per_update`) in each environment (as an example, this could mean that we play 20 timesteps to finish an episode and then\nuse the rest of the timesteps to begin a new one).\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# create a wrapper environment to save episode returns and episode lengths\nenvs_wrapper = gym.wrappers.RecordEpisodeStatistics(envs, deque_size=n_envs * n_updates)\n\ncritic_losses = []\nactor_losses = []\nentropies = []\n\n# use tqdm to get a progress bar for training\nfor sample_phase in tqdm(range(n_updates)):\n\n # we don't have to reset the envs, they just continue playing\n # until the episode is over and then reset automatically\n\n # reset lists that collect experiences of an episode (sample phase)\n ep_value_preds = torch.zeros(n_steps_per_update, n_envs, device=device)\n ep_rewards = torch.zeros(n_steps_per_update, n_envs, device=device)\n ep_action_log_probs = torch.zeros(n_steps_per_update, n_envs, device=device)\n masks = torch.zeros(n_steps_per_update, n_envs, device=device)\n\n # at the start of training reset all envs to get an initial state\n if sample_phase == 0:\n states, info = envs_wrapper.reset(seed=42)\n\n # play n steps in our parallel environments to collect data\n for step in range(n_steps_per_update):\n\n # select an action A_{t} using S_{t} as input for the agent\n actions, action_log_probs, state_value_preds, entropy = agent.select_action(\n states\n )\n\n # perform the action A_{t} in the environment to get S_{t+1} and R_{t+1}\n states, rewards, terminated, truncated, infos = envs_wrapper.step(\n actions.numpy()\n )\n\n ep_value_preds[step] = torch.squeeze(state_value_preds)\n ep_rewards[step] = torch.tensor(rewards, device=device)\n ep_action_log_probs[step] = action_log_probs\n\n # add a mask (for the return calculation later);\n # for each env the mask is 1 if the episode is ongoing and 0 if it is terminated (not by truncation!)\n masks[step] = torch.tensor([not term for term in terminated])\n\n # calculate the losses for actor and critic\n critic_loss, actor_loss = agent.get_losses(\n ep_rewards,\n ep_action_log_probs,\n ep_value_preds,\n entropy,\n masks,\n gamma,\n lam,\n ent_coef,\n device,\n )\n\n # update the actor and critic networks\n agent.update_parameters(critic_loss, actor_loss)\n\n # log the losses and entropy\n critic_losses.append(critic_loss.detach().cpu().numpy())\n actor_losses.append(actor_loss.detach().cpu().numpy())\n entropies.append(entropy.detach().mean().cpu().numpy())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Plotting\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "\"\"\" plot the results \"\"\"\n\n# %matplotlib inline\n\nrolling_length = 20\nfig, axs = plt.subplots(nrows=2, ncols=2, figsize=(12, 5))\nfig.suptitle(\n f\"Training plots for {agent.__class__.__name__} in the LunarLander-v2 environment \\n \\\n (n_envs={n_envs}, n_steps_per_update={n_steps_per_update}, randomize_domain={randomize_domain})\"\n)\n\n# episode return\naxs[0][0].set_title(\"Episode Returns\")\nepisode_returns_moving_average = (\n np.convolve(\n np.array(envs_wrapper.return_queue).flatten(),\n np.ones(rolling_length),\n mode=\"valid\",\n )\n / rolling_length\n)\naxs[0][0].plot(\n np.arange(len(episode_returns_moving_average)) / n_envs,\n episode_returns_moving_average,\n)\naxs[0][0].set_xlabel(\"Number of episodes\")\n\n# entropy\naxs[1][0].set_title(\"Entropy\")\nentropy_moving_average = (\n np.convolve(np.array(entropies), np.ones(rolling_length), mode=\"valid\")\n / rolling_length\n)\naxs[1][0].plot(entropy_moving_average)\naxs[1][0].set_xlabel(\"Number of updates\")\n\n\n# critic loss\naxs[0][1].set_title(\"Critic Loss\")\ncritic_losses_moving_average = (\n np.convolve(\n np.array(critic_losses).flatten(), np.ones(rolling_length), mode=\"valid\"\n )\n / rolling_length\n)\naxs[0][1].plot(critic_losses_moving_average)\naxs[0][1].set_xlabel(\"Number of updates\")\n\n\n# actor loss\naxs[1][1].set_title(\"Actor Loss\")\nactor_losses_moving_average = (\n np.convolve(np.array(actor_losses).flatten(), np.ones(rolling_length), mode=\"valid\")\n / rolling_length\n)\naxs[1][1].plot(actor_losses_moving_average)\naxs[1][1].set_xlabel(\"Number of updates\")\n\nplt.tight_layout()\nplt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\"training_plots\"\n\n\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Performance Analysis of Synchronous and Asynchronous Vectorized Environments\n\n\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "------------------------------\n\nAsynchronous environments can lead to quicker training times and a higher speedup\nfor data collection compared to synchronous environments. This is because asynchronous environments\nallow multiple agents to interact with their environments in parallel,\nwhile synchronous environments run multiple environments serially.\nThis results in better efficiency and faster training times for asynchronous environments.\n\n\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\"performance_plots\"\n\n\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "------------------------------\n\nAccording to the Karp-Flatt metric (a metric used in parallel computing to estimate the limit for the\nspeedup when scaling up the number of parallel processes, here the number of environments),\nthe estimated max. speedup for asynchronous environments is 57, while the estimated maximum speedup\nfor synchronous environments is 21. This suggests that asynchronous environments have significantly\nfaster training times compared to synchronous environments (see graphs).\n\n\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\"karp_flatt_metric\"\n\n\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "------------------------------\n\nHowever, it is important to note that increasing the number of parallel vector environments\ncan lead to slower training times after a certain number of environments (see plot below, where the\nagent was trained until the mean training returns were above -120). The slower training times might occur\nbecause the gradients of the environments are good enough after a relatively low number of environments\n(especially if the environment is not very complex). In this case, increasing the number of environments\ndoes not increase the learning speed, and actually increases the runtime, possibly due to the additional time\nneeded to calculate the gradients. For LunarLander-v2, the best performing configuration used a AsyncVectorEnv\nwith 10 parallel environments, but environments with a higher complexity may require more\nparallel environments to achieve optimal performance.\n\n\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\"runtime_until_threshold_plot\"\n\n\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Saving/ Loading Weights\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "save_weights = False\nload_weights = False\n\nactor_weights_path = \"weights/actor_weights.h5\"\ncritic_weights_path = \"weights/critic_weights.h5\"\n\nif not os.path.exists(\"weights\"):\n os.mkdir(\"weights\")\n\n\"\"\" save network weights \"\"\"\nif save_weights:\n torch.save(agent.actor.state_dict(), actor_weights_path)\n torch.save(agent.critic.state_dict(), critic_weights_path)\n\n\n\"\"\" load network weights \"\"\"\nif load_weights:\n agent = A2C(obs_shape, action_shape, device, critic_lr, actor_lr)\n\n agent.actor.load_state_dict(torch.load(actor_weights_path))\n agent.critic.load_state_dict(torch.load(critic_weights_path))\n agent.actor.eval()\n agent.critic.eval()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Showcase the Agent\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "\"\"\" play a couple of showcase episodes \"\"\"\n\nn_showcase_episodes = 3\n\nfor episode in range(n_showcase_episodes):\n print(f\"starting episode {episode}...\")\n\n # create a new sample environment to get new random parameters\n if randomize_domain:\n env = gym.make(\n \"LunarLander-v2\",\n render_mode=\"human\",\n gravity=np.clip(\n np.random.normal(loc=-10.0, scale=2.0), a_min=-11.99, a_max=-0.01\n ),\n enable_wind=np.random.choice([True, False]),\n wind_power=np.clip(\n np.random.normal(loc=15.0, scale=2.0), a_min=0.01, a_max=19.99\n ),\n turbulence_power=np.clip(\n np.random.normal(loc=1.5, scale=1.0), a_min=0.01, a_max=1.99\n ),\n max_episode_steps=500,\n )\n else:\n env = gym.make(\"LunarLander-v2\", render_mode=\"human\", max_episode_steps=500)\n\n # get an initial state\n state, info = env.reset()\n\n # play one episode\n done = False\n while not done:\n\n # select an action A_{t} using S_{t} as input for the agent\n with torch.no_grad():\n action, _, _, _ = agent.select_action(state[None, :])\n\n # perform the action A_{t} in the environment to get S_{t+1} and R_{t+1}\n state, reward, terminated, truncated, info = env.step(action.item())\n\n # update if the environment is done\n done = terminated or truncated\n\nenv.close()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Try playing the environment yourself\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# from gymnasium.utils.play import play\n#\n# play(gym.make('LunarLander-v2', render_mode='rgb_array'),\n# keys_to_action={'w': 2, 'a': 1, 'd': 3}, noop=0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## References\n\n[1] V. Mnih, A. P. Badia, M. Mirza, A. Graves, T. P. Lillicrap, T. Harley, D. Silver, K. Kavukcuoglu. \"Asynchronous Methods for Deep Reinforcement Learning\" ICML (2016).\n\n[2] J. Schulman, P. Moritz, S. Levine, M. Jordan and P. Abbeel. \"High-dimensional continuous control using generalized advantage estimation.\" ICLR (2016).\n\n[3] Gymnasium Documentation: Vector environments. (URL: https://gymnasium.farama.org/api/vector/)\n\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.16" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/_downloads/01a413564c9bff768b24ed43b946607d/environment_creation.py b/_downloads/56585a5841cc0f2c5a3dea777f5b14f0/environment_creation.py similarity index 99% rename from _downloads/01a413564c9bff768b24ed43b946607d/environment_creation.py rename to _downloads/56585a5841cc0f2c5a3dea777f5b14f0/environment_creation.py index 9515eeacf..19e65145a 100644 --- a/_downloads/01a413564c9bff768b24ed43b946607d/environment_creation.py +++ b/_downloads/56585a5841cc0f2c5a3dea777f5b14f0/environment_creation.py @@ -20,7 +20,7 @@ Subclassing gymnasium.Env ------------------------- Before learning how to create your own environment you should check out -`the documentation of Gymnasium’s API `__. +`the documentation of Gymnasium’s API `__. We will be concerned with a subset of gym-examples that looks like this: diff --git a/_downloads/a5659940aa3f8f568547d47752a43172/tutorials_jupyter.zip b/_downloads/a5659940aa3f8f568547d47752a43172/tutorials_jupyter.zip new file mode 100644 index 000000000..6ad4d1d60 Binary files /dev/null and b/_downloads/a5659940aa3f8f568547d47752a43172/tutorials_jupyter.zip differ diff --git a/_downloads/cbfb989de54c286b47aa60ed45417281/implementing_custom_wrappers.ipynb b/_downloads/b4e38e84e3b5c293cbde554ccbba7111/implementing_custom_wrappers.ipynb similarity index 99% rename from _downloads/cbfb989de54c286b47aa60ed45417281/implementing_custom_wrappers.ipynb rename to _downloads/b4e38e84e3b5c293cbde554ccbba7111/implementing_custom_wrappers.ipynb index 48047ba88..051eb14fc 100644 --- a/_downloads/cbfb989de54c286b47aa60ed45417281/implementing_custom_wrappers.ipynb +++ b/_downloads/b4e38e84e3b5c293cbde554ccbba7111/implementing_custom_wrappers.ipynb @@ -107,7 +107,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.15" + "version": "3.9.16" } }, "nbformat": 4, diff --git a/_downloads/1ddb20b882782d2f22f1dd33432af412/handling_time_limits.py b/_downloads/bcabe4d911fd12d268dd101a2f9af983/handling_time_limits.py similarity index 100% rename from _downloads/1ddb20b882782d2f22f1dd33432af412/handling_time_limits.py rename to _downloads/bcabe4d911fd12d268dd101a2f9af983/handling_time_limits.py diff --git a/_downloads/bbd827c8ce7fa89c011383532a3cf898/reinforce_invpend_gym_v26.ipynb b/_downloads/c5364859a73a7f575f592a0ad6aa1b73/reinforce_invpend_gym_v26.ipynb similarity index 99% rename from _downloads/bbd827c8ce7fa89c011383532a3cf898/reinforce_invpend_gym_v26.ipynb rename to _downloads/c5364859a73a7f575f592a0ad6aa1b73/reinforce_invpend_gym_v26.ipynb index a2a2f0b31..68abbeb3e 100644 --- a/_downloads/bbd827c8ce7fa89c011383532a3cf898/reinforce_invpend_gym_v26.ipynb +++ b/_downloads/c5364859a73a7f575f592a0ad6aa1b73/reinforce_invpend_gym_v26.ipynb @@ -125,7 +125,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.15" + "version": "3.9.16" } }, "nbformat": 4, diff --git a/_downloads/b0e7aa582a7b3c32ccdef133cbd779c5/blackjack_tutorial.ipynb b/_downloads/d1980709c80836b9d7e8f9131878afbb/blackjack_tutorial.ipynb similarity index 94% rename from _downloads/b0e7aa582a7b3c32ccdef133cbd779c5/blackjack_tutorial.ipynb rename to _downloads/d1980709c80836b9d7e8f9131878afbb/blackjack_tutorial.ipynb index f63b4f52d..bb9f7795f 100644 --- a/_downloads/b0e7aa582a7b3c32ccdef133cbd779c5/blackjack_tutorial.ipynb +++ b/_downloads/d1980709c80836b9d7e8f9131878afbb/blackjack_tutorial.ipynb @@ -169,7 +169,7 @@ }, "outputs": [], "source": [ - "rolling_length = 500\nfig, axs = plt.subplots(ncols=3, figsize=(12, 5))\naxs[0].set_title(\"Episode rewards\")\nreward_moving_average = (\n np.convolve(\n np.array(env.return_queue).flatten(), np.ones(rolling_length), mode=\"valid\"\n )\n / rolling_length\n)\naxs[0].plot(range(len(reward_moving_average)), reward_moving_average)\naxs[1].set_title(\"Episode lengths\")\nlength_moving_average = (\n np.convolve(\n np.array(env.length_queue).flatten(), np.ones(rolling_length), mode=\"same\"\n )\n / rolling_length\n)\naxs[1].plot(range(len(length_moving_average)), length_moving_average)\naxs[2].set_title(\"Training Error\")\ntraining_error_moving_average = (\n np.convolve(np.array(agent.training_error), np.ones(rolling_length), mode=\"same\")\n / rolling_length\n)\naxs[2].plot(range(len(training_error_moving_average)), training_error_moving_average)\nplt.tight_layout()\nplt.show()" + "rolling_length = 500\nfig, axs = plt.subplots(ncols=3, figsize=(12, 5))\naxs[0].set_title(\"Episode rewards\")\n# compute and assign a rolling average of the data to provide a smoother graph\nreward_moving_average = (\n np.convolve(\n np.array(env.return_queue).flatten(), np.ones(rolling_length), mode=\"valid\"\n )\n / rolling_length\n)\naxs[0].plot(range(len(reward_moving_average)), reward_moving_average)\naxs[1].set_title(\"Episode lengths\")\nlength_moving_average = (\n np.convolve(\n np.array(env.length_queue).flatten(), np.ones(rolling_length), mode=\"same\"\n )\n / rolling_length\n)\naxs[1].plot(range(len(length_moving_average)), length_moving_average)\naxs[2].set_title(\"Training Error\")\ntraining_error_moving_average = (\n np.convolve(np.array(agent.training_error), np.ones(rolling_length), mode=\"same\")\n / rolling_length\n)\naxs[2].plot(range(len(training_error_moving_average)), training_error_moving_average)\nplt.tight_layout()\nplt.show()" ] }, { @@ -264,7 +264,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.15" + "version": "3.9.16" } }, "nbformat": 4, diff --git a/_downloads/7784aa5068fc88abcafc19118c52cbb7/blackjack_tutorial.py b/_downloads/e1249c888e952c938d27855c3210a4bb/blackjack_tutorial.py similarity index 99% rename from _downloads/7784aa5068fc88abcafc19118c52cbb7/blackjack_tutorial.py rename to _downloads/e1249c888e952c938d27855c3210a4bb/blackjack_tutorial.py index e91b951b3..31495887d 100644 --- a/_downloads/7784aa5068fc88abcafc19118c52cbb7/blackjack_tutorial.py +++ b/_downloads/e1249c888e952c938d27855c3210a4bb/blackjack_tutorial.py @@ -299,6 +299,7 @@ for episode in tqdm(range(n_episodes)): rolling_length = 500 fig, axs = plt.subplots(ncols=3, figsize=(12, 5)) axs[0].set_title("Episode rewards") +# compute and assign a rolling average of the data to provide a smoother graph reward_moving_average = ( np.convolve( np.array(env.return_queue).flatten(), np.ones(rolling_length), mode="valid" diff --git a/_downloads/e688a889564af5a98daa8accfbca806e/vector_envs_tutorial.py b/_downloads/e688a889564af5a98daa8accfbca806e/vector_envs_tutorial.py new file mode 100644 index 000000000..c62512d6b --- /dev/null +++ b/_downloads/e688a889564af5a98daa8accfbca806e/vector_envs_tutorial.py @@ -0,0 +1,710 @@ +""" +Training A2C with Vector Envs and Domain Randomization +====================================================== + +""" + + +# %% +# Introduction +# ------------ +# +# In this tutorial, you'll learn how to use vectorized environments to train an Advantage Actor-Critic agent. +# We are going to use A2C, which is the synchronous version of the A3C algorithm [1]. +# +# Vectorized environments [3] can help to achieve quicker and more robust training by allowing multiple instances +# of the same environment to run in parallel (on multiple CPUs). This can significantly reduce the variance and thus speeds up the training. +# +# We will implement an Advantage Actor-Critic from scratch to look at how you can feed batched states into your networks to get a vector of actions +# (one action per environment) and calculate the losses for actor and critic on minibatches of transitions. +# Each minibatch contains the transitions of one sampling phase: `n_steps_per_update` steps are executed in `n_envs` environments in parallel +# (multiply the two to get the number of transitions in a minibatch). After each sampling phase, the losses are calculated and one gradient step is executed. +# To calculate the advantages, we are going to use the Generalized Advantage Estimation (GAE) method [2], which balances the tradeoff +# between variance and bias of the advantage estimates. +# +# The A2C agent class is initialized with the number of features of the input state, the number of actions the agent can take, +# the learning rates and the number of environments that run in parallel to collect experiences. The actor and critic networks are defined +# and their respective optimizers are initialized. The forward pass of the networks takes in a batched vector of states and returns a tensor of state values +# and a tensor of action logits. The select_action method returns a tuple of the chosen actions, the log-probs of those actions, and the state values for each action. +# In addition, it also returns the entropy of the policy distribution, which is subtracted from the loss later (with a weighting factor `ent_coef`) to encourage exploration. +# +# The get_losses function calculates the losses for the actor and critic networks (using GAE), which are then updated using the update_parameters function. +# + + +# %% +# +# ------------------------------ +# + +# Author: Till Zemann +# License: MIT License + +from __future__ import annotations + +import os + +import matplotlib.pyplot as plt +import numpy as np +import torch +import torch.nn as nn +from torch import optim +from tqdm import tqdm + +import gymnasium as gym + + +# %% +# Advantage Actor-Critic (A2C) +# ---------------------------- +# +# The Actor-Critic combines elements of value-based and policy-based methods. In A2C, the agent has two separate neural networks: +# a critic network that estimates the state-value function, and an actor network that outputs logits for a categorical probability distribution over all actions. +# The critic network is trained to minimize the mean squared error between the predicted state values and the actual returns received by the agent +# (this is equivalent to minimizing the squared advantages, because the advantage of an action is as the difference between the return and the state-value: A(s,a) = Q(s,a) - V(s). +# The actor network is trained to maximize the expected return by selecting actions that have high expected values according to the critic network. +# +# The focus of this tutorial will not be on the details of A2C itself. Instead, the tutorial will focus on how to use vectorized environments +# and domain randomization to accelerate the training process for A2C (and other reinforcement learning algorithms). +# + + +# %% +# +# ------------------------------ +# + + +class A2C(nn.Module): + """ + (Synchronous) Advantage Actor-Critic agent class + + Args: + n_features: The number of features of the input state. + n_actions: The number of actions the agent can take. + device: The device to run the computations on (running on a GPU might be quicker for larger Neural Nets, + for this code CPU is totally fine). + critic_lr: The learning rate for the critic network (should usually be larger than the actor_lr). + actor_lr: The learning rate for the actor network. + n_envs: The number of environments that run in parallel (on multiple CPUs) to collect experiences. + """ + + def __init__( + self, + n_features: int, + n_actions: int, + device: torch.device, + critic_lr: float, + actor_lr: float, + n_envs: int, + ) -> None: + """Initializes the actor and critic networks and their respective optimizers.""" + super().__init__() + self.device = device + self.n_envs = n_envs + + critic_layers = [ + nn.Linear(n_features, 32), + nn.ReLU(), + nn.Linear(32, 32), + nn.ReLU(), + nn.Linear(32, 1), # estimate V(s) + ] + + actor_layers = [ + nn.Linear(n_features, 32), + nn.ReLU(), + nn.Linear(32, 32), + nn.ReLU(), + nn.Linear( + 32, n_actions + ), # estimate action logits (will be fed into a softmax later) + ] + + # define actor and critic networks + self.critic = nn.Sequential(*critic_layers).to(self.device) + self.actor = nn.Sequential(*actor_layers).to(self.device) + + # define optimizers for actor and critic + self.critic_optim = optim.RMSprop(self.critic.parameters(), lr=critic_lr) + self.actor_optim = optim.RMSprop(self.actor.parameters(), lr=actor_lr) + + def forward(self, x: np.ndarray) -> tuple[torch.Tensor, torch.Tensor]: + """ + Forward pass of the networks. + + Args: + x: A batched vector of states. + + Returns: + state_values: A tensor with the state values, with shape [n_envs,]. + action_logits_vec: A tensor with the action logits, with shape [n_envs, n_actions]. + """ + x = torch.Tensor(x).to(self.device) + state_values = self.critic(x) # shape: [n_envs,] + action_logits_vec = self.actor(x) # shape: [n_envs, n_actions] + return (state_values, action_logits_vec) + + def select_action( + self, x: np.ndarray + ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]: + """ + Returns a tuple of the chosen actions and the log-probs of those actions. + + Args: + x: A batched vector of states. + + Returns: + actions: A tensor with the actions, with shape [n_steps_per_update, n_envs]. + action_log_probs: A tensor with the log-probs of the actions, with shape [n_steps_per_update, n_envs]. + state_values: A tensor with the state values, with shape [n_steps_per_update, n_envs]. + """ + state_values, action_logits = self.forward(x) + action_pd = torch.distributions.Categorical( + logits=action_logits + ) # implicitly uses softmax + actions = action_pd.sample() + action_log_probs = action_pd.log_prob(actions) + entropy = action_pd.entropy() + return (actions, action_log_probs, state_values, entropy) + + def get_losses( + self, + rewards: torch.Tensor, + action_log_probs: torch.Tensor, + value_preds: torch.Tensor, + entropy: torch.Tensor, + masks: torch.Tensor, + gamma: float, + lam: float, + ent_coef: float, + device: torch.device, + ) -> tuple[torch.Tensor, torch.Tensor]: + """ + Computes the loss of a minibatch (transitions collected in one sampling phase) for actor and critic + using Generalized Advantage Estimation (GAE) to compute the advantages (https://arxiv.org/abs/1506.02438). + + Args: + rewards: A tensor with the rewards for each time step in the episode, with shape [n_steps_per_update, n_envs]. + action_log_probs: A tensor with the log-probs of the actions taken at each time step in the episode, with shape [n_steps_per_update, n_envs]. + value_preds: A tensor with the state value predictions for each time step in the episode, with shape [n_steps_per_update, n_envs]. + masks: A tensor with the masks for each time step in the episode, with shape [n_steps_per_update, n_envs]. + gamma: The discount factor. + lam: The GAE hyperparameter. (lam=1 corresponds to Monte-Carlo sampling with high variance and no bias, + and lam=0 corresponds to normal TD-Learning that has a low variance but is biased + because the estimates are generated by a Neural Net). + device: The device to run the computations on (e.g. CPU or GPU). + + Returns: + critic_loss: The critic loss for the minibatch. + actor_loss: The actor loss for the minibatch. + """ + T = len(rewards) + advantages = torch.zeros(T, self.n_envs, device=device) + + # compute the advantages using GAE + gae = 0.0 + for t in reversed(range(T - 1)): + td_error = ( + rewards[t] + gamma * masks[t] * value_preds[t + 1] - value_preds[t] + ) + gae = td_error + gamma * lam * masks[t] * gae + advantages[t] = gae + + # calculate the loss of the minibatch for actor and critic + critic_loss = advantages.pow(2).mean() + + # give a bonus for higher entropy to encourage exploration + actor_loss = ( + -(advantages.detach() * action_log_probs).mean() - ent_coef * entropy.mean() + ) + return (critic_loss, actor_loss) + + def update_parameters( + self, critic_loss: torch.Tensor, actor_loss: torch.Tensor + ) -> None: + """ + Updates the parameters of the actor and critic networks. + + Args: + critic_loss: The critic loss. + actor_loss: The actor loss. + """ + self.critic_optim.zero_grad() + critic_loss.backward() + self.critic_optim.step() + + self.actor_optim.zero_grad() + actor_loss.backward() + self.actor_optim.step() + + +# %% +# Using Vectorized Environments +# ----------------------------- +# +# When you calculate the losses for the two Neural Networks over only one epoch, it might have a high variance. With vectorized environments, +# we can play with `n_envs` in parallel and thus get up to a linear speedup (meaning that in theory, we collect samples `n_envs` times quicker) +# that we can use to calculate the loss for the current policy and critic network. When we are using more samples to calculate the loss, +# it will have a lower variance and theirfore leads to quicker learning. +# +# A2C is a synchronous method, meaning that the parameter updates to Networks take place deterministically (after each sampling phase), +# but we can still make use of asynchronous vector envs to spawn multiple processes for parallel environment execution. +# +# The simplest way to create vector environments is by calling `gym.vector.make`, which creates multiple instances of the same environment: +# + +envs = gym.vector.make("LunarLander-v2", num_envs=3, max_episode_steps=600) + + +# %% +# Domain Randomization +# -------------------- +# +# If we want to randomize the environment for training to get more robust agents (that can deal with different parameterizations of an environment +# and theirfore might have a higher degree of generalization), we can set the desired parameters manually or use a pseudo-random number generator to generate them. +# +# Manually setting up 3 parallel 'LunarLander-v2' envs with different parameters: + + +envs = gym.vector.AsyncVectorEnv( + [ + lambda: gym.make( + "LunarLander-v2", + gravity=-10.0, + enable_wind=True, + wind_power=15.0, + turbulence_power=1.5, + max_episode_steps=600, + ), + lambda: gym.make( + "LunarLander-v2", + gravity=-9.8, + enable_wind=True, + wind_power=10.0, + turbulence_power=1.3, + max_episode_steps=600, + ), + lambda: gym.make( + "LunarLander-v2", gravity=-7.0, enable_wind=False, max_episode_steps=600 + ), + ] +) + + +# %% +# +# ------------------------------ +# +# Randomly generating the parameters for 3 parallel 'LunarLander-v2' envs, using `np.clip` to stay in the recommended parameter space: +# + + +envs = gym.vector.AsyncVectorEnv( + [ + lambda: gym.make( + "LunarLander-v2", + gravity=np.clip( + np.random.normal(loc=-10.0, scale=1.0), a_min=-11.99, a_max=-0.01 + ), + enable_wind=np.random.choice([True, False]), + wind_power=np.clip( + np.random.normal(loc=15.0, scale=1.0), a_min=0.01, a_max=19.99 + ), + turbulence_power=np.clip( + np.random.normal(loc=1.5, scale=0.5), a_min=0.01, a_max=1.99 + ), + max_episode_steps=600, + ) + for i in range(3) + ] +) + + +# %% +# +# ------------------------------ +# +# Here we are using normal distributions with the standard parameterization of the environment as the mean and an arbitrary standard deviation (scale). +# Depending on the problem, you can experiment with higher variance and use different distributions as well. +# +# If you are training on the same `n_envs` environments for the entire training time, and `n_envs` is a relatively low number +# (in proportion to how complex the environment is), you might still get some overfitting to the specific parameterizations that you picked. +# To mitigate this, you can either pick a high number of randomly parameterized environments or remake your environments every couple of sampling phases +# to generate a new set of pseudo-random parameters. +# + + +# %% +# Setup +# ----- +# + +# environment hyperparams +n_envs = 10 +n_updates = 1000 +n_steps_per_update = 128 +randomize_domain = False + +# agent hyperparams +gamma = 0.999 +lam = 0.95 # hyperparameter for GAE +ent_coef = 0.01 # coefficient for the entropy bonus (to encourage exploration) +actor_lr = 0.001 +critic_lr = 0.005 + +# Note: the actor has a slower learning rate so that the value targets become +# more stationary and are theirfore easier to estimate for the critic + +# environment setup +if randomize_domain: + envs = gym.vector.AsyncVectorEnv( + [ + lambda: gym.make( + "LunarLander-v2", + gravity=np.clip( + np.random.normal(loc=-10.0, scale=1.0), a_min=-11.99, a_max=-0.01 + ), + enable_wind=np.random.choice([True, False]), + wind_power=np.clip( + np.random.normal(loc=15.0, scale=1.0), a_min=0.01, a_max=19.99 + ), + turbulence_power=np.clip( + np.random.normal(loc=1.5, scale=0.5), a_min=0.01, a_max=1.99 + ), + max_episode_steps=600, + ) + for i in range(n_envs) + ] + ) + +else: + envs = gym.vector.make("LunarLander-v2", num_envs=n_envs, max_episode_steps=600) + + +obs_shape = envs.single_observation_space.shape[0] +action_shape = envs.single_action_space.n + +# set the device +use_cuda = False +if use_cuda: + device = torch.device("cuda" if torch.cuda.is_available() else "cpu") +else: + device = torch.device("cpu") + +# init the agent +agent = A2C(obs_shape, action_shape, device, critic_lr, actor_lr, n_envs) + + +# %% +# Training the A2C Agent +# ---------------------- +# +# For our training loop, we are using the `RecordEpisodeStatistics` wrapper to record the episode lengths and returns and we are also saving +# the losses and entropies to plot them after the agent finished training. +# +# You may notice that the don't reset the vectorized envs at the start of each episode like we would usually do. +# This is because each environment resets automatically once the episode finishes (each environment takes a different number of timesteps to finish +# an episode because of the random seeds). As a result, we are also not collecting data in `episodes`, but rather just play a certain number of steps +# (`n_steps_per_update`) in each environment (as an example, this could mean that we play 20 timesteps to finish an episode and then +# use the rest of the timesteps to begin a new one). +# + +# create a wrapper environment to save episode returns and episode lengths +envs_wrapper = gym.wrappers.RecordEpisodeStatistics(envs, deque_size=n_envs * n_updates) + +critic_losses = [] +actor_losses = [] +entropies = [] + +# use tqdm to get a progress bar for training +for sample_phase in tqdm(range(n_updates)): + + # we don't have to reset the envs, they just continue playing + # until the episode is over and then reset automatically + + # reset lists that collect experiences of an episode (sample phase) + ep_value_preds = torch.zeros(n_steps_per_update, n_envs, device=device) + ep_rewards = torch.zeros(n_steps_per_update, n_envs, device=device) + ep_action_log_probs = torch.zeros(n_steps_per_update, n_envs, device=device) + masks = torch.zeros(n_steps_per_update, n_envs, device=device) + + # at the start of training reset all envs to get an initial state + if sample_phase == 0: + states, info = envs_wrapper.reset(seed=42) + + # play n steps in our parallel environments to collect data + for step in range(n_steps_per_update): + + # select an action A_{t} using S_{t} as input for the agent + actions, action_log_probs, state_value_preds, entropy = agent.select_action( + states + ) + + # perform the action A_{t} in the environment to get S_{t+1} and R_{t+1} + states, rewards, terminated, truncated, infos = envs_wrapper.step( + actions.numpy() + ) + + ep_value_preds[step] = torch.squeeze(state_value_preds) + ep_rewards[step] = torch.tensor(rewards, device=device) + ep_action_log_probs[step] = action_log_probs + + # add a mask (for the return calculation later); + # for each env the mask is 1 if the episode is ongoing and 0 if it is terminated (not by truncation!) + masks[step] = torch.tensor([not term for term in terminated]) + + # calculate the losses for actor and critic + critic_loss, actor_loss = agent.get_losses( + ep_rewards, + ep_action_log_probs, + ep_value_preds, + entropy, + masks, + gamma, + lam, + ent_coef, + device, + ) + + # update the actor and critic networks + agent.update_parameters(critic_loss, actor_loss) + + # log the losses and entropy + critic_losses.append(critic_loss.detach().cpu().numpy()) + actor_losses.append(actor_loss.detach().cpu().numpy()) + entropies.append(entropy.detach().mean().cpu().numpy()) + + +# %% +# Plotting +# -------- +# + +""" plot the results """ + +# %matplotlib inline + +rolling_length = 20 +fig, axs = plt.subplots(nrows=2, ncols=2, figsize=(12, 5)) +fig.suptitle( + f"Training plots for {agent.__class__.__name__} in the LunarLander-v2 environment \n \ + (n_envs={n_envs}, n_steps_per_update={n_steps_per_update}, randomize_domain={randomize_domain})" +) + +# episode return +axs[0][0].set_title("Episode Returns") +episode_returns_moving_average = ( + np.convolve( + np.array(envs_wrapper.return_queue).flatten(), + np.ones(rolling_length), + mode="valid", + ) + / rolling_length +) +axs[0][0].plot( + np.arange(len(episode_returns_moving_average)) / n_envs, + episode_returns_moving_average, +) +axs[0][0].set_xlabel("Number of episodes") + +# entropy +axs[1][0].set_title("Entropy") +entropy_moving_average = ( + np.convolve(np.array(entropies), np.ones(rolling_length), mode="valid") + / rolling_length +) +axs[1][0].plot(entropy_moving_average) +axs[1][0].set_xlabel("Number of updates") + + +# critic loss +axs[0][1].set_title("Critic Loss") +critic_losses_moving_average = ( + np.convolve( + np.array(critic_losses).flatten(), np.ones(rolling_length), mode="valid" + ) + / rolling_length +) +axs[0][1].plot(critic_losses_moving_average) +axs[0][1].set_xlabel("Number of updates") + + +# actor loss +axs[1][1].set_title("Actor Loss") +actor_losses_moving_average = ( + np.convolve(np.array(actor_losses).flatten(), np.ones(rolling_length), mode="valid") + / rolling_length +) +axs[1][1].plot(actor_losses_moving_average) +axs[1][1].set_xlabel("Number of updates") + +plt.tight_layout() +plt.show() + +# %% +# .. image:: /_static/img/tutorials/vector_env_a2c_training_plots.png +# :alt: training_plots +# + + +# %% +# Performance Analysis of Synchronous and Asynchronous Vectorized Environments +# ---------------------------------------------------------------------------- +# + +# %% +# +# ------------------------------ +# +# Asynchronous environments can lead to quicker training times and a higher speedup +# for data collection compared to synchronous environments. This is because asynchronous environments +# allow multiple agents to interact with their environments in parallel, +# while synchronous environments run multiple environments serially. +# This results in better efficiency and faster training times for asynchronous environments. +# + +# %% +# .. image:: /_static/img/tutorials/vector_env_performance_plots.png +# :alt: performance_plots +# + +# %% +# +# ------------------------------ +# +# According to the Karp-Flatt metric (a metric used in parallel computing to estimate the limit for the +# speedup when scaling up the number of parallel processes, here the number of environments), +# the estimated max. speedup for asynchronous environments is 57, while the estimated maximum speedup +# for synchronous environments is 21. This suggests that asynchronous environments have significantly +# faster training times compared to synchronous environments (see graphs). +# + +# %% +# .. image:: /_static/img/tutorials/vector_env_karp_flatt_plot.png +# :alt: karp_flatt_metric +# + +# %% +# +# ------------------------------ +# +# However, it is important to note that increasing the number of parallel vector environments +# can lead to slower training times after a certain number of environments (see plot below, where the +# agent was trained until the mean training returns were above -120). The slower training times might occur +# because the gradients of the environments are good enough after a relatively low number of environments +# (especially if the environment is not very complex). In this case, increasing the number of environments +# does not increase the learning speed, and actually increases the runtime, possibly due to the additional time +# needed to calculate the gradients. For LunarLander-v2, the best performing configuration used a AsyncVectorEnv +# with 10 parallel environments, but environments with a higher complexity may require more +# parallel environments to achieve optimal performance. +# + +# %% +# .. image:: /_static/img/tutorials/vector_env_runtime_until_threshold.png +# :alt: runtime_until_threshold_plot +# + + +# %% +# Saving/ Loading Weights +# ----------------------- +# + +save_weights = False +load_weights = False + +actor_weights_path = "weights/actor_weights.h5" +critic_weights_path = "weights/critic_weights.h5" + +if not os.path.exists("weights"): + os.mkdir("weights") + +""" save network weights """ +if save_weights: + torch.save(agent.actor.state_dict(), actor_weights_path) + torch.save(agent.critic.state_dict(), critic_weights_path) + + +""" load network weights """ +if load_weights: + agent = A2C(obs_shape, action_shape, device, critic_lr, actor_lr) + + agent.actor.load_state_dict(torch.load(actor_weights_path)) + agent.critic.load_state_dict(torch.load(critic_weights_path)) + agent.actor.eval() + agent.critic.eval() + + +# %% +# Showcase the Agent +# ------------------ +# + +""" play a couple of showcase episodes """ + +n_showcase_episodes = 3 + +for episode in range(n_showcase_episodes): + print(f"starting episode {episode}...") + + # create a new sample environment to get new random parameters + if randomize_domain: + env = gym.make( + "LunarLander-v2", + render_mode="human", + gravity=np.clip( + np.random.normal(loc=-10.0, scale=2.0), a_min=-11.99, a_max=-0.01 + ), + enable_wind=np.random.choice([True, False]), + wind_power=np.clip( + np.random.normal(loc=15.0, scale=2.0), a_min=0.01, a_max=19.99 + ), + turbulence_power=np.clip( + np.random.normal(loc=1.5, scale=1.0), a_min=0.01, a_max=1.99 + ), + max_episode_steps=500, + ) + else: + env = gym.make("LunarLander-v2", render_mode="human", max_episode_steps=500) + + # get an initial state + state, info = env.reset() + + # play one episode + done = False + while not done: + + # select an action A_{t} using S_{t} as input for the agent + with torch.no_grad(): + action, _, _, _ = agent.select_action(state[None, :]) + + # perform the action A_{t} in the environment to get S_{t+1} and R_{t+1} + state, reward, terminated, truncated, info = env.step(action.item()) + + # update if the environment is done + done = terminated or truncated + +env.close() + + +# %% +# Try playing the environment yourself +# ------------------------------------ +# + +# from gymnasium.utils.play import play +# +# play(gym.make('LunarLander-v2', render_mode='rgb_array'), +# keys_to_action={'w': 2, 'a': 1, 'd': 3}, noop=0) + + +# %% +# References +# ---------- +# +# [1] V. Mnih, A. P. Badia, M. Mirza, A. Graves, T. P. Lillicrap, T. Harley, D. Silver, K. Kavukcuoglu. "Asynchronous Methods for Deep Reinforcement Learning" ICML (2016). +# +# [2] J. Schulman, P. Moritz, S. Levine, M. Jordan and P. Abbeel. "High-dimensional continuous control using generalized advantage estimation." ICLR (2016). +# +# [3] Gymnasium Documentation: Vector environments. (URL: https://gymnasium.farama.org/api/vector/) diff --git a/_downloads/6f2a99877a488456d33ac44780187aa4/implementing_custom_wrappers.py b/_downloads/edd29dab6b748d30e0d5574912999157/implementing_custom_wrappers.py similarity index 100% rename from _downloads/6f2a99877a488456d33ac44780187aa4/implementing_custom_wrappers.py rename to _downloads/edd29dab6b748d30e0d5574912999157/implementing_custom_wrappers.py diff --git a/_downloads/13a63d72db29f8035b56e0c8762b90e0/reinforce_invpend_gym_v26.py b/_downloads/f89a2a42559f7e216498edbfe9cb9bc8/reinforce_invpend_gym_v26.py similarity index 100% rename from _downloads/13a63d72db29f8035b56e0c8762b90e0/reinforce_invpend_gym_v26.py rename to _downloads/f89a2a42559f7e216498edbfe9cb9bc8/reinforce_invpend_gym_v26.py diff --git a/_images/sphx_glr_blackjack_tutorial_thumb.png b/_images/sphx_glr_blackjack_tutorial_thumb.png new file mode 100644 index 000000000..ab51e6970 Binary files /dev/null and b/_images/sphx_glr_blackjack_tutorial_thumb.png differ diff --git a/_images/sphx_glr_environment_creation_thumb.png b/_images/sphx_glr_environment_creation_thumb.png new file mode 100644 index 000000000..ab51e6970 Binary files /dev/null and b/_images/sphx_glr_environment_creation_thumb.png differ diff --git a/_images/sphx_glr_handling_time_limits_thumb.png b/_images/sphx_glr_handling_time_limits_thumb.png new file mode 100644 index 000000000..ab51e6970 Binary files /dev/null and b/_images/sphx_glr_handling_time_limits_thumb.png differ diff --git a/_images/sphx_glr_implementing_custom_wrappers_thumb.png b/_images/sphx_glr_implementing_custom_wrappers_thumb.png new file mode 100644 index 000000000..ab51e6970 Binary files /dev/null and b/_images/sphx_glr_implementing_custom_wrappers_thumb.png differ diff --git a/_images/sphx_glr_reinforce_invpend_gym_v26_thumb.png b/_images/sphx_glr_reinforce_invpend_gym_v26_thumb.png new file mode 100644 index 000000000..ab51e6970 Binary files /dev/null and b/_images/sphx_glr_reinforce_invpend_gym_v26_thumb.png differ diff --git a/_images/sphx_glr_vector_envs_tutorial_thumb.png b/_images/sphx_glr_vector_envs_tutorial_thumb.png new file mode 100644 index 000000000..ab51e6970 Binary files /dev/null and b/_images/sphx_glr_vector_envs_tutorial_thumb.png differ diff --git a/_images/vector_env_a2c_training_plots.png b/_images/vector_env_a2c_training_plots.png new file mode 100644 index 000000000..1e581aea9 Binary files /dev/null and b/_images/vector_env_a2c_training_plots.png differ diff --git a/_images/vector_env_karp_flatt_plot.png b/_images/vector_env_karp_flatt_plot.png new file mode 100644 index 000000000..e411a73fa Binary files /dev/null and b/_images/vector_env_karp_flatt_plot.png differ diff --git a/_images/vector_env_performance_plots.png b/_images/vector_env_performance_plots.png new file mode 100644 index 000000000..836363dfc Binary files /dev/null and b/_images/vector_env_performance_plots.png differ diff --git a/_images/vector_env_runtime_until_threshold.png b/_images/vector_env_runtime_until_threshold.png new file mode 100644 index 000000000..55c1c9628 Binary files /dev/null and b/_images/vector_env_runtime_until_threshold.png differ diff --git a/_static/binder_badge_logo.svg b/_static/binder_badge_logo.svg new file mode 100644 index 000000000..327f6b639 --- /dev/null +++ b/_static/binder_badge_logo.svg @@ -0,0 +1 @@ + launchlaunchbinderbinder \ No newline at end of file diff --git a/_static/broken_example.png b/_static/broken_example.png new file mode 100644 index 000000000..4fea24e7d Binary files /dev/null and b/_static/broken_example.png differ diff --git a/_static/favicon.png b/_static/favicon.png index d94b34384..b1ec92701 100644 Binary files a/_static/favicon.png and b/_static/favicon.png differ diff --git a/_static/img/favicon.png b/_static/img/favicon.png index d94b34384..b1ec92701 100644 Binary files a/_static/img/favicon.png and b/_static/img/favicon.png differ diff --git a/_static/img/tutorials/vector_env_a2c_training_plots.png b/_static/img/tutorials/vector_env_a2c_training_plots.png new file mode 100644 index 000000000..1e581aea9 Binary files /dev/null and b/_static/img/tutorials/vector_env_a2c_training_plots.png differ diff --git a/_static/img/tutorials/vector_env_karp_flatt_plot.png b/_static/img/tutorials/vector_env_karp_flatt_plot.png new file mode 100644 index 000000000..e411a73fa Binary files /dev/null and b/_static/img/tutorials/vector_env_karp_flatt_plot.png differ diff --git a/_static/img/tutorials/vector_env_performance_plots.png b/_static/img/tutorials/vector_env_performance_plots.png new file mode 100644 index 000000000..836363dfc Binary files /dev/null and b/_static/img/tutorials/vector_env_performance_plots.png differ diff --git a/_static/img/tutorials/vector_env_runtime_until_threshold.png b/_static/img/tutorials/vector_env_runtime_until_threshold.png new file mode 100644 index 000000000..55c1c9628 Binary files /dev/null and b/_static/img/tutorials/vector_env_runtime_until_threshold.png differ diff --git a/_static/no_image.png b/_static/no_image.png new file mode 100644 index 000000000..8c2d48d5d Binary files /dev/null and b/_static/no_image.png differ diff --git a/_static/sg_gallery-binder.css b/_static/sg_gallery-binder.css new file mode 100644 index 000000000..a33aa4204 --- /dev/null +++ b/_static/sg_gallery-binder.css @@ -0,0 +1,6 @@ +/* CSS for binder integration */ + +div.binder-badge { + margin: 1em auto; + vertical-align: middle; +} diff --git a/_static/sg_gallery-dataframe.css b/_static/sg_gallery-dataframe.css new file mode 100644 index 000000000..25be73092 --- /dev/null +++ b/_static/sg_gallery-dataframe.css @@ -0,0 +1,46 @@ +/* Pandas dataframe css */ +/* Taken from: https://github.com/spatialaudio/nbsphinx/blob/fb3ba670fc1ba5f54d4c487573dbc1b4ecf7e9ff/src/nbsphinx.py#L587-L619 */ +html[data-theme="light"] { + --sg-text-color: #000; + --sg-tr-odd-color: #f5f5f5; + --sg-tr-hover-color: rgba(66, 165, 245, 0.2); +} +html[data-theme="dark"] { + --sg-text-color: #fff; + --sg-tr-odd-color: #373737; + --sg-tr-hover-color: rgba(30, 81, 122, 0.2); +} + +table.dataframe { + border: none !important; + border-collapse: collapse; + border-spacing: 0; + border-color: transparent; + color: var(--sg-text-color); + font-size: 12px; + table-layout: fixed; +} +table.dataframe thead { + border-bottom: 1px solid var(--sg-text-color); + vertical-align: bottom; +} +table.dataframe tr, +table.dataframe th, +table.dataframe td { + text-align: right; + vertical-align: middle; + padding: 0.5em 0.5em; + line-height: normal; + white-space: normal; + max-width: none; + border: none; +} +table.dataframe th { + font-weight: bold; +} +table.dataframe tbody tr:nth-child(odd) { + background: var(--sg-tr-odd-color); +} +table.dataframe tbody tr:hover { + background: var(--sg-tr-hover-color); +} diff --git a/_static/sg_gallery-rendered-html.css b/_static/sg_gallery-rendered-html.css new file mode 100644 index 000000000..93dc2ffb0 --- /dev/null +++ b/_static/sg_gallery-rendered-html.css @@ -0,0 +1,224 @@ +/* Adapted from notebook/static/style/style.min.css */ +html[data-theme="light"] { + --sg-text-color: #000; + --sg-background-color: #ffffff; + --sg-code-background-color: #eff0f1; + --sg-tr-hover-color: rgba(66, 165, 245, 0.2); + --sg-tr-odd-color: #f5f5f5; +} +html[data-theme="dark"] { + --sg-text-color: #fff; + --sg-background-color: #121212; + --sg-code-background-color: #2f2f30; + --sg-tr-hover-color: rgba(66, 165, 245, 0.2); + --sg-tr-odd-color: #1f1f1f; +} + +.rendered_html { + color: var(--sg-text-color); + /* any extras will just be numbers: */ +} +.rendered_html em { + font-style: italic; +} +.rendered_html strong { + font-weight: bold; +} +.rendered_html u { + text-decoration: underline; +} +.rendered_html :link { + text-decoration: underline; +} +.rendered_html :visited { + text-decoration: underline; +} +.rendered_html h1 { + font-size: 185.7%; + margin: 1.08em 0 0 0; + font-weight: bold; + line-height: 1.0; +} +.rendered_html h2 { + font-size: 157.1%; + margin: 1.27em 0 0 0; + font-weight: bold; + line-height: 1.0; +} +.rendered_html h3 { + font-size: 128.6%; + margin: 1.55em 0 0 0; + font-weight: bold; + line-height: 1.0; +} +.rendered_html h4 { + font-size: 100%; + margin: 2em 0 0 0; + font-weight: bold; + line-height: 1.0; +} +.rendered_html h5 { + font-size: 100%; + margin: 2em 0 0 0; + font-weight: bold; + line-height: 1.0; + font-style: italic; +} +.rendered_html h6 { + font-size: 100%; + margin: 2em 0 0 0; + font-weight: bold; + line-height: 1.0; + font-style: italic; +} +.rendered_html h1:first-child { + margin-top: 0.538em; +} +.rendered_html h2:first-child { + margin-top: 0.636em; +} +.rendered_html h3:first-child { + margin-top: 0.777em; +} +.rendered_html h4:first-child { + margin-top: 1em; +} +.rendered_html h5:first-child { + margin-top: 1em; +} +.rendered_html h6:first-child { + margin-top: 1em; +} +.rendered_html ul:not(.list-inline), +.rendered_html ol:not(.list-inline) { + padding-left: 2em; +} +.rendered_html ul { + list-style: disc; +} +.rendered_html ul ul { + list-style: square; + margin-top: 0; +} +.rendered_html ul ul ul { + list-style: circle; +} +.rendered_html ol { + list-style: decimal; +} +.rendered_html ol ol { + list-style: upper-alpha; + margin-top: 0; +} +.rendered_html ol ol ol { + list-style: lower-alpha; +} +.rendered_html ol ol ol ol { + list-style: lower-roman; +} +.rendered_html ol ol ol ol ol { + list-style: decimal; +} +.rendered_html * + ul { + margin-top: 1em; +} +.rendered_html * + ol { + margin-top: 1em; +} +.rendered_html hr { + color: var(--sg-text-color); + background-color: var(--sg-text-color); +} +.rendered_html pre { + margin: 1em 2em; + padding: 0px; + background-color: var(--sg-background-color); +} +.rendered_html code { + background-color: var(--sg-code-background-color); +} +.rendered_html p code { + padding: 1px 5px; +} +.rendered_html pre code { + background-color: var(--sg-background-color); +} +.rendered_html pre, +.rendered_html code { + border: 0; + color: var(--sg-text-color); + font-size: 100%; +} +.rendered_html blockquote { + margin: 1em 2em; +} +.rendered_html table { + margin-left: auto; + margin-right: auto; + border: none; + border-collapse: collapse; + border-spacing: 0; + color: var(--sg-text-color); + font-size: 12px; + table-layout: fixed; +} +.rendered_html thead { + border-bottom: 1px solid var(--sg-text-color); + vertical-align: bottom; +} +.rendered_html tr, +.rendered_html th, +.rendered_html td { + text-align: right; + vertical-align: middle; + padding: 0.5em 0.5em; + line-height: normal; + white-space: normal; + max-width: none; + border: none; +} +.rendered_html th { + font-weight: bold; +} +.rendered_html tbody tr:nth-child(odd) { + background: var(--sg-tr-odd-color); +} +.rendered_html tbody tr:hover { + color: var(--sg-text-color); + background: var(--sg-tr-hover-color); +} +.rendered_html * + table { + margin-top: 1em; +} +.rendered_html p { + text-align: left; +} +.rendered_html * + p { + margin-top: 1em; +} +.rendered_html img { + display: block; + margin-left: auto; + margin-right: auto; +} +.rendered_html * + img { + margin-top: 1em; +} +.rendered_html img, +.rendered_html svg { + max-width: 100%; + height: auto; +} +.rendered_html img.unconfined, +.rendered_html svg.unconfined { + max-width: none; +} +.rendered_html .alert { + margin-bottom: initial; +} +.rendered_html * + .alert { + margin-top: 1em; +} +[dir="rtl"] .rendered_html p { + text-align: right; +} diff --git a/_static/sg_gallery.css b/_static/sg_gallery.css new file mode 100644 index 000000000..72227837d --- /dev/null +++ b/_static/sg_gallery.css @@ -0,0 +1,342 @@ +/* +Sphinx-Gallery has compatible CSS to fix default sphinx themes +Tested for Sphinx 1.3.1 for all themes: default, alabaster, sphinxdoc, +scrolls, agogo, traditional, nature, haiku, pyramid +Tested for Read the Docs theme 0.1.7 */ + +/* Define light colors */ +:root, html[data-theme="light"], body[data-theme="light"]{ + --sg-tooltip-foreground: black; + --sg-tooltip-background: rgba(250, 250, 250, 0.9); + --sg-tooltip-border: #ccc transparent; + --sg-thumb-box-shadow-color: #6c757d40; + --sg-thumb-hover-border: #0069d9; + --sg-script-out: #888; + --sg-script-pre: #fafae2; + --sg-pytb-foreground: #000; + --sg-pytb-background: #ffe4e4; + --sg-pytb-border-color: #f66; + --sg-download-a-background-color: #ffc; + --sg-download-a-background-image: linear-gradient(to bottom, #ffc, #d5d57e); + --sg-download-a-border-color: 1px solid #c2c22d; + --sg-download-a-color: #000; + --sg-download-a-hover-background-color: #d5d57e; + --sg-download-a-hover-box-shadow-1: rgba(255, 255, 255, 0.1); + --sg-download-a-hover-box-shadow-2: rgba(0, 0, 0, 0.25); +} +@media(prefers-color-scheme: light) { + :root[data-theme="auto"], html[data-theme="auto"], body[data-theme="auto"] { + --sg-tooltip-foreground: black; + --sg-tooltip-background: rgba(250, 250, 250, 0.9); + --sg-tooltip-border: #ccc transparent; + --sg-thumb-box-shadow-color: #6c757d40; + --sg-thumb-hover-border: #0069d9; + --sg-script-out: #888; + --sg-script-pre: #fafae2; + --sg-pytb-foreground: #000; + --sg-pytb-background: #ffe4e4; + --sg-pytb-border-color: #f66; + --sg-download-a-background-color: #ffc; + --sg-download-a-background-image: linear-gradient(to bottom, #ffc, #d5d57e); + --sg-download-a-border-color: 1px solid #c2c22d; + --sg-download-a-color: #000; + --sg-download-a-hover-background-color: #d5d57e; + --sg-download-a-hover-box-shadow-1: rgba(255, 255, 255, 0.1); + --sg-download-a-hover-box-shadow-2: rgba(0, 0, 0, 0.25); + } +} + +html[data-theme="dark"], body[data-theme="dark"] { + --sg-tooltip-foreground: white; + --sg-tooltip-background: rgba(10, 10, 10, 0.9); + --sg-tooltip-border: #333 transparent; + --sg-thumb-box-shadow-color: #79848d40; + --sg-thumb-hover-border: #003975; + --sg-script-out: rgb(179, 179, 179); + --sg-script-pre: #2e2e22; + --sg-pytb-foreground: #fff; + --sg-pytb-background: #1b1717; + --sg-pytb-border-color: #622; + --sg-download-a-background-color: #443; + --sg-download-a-background-image: linear-gradient(to bottom, #443, #221); + --sg-download-a-border-color: 1px solid #3a3a0d; + --sg-download-a-color: #fff; + --sg-download-a-hover-background-color: #616135; + --sg-download-a-hover-box-shadow-1: rgba(0, 0, 0, 0.1); + --sg-download-a-hover-box-shadow-2: rgba(255, 255, 255, 0.25); +} +@media(prefers-color-scheme: dark){ + html[data-theme="auto"], body[data-theme="auto"] { + --sg-tooltip-foreground: white; + --sg-tooltip-background: rgba(10, 10, 10, 0.9); + --sg-tooltip-border: #333 transparent; + --sg-thumb-box-shadow-color: #79848d40; + --sg-thumb-hover-border: #003975; + --sg-script-out: rgb(179, 179, 179); + --sg-script-pre: #2e2e22; + --sg-pytb-foreground: #fff; + --sg-pytb-background: #1b1717; + --sg-pytb-border-color: #622; + --sg-download-a-background-color: #443; + --sg-download-a-background-image: linear-gradient(to bottom, #443, #221); + --sg-download-a-border-color: 1px solid #3a3a0d; + --sg-download-a-color: #fff; + --sg-download-a-hover-background-color: #616135; + --sg-download-a-hover-box-shadow-1: rgba(0, 0, 0, 0.1); + --sg-download-a-hover-box-shadow-2: rgba(255, 255, 255, 0.25); + } +} + +.sphx-glr-thumbnails { + width: 100%; + margin: 0px 0px 20px 0px; + + /* align thumbnails on a grid */ + justify-content: space-between; + display: grid; + /* each grid column should be at least 160px (this will determine + the actual number of columns) and then take as much of the + remaining width as possible */ + grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); + gap: 15px; +} +.sphx-glr-thumbnails .toctree-wrapper { + /* hide empty toctree divs added to the DOM + by sphinx even though the toctree is hidden + (they would fill grid places with empty divs) */ + display: none; +} +.sphx-glr-thumbcontainer { + background: transparent; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius: 5px; + box-shadow: 0 0 10px var(--sg-thumb-box-shadow-color); + + /* useful to absolutely position link in div */ + position: relative; + + /* thumbnail width should include padding and borders + and take all available space */ + box-sizing: border-box; + width: 100%; + padding: 10px; + border: 1px solid transparent; + + /* align content in thumbnail */ + display: flex; + flex-direction: column; + align-items: center; + gap: 7px; +} +.sphx-glr-thumbcontainer p { + position: absolute; + top: 0; + left: 0; +} +.sphx-glr-thumbcontainer p, +.sphx-glr-thumbcontainer p a { + /* link should cover the whole thumbnail div */ + width: 100%; + height: 100%; +} +.sphx-glr-thumbcontainer p a span { + /* text within link should be masked + (we are just interested in the href) */ + display: none; +} +.sphx-glr-thumbcontainer:hover { + border: 1px solid; + border-color: var(--sg-thumb-hover-border); + cursor: pointer; +} +.sphx-glr-thumbcontainer a.internal { + bottom: 0; + display: block; + left: 0; + box-sizing: border-box; + padding: 150px 10px 0; + position: absolute; + right: 0; + top: 0; +} +/* Next one is to avoid Sphinx traditional theme to cover all the +thumbnail with its default link Background color */ +.sphx-glr-thumbcontainer a.internal:hover { + background-color: transparent; +} + +.sphx-glr-thumbcontainer p { + margin: 0 0 0.1em 0; +} +.sphx-glr-thumbcontainer .figure { + margin: 10px; + width: 160px; +} +.sphx-glr-thumbcontainer img { + display: inline; + max-height: 112px; + max-width: 160px; +} +.sphx-glr-thumbcontainer[tooltip]:hover:after { + background: var(--sg-tooltip-background); + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + color: var(--sg-tooltip-foreground); + content: attr(tooltip); + padding: 10px; + z-index: 98; + width: 100%; + height: 100%; + position: absolute; + pointer-events: none; + top: 0; + box-sizing: border-box; + overflow: hidden; + backdrop-filter: blur(3px); +} + +.sphx-glr-script-out { + color: var(--sg-script-out); + display: flex; + gap: 0.5em; +} +.sphx-glr-script-out::before { + content: "Out:"; + /* These numbers come from the pre style in the pydata sphinx theme. This + * turns out to match perfectly on the rtd theme, but be a bit too low for + * the pydata sphinx theme. As I could not find a dimension to use that was + * scaled the same way, I just picked one option that worked pretty close for + * both. */ + line-height: 1.4; + padding-top: 10px; +} +.sphx-glr-script-out .highlight { + background-color: transparent; + /* These options make the div expand... */ + flex-grow: 1; + /* ... but also keep it from overflowing its flex container. */ + overflow: auto; +} +.sphx-glr-script-out .highlight pre { + background-color: var(--sg-script-pre); + border: 0; + max-height: 30em; + overflow: auto; + padding-left: 1ex; + /* This margin is necessary in the pydata sphinx theme because pre has a box + * shadow which would be clipped by the overflow:auto in the parent div + * above. */ + margin: 2px; + word-break: break-word; +} +.sphx-glr-script-out + p { + margin-top: 1.8em; +} +blockquote.sphx-glr-script-out { + margin-left: 0pt; +} +.sphx-glr-script-out.highlight-pytb .highlight pre { + color: var(--sg-pytb-foreground); + background-color: var(--sg-pytb-background); + border: 1px solid var(--sg-pytb-border-color); + margin-top: 10px; + padding: 7px; +} + +div.sphx-glr-footer { + text-align: center; +} + +div.sphx-glr-download { + margin: 1em auto; + vertical-align: middle; +} + +div.sphx-glr-download a { + background-color: var(--sg-download-a-background-color); + background-image: var(--sg-download-a-background-image); + border-radius: 4px; + border: 1px solid var(--sg-download-a-border-color); + color: var(--sg-download-a-color); + display: inline-block; + font-weight: bold; + padding: 1ex; + text-align: center; +} + +div.sphx-glr-download code.download { + display: inline-block; + white-space: normal; + word-break: normal; + overflow-wrap: break-word; + /* border and background are given by the enclosing 'a' */ + border: none; + background: none; +} + +div.sphx-glr-download a:hover { + box-shadow: inset 0 1px 0 var(--sg-download-a-hover-box-shadow-1), 0 1px 5px var(--sg-download-a-hover-box-shadow-2); + text-decoration: none; + background-image: none; + background-color: var(--sg-download-a-hover-background-color); +} + +.sphx-glr-example-title:target::before { + display: block; + content: ""; + margin-top: -50px; + height: 50px; + visibility: hidden; +} + +ul.sphx-glr-horizontal { + list-style: none; + padding: 0; +} +ul.sphx-glr-horizontal li { + display: inline; +} +ul.sphx-glr-horizontal img { + height: auto !important; +} + +.sphx-glr-single-img { + margin: auto; + display: block; + max-width: 100%; +} + +.sphx-glr-multi-img { + max-width: 42%; + height: auto; +} + +div.sphx-glr-animation { + margin: auto; + display: block; + max-width: 100%; +} +div.sphx-glr-animation .animation { + display: block; +} + +p.sphx-glr-signature a.reference.external { + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius: 5px; + padding: 3px; + font-size: 75%; + text-align: right; + margin-left: auto; + display: table; +} + +.sphx-glr-clear { + clear: both; +} + +a.sphx-glr-backref-instance { + text-decoration: none; +} diff --git a/_static/styles/furo-extensions.css b/_static/styles/furo-extensions.css index c17edf89e..c2f92cc68 100644 --- a/_static/styles/furo-extensions.css +++ b/_static/styles/furo-extensions.css @@ -1,2 +1,2 @@ -#furo-sidebar-ad-placement{padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)}#furo-sidebar-ad-placement .ethical-sidebar{background:var(--color-background-secondary);border:none;box-shadow:none}#furo-sidebar-ad-placement .ethical-sidebar:hover{background:var(--color-background-hover)}#furo-sidebar-ad-placement .ethical-sidebar a{color:var(--color-foreground-primary)}#furo-sidebar-ad-placement .ethical-callout a{color:var(--color-foreground-secondary)!important}#furo-readthedocs-versions{background:transparent;display:block;position:static;width:100%}#furo-readthedocs-versions .rst-versions{background:#1a1c1e}#furo-readthedocs-versions .rst-current-version{background:var(--color-sidebar-item-background);cursor:unset}#furo-readthedocs-versions .rst-current-version:hover{background:var(--color-sidebar-item-background)}#furo-readthedocs-versions .rst-current-version .fa-book{color:var(--color-foreground-primary)}#furo-readthedocs-versions>.rst-other-versions{padding:0}#furo-readthedocs-versions>.rst-other-versions small{opacity:1}#furo-readthedocs-versions .injected .rst-versions{position:unset}#furo-readthedocs-versions:focus-within,#furo-readthedocs-versions:hover{box-shadow:0 0 0 1px var(--color-sidebar-background-border)}#furo-readthedocs-versions:focus-within .rst-current-version,#furo-readthedocs-versions:hover .rst-current-version{background:#1a1c1e;font-size:inherit;height:auto;line-height:inherit;padding:12px;text-align:right}#furo-readthedocs-versions:focus-within .rst-current-version .fa-book,#furo-readthedocs-versions:hover .rst-current-version .fa-book{color:#fff;float:left}#furo-readthedocs-versions:focus-within .fa-caret-down,#furo-readthedocs-versions:hover .fa-caret-down{display:none}#furo-readthedocs-versions:focus-within .injected,#furo-readthedocs-versions:focus-within .rst-current-version,#furo-readthedocs-versions:focus-within .rst-other-versions,#furo-readthedocs-versions:hover .injected,#furo-readthedocs-versions:hover .rst-current-version,#furo-readthedocs-versions:hover .rst-other-versions{display:block}#furo-readthedocs-versions:focus-within>.rst-current-version,#furo-readthedocs-versions:hover>.rst-current-version{display:none}.highlight:hover button.copybtn{color:var(--color-code-foreground)}.highlight button.copybtn{align-items:center;background-color:var(--color-code-background);border:none;color:var(--color-background-item);cursor:pointer;height:1.25em;opacity:1;right:.5rem;top:.625rem;transition:color .3s,opacity .3s;width:1.25em}.highlight button.copybtn:hover{background-color:var(--color-code-background);color:var(--color-brand-content)}.highlight button.copybtn:after{background-color:transparent;color:var(--color-code-foreground);display:none}.highlight button.copybtn.success{color:#22863a;transition:color 0ms}.highlight button.copybtn.success:after{display:block}.highlight button.copybtn svg{padding:0}body{--sd-color-primary:var(--color-brand-primary);--sd-color-primary-highlight:var(--color-brand-content);--sd-color-primary-text:var(--color-background-primary);--sd-color-shadow:rgba(0,0,0,.05);--sd-color-card-border:var(--color-card-border);--sd-color-card-border-hover:var(--color-brand-content);--sd-color-card-background:var(--color-card-background);--sd-color-card-text:var(--color-foreground-primary);--sd-color-card-header:var(--color-card-marginals-background);--sd-color-card-footer:var(--color-card-marginals-background);--sd-color-tabs-label-active:var(--color-brand-content);--sd-color-tabs-label-hover:var(--color-foreground-muted);--sd-color-tabs-label-inactive:var(--color-foreground-muted);--sd-color-tabs-underline-active:var(--color-brand-content);--sd-color-tabs-underline-hover:var(--color-foreground-border);--sd-color-tabs-underline-inactive:var(--color-background-border);--sd-color-tabs-overline:var(--color-background-border);--sd-color-tabs-underline:var(--color-background-border)}.sd-tab-content{box-shadow:0 -2px var(--sd-color-tabs-overline),0 1px var(--sd-color-tabs-underline)}.sd-card{box-shadow:0 .1rem .25rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)}.sd-shadow-sm{box-shadow:0 .1rem .25rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)!important}.sd-shadow-md{box-shadow:0 .3rem .75rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)!important}.sd-shadow-lg{box-shadow:0 .6rem 1.5rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)!important}.sd-card-hover:hover{transform:none}.sd-cards-carousel{gap:.25rem;padding:.25rem}body{--tabs--label-text:var(--color-foreground-muted);--tabs--label-text--hover:var(--color-foreground-muted);--tabs--label-text--active:var(--color-brand-content);--tabs--label-text--active--hover:var(--color-brand-content);--tabs--label-background:transparent;--tabs--label-background--hover:transparent;--tabs--label-background--active:transparent;--tabs--label-background--active--hover:transparent;--tabs--padding-x:0.25em;--tabs--margin-x:1em;--tabs--border:var(--color-background-border);--tabs--label-border:transparent;--tabs--label-border--hover:var(--color-foreground-muted);--tabs--label-border--active:var(--color-brand-content);--tabs--label-border--active--hover:var(--color-brand-content)}[role=main] .container{max-width:none;padding-left:0;padding-right:0}.shadow.docutils{border:none;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 .0625rem rgba(0,0,0,.1)!important}.sphinx-bs .card{background-color:var(--color-background-secondary);color:var(--color-foreground)}h1{font-size:2.2rem}h2{font-size:1.7rem}h3{font-size:1.4rem}html:has(.farama-header-menu.active){visibility:hidden}.farama-hidden[aria-hidden=true]{visibility:hidden}.farama-hidden[aria-hidden=false]{visibility:visible}.cookie-alert{background-color:var(--color-background-secondary);border-top:1px solid var(--color-background-border);bottom:0;color:var(--color-foreground-primary);display:flex;left:0;min-height:70px;position:fixed;width:100%;z-index:99999}.cookie-alert__container{align-items:center;display:flex;margin:auto;max-width:calc(100% - 28px);width:700px}.cookie-alert p{flex:1}.cookie-alert button{background-color:transparent;border:none;cursor:pointer;height:26px;padding:0;width:26px}.cookie-alert button svg .cls-1{stroke:var(--color-foreground-primary)}.cookie-alert button svg{width:100%}article[role=main]:has(.farama-env-icon-container) .farama-env-icon-container{display:flex;margin-top:7px;position:absolute}article[role=main]:has(.farama-env-icon-container) .section h1:first-child,article[role=main]:has(.farama-env-icon-container) .section h2:first-child,article[role=main]:has(.farama-env-icon-container) section h1:first-child,article[role=main]:has(.farama-env-icon-container) section h2:first-child{margin-left:34px}.farama-env-icon{height:32px}.env-grid{box-sizing:border-box;display:flex;flex-wrap:wrap;justify-content:center;width:100%}.env-grid__cell{display:flex;flex-direction:column;height:180px;padding:10px;width:180px}.cell__image-container{display:flex;height:148px;justify-content:center}.cell__image-container img{max-height:100%;-o-object-fit:contain;object-fit:contain}.cell__title{align-items:flex-end;display:flex;height:32px;justify-content:center;line-height:16px;text-align:center}.more-btn{display:block;margin:12px auto;width:240px}html:has(.farama-header-menu.active){overflow:hidden}body{--farama-header-height:52px;--farama-header-logo-margin:10px}.farama-header{background-color:var(--color-background-secondary);border-bottom:1px solid var(--color-header-border);box-sizing:border-box;display:flex;height:var(--farama-header-height);padding:0 36px 0 24px;position:absolute;width:100%;z-index:95}.farama-header .farama-header__container{display:flex;margin:0 auto;max-width:1400px;width:100%}.farama-header a{color:var(--color-foreground-primary);text-decoration:none;transition:color .125s ease}.farama-header a:hover{color:var(--color-foreground-secondary)}.farama-header .farama-header__logo{margin:var(--farama-header-logo-margin);max-height:calc(var(--farama-header-height) - var(--farama-header-logo-margin))}.farama-header .farama-header__title{align-self:center;font-size:var(--font-size--normal);font-weight:400;margin:0 0 2px;padding:0 0 0 4px}.farama-header .farama-header__left,.farama-header .farama-header__left a{display:flex}.farama-header .farama-header__left--mobile{display:none}.farama-header .farama-header__left--mobile .nav-overlay-icon svg{stroke:var(--color-foreground-primary);fill:var(--color-foreground-primary);stroke-width:2px;padding:0 6px;width:20px}.farama-header .farama-header__right{align-items:center;display:flex;flex:1;justify-content:flex-end;z-index:2}.farama-header .farama-header__right .farama-header__nav{display:flex;height:100%;list-style:none}.farama-header .farama-header__right .farama-header__nav li{align-items:center;cursor:pointer;display:flex;margin-left:20px;text-decoration:none}.farama-header .farama-header__right .farama-header__nav li a{align-items:center;display:flex;height:100%}.farama-header .farama-header__right .farama-header__nav li .farama-header__dropdown-container{align-items:center;display:flex;height:100%;position:relative}.farama-header .farama-header__right .farama-header__nav li .farama-header__dropdown-container:hover .farama-header__dropdown-menu{display:block}.farama-header .farama-header__right .farama-header__nav li .farama-header__dropdown-container svg{fill:var(--color-foreground-primary);width:32px}.farama-header .farama-header__right .farama-header__nav li .farama-header__dropdown-container .farama-header__dropdown-menu{background:var(--color-background-hover);border:1px solid var(--color-background-border);display:none;position:absolute;right:0;top:var(--farama-header-height);z-index:9999}.farama-header .farama-header__right .farama-header__nav li .farama-header__dropdown-container .farama-header__dropdown-menu ul{display:inherit;margin:0;padding:6px 14px}.farama-header .farama-header__right .farama-header__nav li .farama-header__dropdown-container .farama-header__dropdown-menu li{margin:0;padding:6px 0}.farama-header .farama-header__right .farama-header-menu{display:flex;justify-content:center;position:relative}.farama-header .farama-header__right .farama-header-menu .farama-header-menu__btn{background:none;border:none;cursor:pointer;display:flex}.farama-header .farama-header__right .farama-header-menu .farama-header-menu__btn img{width:26px}.farama-header .farama-header__right .farama-header-menu .farama-header-menu__btn svg{stroke:var(--color-foreground-primary);stroke-width:2px;align-self:center;width:14px}.farama-header .farama-header__right .farama-header-menu.active .farama-header-menu-container{transform:translateY(100vh)}.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container{background-color:var(--color-background-secondary);border-left:1px solid var(--color-background-border);box-sizing:border-box;height:100%;overflow:auto;position:fixed;right:0;top:-100vh;transform:translateY(0);transition:transform .2s ease-in;width:100%;z-index:99}.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__header{align-items:center;border-bottom:1px solid var(--color-background-border);box-sizing:border-box;display:flex;margin:0 auto;max-width:1400px;padding:7px 52px;position:relative;width:100%}.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__header a{align-items:center;display:flex}.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__header a .farama-header-menu__logo{width:36px}.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__header a span{color:var(--color-sidebar-brand-text);padding-left:8px}.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__header .farama-header-menu-header__right{padding-right:inherit;position:absolute;right:0}.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__header .farama-header-menu-header__right button{background:none;border:none;cursor:pointer;display:flex}.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__header .farama-header-menu-header__right button svg{color:var(--color-foreground-primary);width:20px}.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__body{box-sizing:border-box;display:flex;flex-wrap:wrap;margin:0 auto;max-width:1400px;padding:22px 52px;width:100%}.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__body .farama-header-menu__section{margin-bottom:24px;min-width:250px}.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__body .farama-header-menu__section .farama-header-menu__section-title{display:block;font-size:var(--font-size--normal);font-weight:700;margin-bottom:12px;text-transform:uppercase}.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__body .farama-header-menu__section .farama-header-menu-list{display:inherit;list-style:none;margin:0;padding:0}.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__body .farama-header-menu__section .farama-header-menu-list li:hover{background-color:var(--color-background-hover)}.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__body .farama-header-menu__section .farama-header-menu-list li a{align-items:center;display:flex;padding:12px 18px}.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__body .farama-header-menu__section .farama-header-menu-list li a:hover{color:inherit}.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__body .farama-header-menu__section .farama-header-menu-list li a img{filter:invert(100%);margin-right:10px;width:24px}.farama-sidebar__title{align-items:center;display:flex;margin-top:var(--sidebar-search-space-above);min-height:calc(52px - var(--sidebar-search-space-above));padding-right:4px}.farama-sidebar__title img{height:calc(var(--farama-header-height) - 20px);margin:var(--farama-header-logo-margin)}.farama-sidebar__title span{color:var(--color-foreground-primary)}.farama-sidebar__title span:hover{text-decoration:none}.sidebar-brand{align-items:center;flex-direction:row;padding:var(--sidebar-item-spacing-vertical)}.sidebar-brand .sidebar-logo-container{display:flex;height:auto;max-width:55px}.sidebar-brand .sidebar-brand-text{font-size:1.3rem;padding-left:11px}.mobile-header .header-center{opacity:0;transition:opacity easy-in .2s}.mobile-header.scrolled .header-center{opacity:1}.sphx-glr-script-out{color:var(--color-foreground-secondary);display:flex;gap:.5em}.sphx-glr-script-out:before{content:"Out:";line-height:1.4;padding-top:10px}.sphx-glr-script-out .highlight{overflow-x:auto}@media(max-width:950px){.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__header{padding:7px 42px}.farama-header .farama-header-menu__btn-name{display:none}}@media(max-width:600px){.farama-header{padding:0 4px}.farama-header .farama-header__title{font-size:var(--font-size--small)}.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__header{padding:8px 12px}.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__body{padding:18px 12px}}@media(max-width:480px){.farama-header .farama-header__title{width:110px}.farama-header .farama-header-menu__btn-name{text-align:right;width:100px}}body[data-theme=dark] .farama-black-logo-invert,body[data-theme=light] .farama-white-logo-invert{filter:invert(1)}body[data-theme=dark] img[src*="//render.githubusercontent.com/render/math"]{filter:invert(90%)} +#furo-sidebar-ad-placement{padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)}#furo-sidebar-ad-placement .ethical-sidebar{background:var(--color-background-secondary);border:none;box-shadow:none}#furo-sidebar-ad-placement .ethical-sidebar:hover{background:var(--color-background-hover)}#furo-sidebar-ad-placement .ethical-sidebar a{color:var(--color-foreground-primary)}#furo-sidebar-ad-placement .ethical-callout a{color:var(--color-foreground-secondary)!important}#furo-readthedocs-versions{background:transparent;display:block;position:static;width:100%}#furo-readthedocs-versions .rst-versions{background:#1a1c1e}#furo-readthedocs-versions .rst-current-version{background:var(--color-sidebar-item-background);cursor:unset}#furo-readthedocs-versions .rst-current-version:hover{background:var(--color-sidebar-item-background)}#furo-readthedocs-versions .rst-current-version .fa-book{color:var(--color-foreground-primary)}#furo-readthedocs-versions>.rst-other-versions{padding:0}#furo-readthedocs-versions>.rst-other-versions small{opacity:1}#furo-readthedocs-versions .injected .rst-versions{position:unset}#furo-readthedocs-versions:focus-within,#furo-readthedocs-versions:hover{box-shadow:0 0 0 1px var(--color-sidebar-background-border)}#furo-readthedocs-versions:focus-within .rst-current-version,#furo-readthedocs-versions:hover .rst-current-version{background:#1a1c1e;font-size:inherit;height:auto;line-height:inherit;padding:12px;text-align:right}#furo-readthedocs-versions:focus-within .rst-current-version .fa-book,#furo-readthedocs-versions:hover .rst-current-version .fa-book{color:#fff;float:left}#furo-readthedocs-versions:focus-within .fa-caret-down,#furo-readthedocs-versions:hover .fa-caret-down{display:none}#furo-readthedocs-versions:focus-within .injected,#furo-readthedocs-versions:focus-within .rst-current-version,#furo-readthedocs-versions:focus-within .rst-other-versions,#furo-readthedocs-versions:hover .injected,#furo-readthedocs-versions:hover .rst-current-version,#furo-readthedocs-versions:hover .rst-other-versions{display:block}#furo-readthedocs-versions:focus-within>.rst-current-version,#furo-readthedocs-versions:hover>.rst-current-version{display:none}.highlight:hover button.copybtn{color:var(--color-code-foreground)}.highlight button.copybtn{align-items:center;background-color:var(--color-code-background);border:none;color:var(--color-background-item);cursor:pointer;height:1.25em;opacity:1;right:.5rem;top:.625rem;transition:color .3s,opacity .3s;width:1.25em}.highlight button.copybtn:hover{background-color:var(--color-code-background);color:var(--color-brand-content)}.highlight button.copybtn:after{background-color:transparent;color:var(--color-code-foreground);display:none}.highlight button.copybtn.success{color:#22863a;transition:color 0ms}.highlight button.copybtn.success:after{display:block}.highlight button.copybtn svg{padding:0}body{--sd-color-primary:var(--color-brand-primary);--sd-color-primary-highlight:var(--color-brand-content);--sd-color-primary-text:var(--color-background-primary);--sd-color-shadow:rgba(0,0,0,.05);--sd-color-card-border:var(--color-card-border);--sd-color-card-border-hover:var(--color-brand-content);--sd-color-card-background:var(--color-card-background);--sd-color-card-text:var(--color-foreground-primary);--sd-color-card-header:var(--color-card-marginals-background);--sd-color-card-footer:var(--color-card-marginals-background);--sd-color-tabs-label-active:var(--color-brand-content);--sd-color-tabs-label-hover:var(--color-foreground-muted);--sd-color-tabs-label-inactive:var(--color-foreground-muted);--sd-color-tabs-underline-active:var(--color-brand-content);--sd-color-tabs-underline-hover:var(--color-foreground-border);--sd-color-tabs-underline-inactive:var(--color-background-border);--sd-color-tabs-overline:var(--color-background-border);--sd-color-tabs-underline:var(--color-background-border)}.sd-tab-content{box-shadow:0 -2px var(--sd-color-tabs-overline),0 1px var(--sd-color-tabs-underline)}.sd-card{box-shadow:0 .1rem .25rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)}.sd-shadow-sm{box-shadow:0 .1rem .25rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)!important}.sd-shadow-md{box-shadow:0 .3rem .75rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)!important}.sd-shadow-lg{box-shadow:0 .6rem 1.5rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)!important}.sd-card-hover:hover{transform:none}.sd-cards-carousel{gap:.25rem;padding:.25rem}body{--tabs--label-text:var(--color-foreground-muted);--tabs--label-text--hover:var(--color-foreground-muted);--tabs--label-text--active:var(--color-brand-content);--tabs--label-text--active--hover:var(--color-brand-content);--tabs--label-background:transparent;--tabs--label-background--hover:transparent;--tabs--label-background--active:transparent;--tabs--label-background--active--hover:transparent;--tabs--padding-x:0.25em;--tabs--margin-x:1em;--tabs--border:var(--color-background-border);--tabs--label-border:transparent;--tabs--label-border--hover:var(--color-foreground-muted);--tabs--label-border--active:var(--color-brand-content);--tabs--label-border--active--hover:var(--color-brand-content)}[role=main] .container{max-width:none;padding-left:0;padding-right:0}.shadow.docutils{border:none;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 .0625rem rgba(0,0,0,.1)!important}.sphinx-bs .card{background-color:var(--color-background-secondary);color:var(--color-foreground)}h1{font-size:2.2rem}h2{font-size:1.7rem}h3{font-size:1.4rem}html:has(.farama-header-menu.active){visibility:hidden}.farama-hidden[aria-hidden=true]{visibility:hidden}.farama-hidden[aria-hidden=false]{visibility:visible}.cookie-alert{background-color:var(--color-background-secondary);border-top:1px solid var(--color-background-border);bottom:0;color:var(--color-foreground-primary);display:flex;left:0;min-height:70px;position:fixed;width:100%;z-index:99999}.cookie-alert__container{align-items:center;display:flex;margin:auto;max-width:calc(100% - 28px);width:700px}.cookie-alert p{flex:1}.cookie-alert button{background-color:transparent;border:none;cursor:pointer;height:26px;padding:0;width:26px}.cookie-alert button svg .cls-1{stroke:var(--color-foreground-primary)}.cookie-alert button svg{width:100%}article[role=main]:has(.farama-env-icon-container) .farama-env-icon-container{display:flex;margin-top:7px;position:absolute}article[role=main]:has(.farama-env-icon-container) .section h1:first-child,article[role=main]:has(.farama-env-icon-container) .section h2:first-child,article[role=main]:has(.farama-env-icon-container) section h1:first-child,article[role=main]:has(.farama-env-icon-container) section h2:first-child{margin-left:34px}.farama-env-icon{height:32px}.env-grid{box-sizing:border-box;display:flex;flex-wrap:wrap;justify-content:center;width:100%}.env-grid__cell{display:flex;flex-direction:column;height:180px;padding:10px;width:180px}.cell__image-container{display:flex;height:148px;justify-content:center}.cell__image-container img{max-height:100%;-o-object-fit:contain;object-fit:contain}.cell__title{align-items:flex-end;display:flex;height:32px;justify-content:center;line-height:16px;text-align:center}.more-btn{display:block;margin:12px auto;width:240px}html:has(.farama-header-menu.active){overflow:hidden}body{--farama-header-height:52px;--farama-header-logo-margin:10px}.farama-header{background-color:var(--color-background-secondary);border-bottom:1px solid var(--color-header-border);box-sizing:border-box;display:flex;height:var(--farama-header-height);padding:0 36px 0 24px;position:absolute;width:100%;z-index:95}.farama-header .farama-header__container{display:flex;justify-content:space-between;margin:0 auto;max-width:1400px;width:100%}.farama-header a{color:var(--color-foreground-primary);text-decoration:none;transition:color .125s ease}.farama-header a:hover{color:var(--color-foreground-secondary)}.farama-header .farama-header__logo{margin:var(--farama-header-logo-margin);max-height:calc(var(--farama-header-height) - var(--farama-header-logo-margin))}.farama-header .farama-header__title{align-self:center;font-size:var(--font-size--normal);font-weight:400;margin:0 0 2px;padding:0 0 0 4px}.farama-header .farama-header__left,.farama-header .farama-header__left a{display:flex}.farama-header .farama-header__left--mobile{display:none}.farama-header .farama-header__left--mobile .nav-overlay-icon svg{stroke:var(--color-foreground-primary);fill:var(--color-foreground-primary);stroke-width:2px;padding:0 6px;width:20px}.farama-header .farama-header__right{align-items:center;display:flex;z-index:2}.farama-header .farama-header__right .farama-header__nav{display:flex;height:100%;list-style:none}.farama-header .farama-header__right .farama-header__nav li{align-items:center;cursor:pointer;display:flex;margin-left:20px;text-decoration:none}.farama-header .farama-header__right .farama-header__nav li a{align-items:center;display:flex;height:100%}.farama-header .farama-header__right .farama-header__nav li .farama-header__dropdown-container{align-items:center;display:flex;height:100%;position:relative}.farama-header .farama-header__right .farama-header__nav li .farama-header__dropdown-container:hover .farama-header__dropdown-menu{display:block}.farama-header .farama-header__right .farama-header__nav li .farama-header__dropdown-container svg{fill:var(--color-foreground-primary);width:32px}.farama-header .farama-header__right .farama-header__nav li .farama-header__dropdown-container .farama-header__dropdown-menu{background:var(--color-background-hover);border:1px solid var(--color-background-border);display:none;position:absolute;right:0;top:var(--farama-header-height);z-index:9999}.farama-header .farama-header__right .farama-header__nav li .farama-header__dropdown-container .farama-header__dropdown-menu ul{display:inherit;margin:0;padding:6px 14px}.farama-header .farama-header__right .farama-header__nav li .farama-header__dropdown-container .farama-header__dropdown-menu li{margin:0;padding:6px 0}.farama-header .farama-header__right .farama-header-menu{display:flex;justify-content:center;position:relative}.farama-header .farama-header__right .farama-header-menu .farama-header-menu__btn{background:none;border:none;cursor:pointer;display:flex}.farama-header .farama-header__right .farama-header-menu .farama-header-menu__btn img{width:26px}.farama-header .farama-header__right .farama-header-menu .farama-header-menu__btn svg{stroke:var(--color-foreground-primary);stroke-width:2px;align-self:center;width:14px}.farama-header .farama-header__right .farama-header-menu.active .farama-header-menu-container{transform:translateY(100vh)}.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container{background-color:var(--color-background-secondary);border-left:1px solid var(--color-background-border);box-sizing:border-box;height:100%;overflow:auto;position:fixed;right:0;top:-100vh;transform:translateY(0);transition:transform .2s ease-in;width:100%;z-index:99}.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__header{align-items:center;border-bottom:1px solid var(--color-background-border);box-sizing:border-box;display:flex;margin:0 auto;max-width:1400px;padding:7px 52px;position:relative;width:100%}.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__header a{align-items:center;display:flex}.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__header a .farama-header-menu__logo{width:36px}.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__header a span{color:var(--color-sidebar-brand-text);padding-left:8px}.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__header .farama-header-menu-header__right{padding-right:inherit;position:absolute;right:0}.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__header .farama-header-menu-header__right button{background:none;border:none;cursor:pointer;display:flex}.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__header .farama-header-menu-header__right button svg{color:var(--color-foreground-primary);width:20px}.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__body{box-sizing:border-box;display:flex;flex-wrap:wrap;margin:0 auto;max-width:1400px;padding:22px 52px;width:100%}.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__body .farama-header-menu__section{margin-bottom:24px;min-width:250px}.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__body .farama-header-menu__section .farama-header-menu__section-title{display:block;font-size:var(--font-size--normal);font-weight:700;margin-bottom:12px;text-transform:uppercase}.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__body .farama-header-menu__section .farama-header-menu-list{display:inherit;list-style:none;margin:0;padding:0}.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__body .farama-header-menu__section .farama-header-menu-list li:hover{background-color:var(--color-background-hover)}.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__body .farama-header-menu__section .farama-header-menu-list li a{align-items:center;display:flex;padding:12px 18px}.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__body .farama-header-menu__section .farama-header-menu-list li a:hover{color:inherit}.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__body .farama-header-menu__section .farama-header-menu-list li a img{margin-right:10px;width:24px}.farama-sidebar__title{align-items:center;display:flex;margin-top:var(--sidebar-search-space-above);min-height:calc(52px - var(--sidebar-search-space-above));padding-right:4px}.farama-sidebar__title img{height:calc(var(--farama-header-height) - 20px);margin:var(--farama-header-logo-margin)}.farama-sidebar__title span{color:var(--color-foreground-primary)}.farama-sidebar__title span:hover{text-decoration:none}.sidebar-brand{align-items:center;flex-direction:row;padding:var(--sidebar-item-spacing-vertical)}.sidebar-brand .sidebar-logo-container{display:flex;height:auto;max-width:55px}.sidebar-brand .sidebar-brand-text{font-size:1.3rem;padding-left:11px}.mobile-header .header-center{opacity:0;transition:opacity easy-in .2s}.mobile-header.scrolled .header-center{opacity:1}.sphx-glr-script-out{color:var(--color-foreground-secondary);display:flex;gap:.5em}.sphx-glr-script-out:before{content:"Out:";line-height:1.4;padding-top:10px}.sphx-glr-script-out .highlight{overflow-x:auto}.sphx-glr-thumbcontainer{z-index:1}div.sphx-glr-download a{background:#0f4a65;box-sizing:border-box;max-width:100%;width:340px}div.sphx-glr-download a:hover{background:#0d3a4e;box-shadow:none}body[data-theme=dark] div.sphx-glr-download a{background:#0f4a65}body[data-theme=dark] div.sphx-glr-download a:hover{background:#0d3a4e}body[data-theme=light] div.sphx-glr-download a{background:#f9d4a1}body[data-theme=light] div.sphx-glr-download a:hover{background:#d9b481}.sphx-glr-thumbcontainer img{background-color:#fff;border-radius:4px}@media(max-width:950px){.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__header{padding:7px 42px}.farama-header .farama-header-menu__btn-name{display:none}}@media(max-width:600px){.farama-header{padding:0 4px}.farama-header .farama-header__title{font-size:var(--font-size--small)}.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__header{padding:8px 12px}.farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__body{padding:18px 12px}}@media(max-width:480px){.farama-header .farama-header__title{width:110px}.farama-header .farama-header-menu__btn-name{text-align:right;width:100px}}body[data-theme=dark] .farama-black-logo-invert,body[data-theme=light] .farama-white-logo-invert{filter:invert(1)}body[data-theme=dark] img[src*="//render.githubusercontent.com/render/math"]{filter:invert(90%)} /*# sourceMappingURL=furo-extensions.css.map*/ \ No newline at end of file diff --git a/_static/styles/furo-extensions.css.map b/_static/styles/furo-extensions.css.map index 66fd043e8..4e982ce3c 100644 --- a/_static/styles/furo-extensions.css.map +++ b/_static/styles/furo-extensions.css.map @@ -1 +1 @@ -{"version":3,"file":"styles/furo-extensions.css","mappings":"AAGA,2BACE,oFACA,4CAKE,6CAHA,YACA,eAEA,CACA,kDACE,yCAEF,8CACE,sCAEJ,8CACE,kDAEJ,2BAGE,uBACA,cAHA,gBACA,UAEA,CAGA,yCACE,mBAEF,gDAEE,gDADA,YACA,CACA,sDACE,gDACF,yDACE,sCAEJ,+CACE,UACA,qDACE,UAGF,mDACE,eAEJ,yEAEE,4DAEA,mHASE,mBAPA,kBAEA,YADA,oBAGA,aADA,gBAIA,CAEA,qIAEE,WADA,UACA,CAEJ,uGACE,aAEF,iUAGE,cAEF,mHACE,aC1EJ,gCACE,mCAEF,0BAKE,mBAUA,8CACA,YAFA,mCAKA,eAZA,cALA,UASA,YADA,YAYA,iCAdA,YAcA,CAEA,gCAEE,8CADA,gCACA,CAEF,gCAGE,6BADA,mCADA,YAEA,CAEF,kCAEE,cADA,oBACA,CACA,wCACE,cAEJ,8BACE,UC5CN,KAEE,6CAA8C,CAC9C,uDAAwD,CACxD,uDAAwD,CAGxD,iCAAsC,CAGtC,+CAAgD,CAChD,uDAAwD,CACxD,uDAAwD,CACxD,oDAAqD,CACrD,6DAA8D,CAC9D,6DAA8D,CAG9D,uDAAwD,CACxD,yDAA0D,CAC1D,4DAA6D,CAC7D,2DAA4D,CAC5D,8DAA+D,CAC/D,iEAAkE,CAClE,uDAAwD,CACxD,wDAAyD,CAG3D,gBACE,qFAGF,SACE,6EAEF,cACE,uFAEF,cACE,uFAEF,cACE,uFAGF,qBACE,eAEF,mBACE,WACA,eChDF,KACE,gDAAiD,CACjD,uDAAwD,CACxD,qDAAsD,CACtD,4DAA6D,CAC7D,oCAAqC,CACrC,2CAA4C,CAC5C,4CAA6C,CAC7C,mDAAoD,CACpD,wBAAyB,CACzB,oBAAqB,CACrB,6CAA8C,CAC9C,gCAAiC,CACjC,yDAA0D,CAC1D,uDAAwD,CACxD,8DAA+D,CCbjE,uBACE,eACA,eACA,gBAGF,iBACE,YACA,+EAGF,iBACE,mDACA,8BCfF,GACI,iBACJ,GACI,iBACJ,GACI,iBAGJ,qCACI,kBAEJ,iCACI,kBAEJ,kCACI,mBAIJ,cAKI,mDAEA,oDACA,SAFA,sCAJA,aAOA,OALA,gBAHA,eAEA,WAOA,cAEJ,yBAEI,mBADA,aAIA,YADA,4BADA,WAEA,CAEJ,gBACI,OAEJ,qBACI,6BACA,YAIA,eAFA,YACA,UAFA,UAGA,CAEJ,gCACI,uCAEJ,yBACI,WAKA,8EAEI,aACA,eAFA,iBAEA,CAEJ,0SACI,iBAER,iBACI,YAIJ,UAKI,sBAJA,aACA,eACA,uBACA,UACA,CAEJ,gBACI,aACA,sBAEA,aACA,aAFA,WAEA,CAEJ,uBACI,aACA,aACA,uBAEJ,2BACI,gBACA,yCAEJ,aAII,qBAHA,aAIA,YAHA,uBAIA,iBAHA,iBAGA,CAEJ,UAGI,cADA,iBADA,WAEA,CAIJ,qCACI,gBAEJ,KACI,2BAA4B,CAC5B,gCAAiC,CAErC,eAMI,mDADA,mDAGA,sBANA,aAEA,mCAGA,sBANA,kBAEA,WAMA,WAEA,yCAGI,aACA,cAFA,iBADA,UAGA,CAEJ,iBACI,sCACA,qBACA,4BAEA,uBACI,wCAER,oCAEI,wCADA,+EACA,CAEJ,qCAKI,kBAJA,mCACA,gBACA,eACA,iBACA,CAKA,0EACI,aAER,4CACI,aAEA,kEAEI,uCACA,qCACA,iBACA,cAJA,UAIA,CAER,qCAEI,mBADA,aAGA,OADA,yBAEA,UAEA,yDACI,aAEA,YADA,eACA,CAEA,4DAII,mBACA,eAFA,aADA,iBADA,oBAIA,CAEA,8DAGI,mBADA,aADA,WAEA,CAEJ,+FAGI,mBADA,aAEA,YAHA,iBAGA,CAGI,mIACI,cAER,mGAEI,qCADA,UACA,CAEJ,6HAKI,yCADA,gDAGA,aANA,kBAEA,QADA,gCAIA,YACA,CAEA,gIACI,gBACA,SACA,iBAEJ,gIACI,SACA,cAEpB,yDAEI,aACA,uBAFA,iBAEA,CAEA,kFAEI,gBACA,YACA,eAHA,YAGA,CAEA,sFACI,WACJ,sFAEI,uCACA,iBACA,kBAHA,UAGA,CAGR,8FACI,4BAEJ,uFAUI,mDACA,qDAHA,sBAFA,YAMA,cAXA,eAEA,QACA,WAGA,wBAEA,iCAJA,WAHA,UAUA,CAEA,mHASI,mBAFA,uDAHA,sBAIA,aAHA,cAFA,iBAGA,iBALA,kBACA,UAOA,CAEA,qHAEI,mBADA,YACA,CAEA,+IACI,WAEJ,0HACI,sCACA,iBAER,qJAGI,sBAFA,kBACA,OACA,CAEA,4JAEI,gBACA,YACA,eAHA,YAGA,CAEA,gKAEI,sCADA,UACA,CAEhB,iHAKI,sBAJA,aAMA,eADA,cAHA,iBACA,kBAFA,UAKA,CAEA,8IAEI,mBADA,eACA,CAEA,iLACI,cACA,mCACA,gBAEA,mBADA,wBACA,CAEJ,uKACI,gBAGA,gBAFA,SACA,SACA,CAII,gLACI,+CAEJ,4KAGI,mBAFA,aACA,iBACA,CAEA,kLACI,cAEJ,gLAGI,oBADA,kBADA,UAEA,CAExC,uBAEI,mBADA,aAIA,6CADA,0DADA,iBAEA,CAEA,2BACI,gDACA,wCACJ,4BACI,sCACA,kCACI,qBAEZ,eAGI,mBAFA,mBACA,4CACA,CAEA,uCACI,aAEA,YADA,cACA,CAEJ,mCACI,iBACA,kBAGJ,8BAEI,SAAQ,CADR,8BACA,CAGJ,uCACI,UAIR,qBACI,wCACA,aACA,SAEJ,4BACI,eACA,gBACA,iBAEJ,gCACI,gBAGJ,wBACI,mHACI,iBAEJ,6CACI,cAGR,wBACI,eACI,cAEA,qCACI,kCAGA,mHACI,iBACJ,iHACI,mBAGhB,wBAEQ,qCACI,YAEJ,6CACI,iBACA,aAUR,iGACI,iBAEJ,6EACI","sources":["webpack:///./src/furo/assets/styles/extensions/_readthedocs.sass","webpack:///./src/furo/assets/styles/extensions/_copybutton.sass","webpack:///./src/furo/assets/styles/extensions/_sphinx-design.sass","webpack:///./src/furo/assets/styles/extensions/_sphinx-inline-tabs.sass","webpack:///./src/furo/assets/styles/extensions/_sphinx-panels.sass","webpack:///./src/furo/assets/styles/extensions/_farama.sass"],"sourcesContent":["// This file contains the styles used for tweaking how ReadTheDoc's embedded\n// contents would show up inside the theme.\n\n#furo-sidebar-ad-placement\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)\n .ethical-sidebar\n // Remove the border and box-shadow.\n border: none\n box-shadow: none\n // Manage the background colors.\n background: var(--color-background-secondary)\n &:hover\n background: var(--color-background-hover)\n // Ensure the text is legible.\n a\n color: var(--color-foreground-primary)\n\n .ethical-callout a\n color: var(--color-foreground-secondary) !important\n\n#furo-readthedocs-versions\n position: static\n width: 100%\n background: transparent\n display: block\n\n // Make the background color fit with the theme's aesthetic.\n .rst-versions\n background: rgb(26, 28, 30)\n\n .rst-current-version\n cursor: unset\n background: var(--color-sidebar-item-background)\n &:hover\n background: var(--color-sidebar-item-background)\n .fa-book\n color: var(--color-foreground-primary)\n\n > .rst-other-versions\n padding: 0\n small\n opacity: 1\n\n .injected\n .rst-versions\n position: unset\n\n &:hover,\n &:focus-within\n box-shadow: 0 0 0 1px var(--color-sidebar-background-border)\n\n .rst-current-version\n // Undo the tweaks done in RTD's CSS\n font-size: inherit\n line-height: inherit\n height: auto\n text-align: right\n padding: 12px\n\n // Match the rest of the body\n background: #1a1c1e\n\n .fa-book\n float: left\n color: white\n\n .fa-caret-down\n display: none\n\n .rst-current-version,\n .rst-other-versions,\n .injected\n display: block\n\n > .rst-current-version\n display: none\n",".highlight\n &:hover button.copybtn\n color: var(--color-code-foreground)\n\n button.copybtn\n // Make it visible\n opacity: 1\n\n // Align things correctly\n align-items: center\n\n height: 1.25em\n width: 1.25em\n\n top: 0.625rem // $code-spacing-vertical\n right: 0.5rem\n\n // Make it look better\n color: var(--color-background-item)\n background-color: var(--color-code-background)\n border: none\n\n // Change to cursor to make it obvious that you can click on it\n cursor: pointer\n\n // Transition smoothly, for aesthetics\n transition: color 300ms, opacity 300ms\n\n &:hover\n color: var(--color-brand-content)\n background-color: var(--color-code-background)\n\n &::after\n display: none\n color: var(--color-code-foreground)\n background-color: transparent\n\n &.success\n transition: color 0ms\n color: #22863a\n &::after\n display: block\n\n svg\n padding: 0\n","body\n // Colors\n --sd-color-primary: var(--color-brand-primary)\n --sd-color-primary-highlight: var(--color-brand-content)\n --sd-color-primary-text: var(--color-background-primary)\n\n // Shadows\n --sd-color-shadow: rgba(0, 0, 0, 0.05)\n\n // Cards\n --sd-color-card-border: var(--color-card-border)\n --sd-color-card-border-hover: var(--color-brand-content)\n --sd-color-card-background: var(--color-card-background)\n --sd-color-card-text: var(--color-foreground-primary)\n --sd-color-card-header: var(--color-card-marginals-background)\n --sd-color-card-footer: var(--color-card-marginals-background)\n\n // Tabs\n --sd-color-tabs-label-active: var(--color-brand-content)\n --sd-color-tabs-label-hover: var(--color-foreground-muted)\n --sd-color-tabs-label-inactive: var(--color-foreground-muted)\n --sd-color-tabs-underline-active: var(--color-brand-content)\n --sd-color-tabs-underline-hover: var(--color-foreground-border)\n --sd-color-tabs-underline-inactive: var(--color-background-border)\n --sd-color-tabs-overline: var(--color-background-border)\n --sd-color-tabs-underline: var(--color-background-border)\n\n// Tabs\n.sd-tab-content\n box-shadow: 0 -2px var(--sd-color-tabs-overline), 0 1px var(--sd-color-tabs-underline)\n\n// Shadows\n.sd-card // Have a shadow by default\n box-shadow: 0 0.1rem 0.25rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1)\n\n.sd-shadow-sm\n box-shadow: 0 0.1rem 0.25rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n.sd-shadow-md\n box-shadow: 0 0.3rem 0.75rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n.sd-shadow-lg\n box-shadow: 0 0.6rem 1.5rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n// Cards\n.sd-card-hover:hover // Don't change scale on hover\n transform: none\n\n.sd-cards-carousel // Have a bit of gap in the carousel by default\n gap: 0.25rem\n padding: 0.25rem\n","// This file contains styles to tweak sphinx-inline-tabs to work well with Furo.\n\nbody\n --tabs--label-text: var(--color-foreground-muted)\n --tabs--label-text--hover: var(--color-foreground-muted)\n --tabs--label-text--active: var(--color-brand-content)\n --tabs--label-text--active--hover: var(--color-brand-content)\n --tabs--label-background: transparent\n --tabs--label-background--hover: transparent\n --tabs--label-background--active: transparent\n --tabs--label-background--active--hover: transparent\n --tabs--padding-x: 0.25em\n --tabs--margin-x: 1em\n --tabs--border: var(--color-background-border)\n --tabs--label-border: transparent\n --tabs--label-border--hover: var(--color-foreground-muted)\n --tabs--label-border--active: var(--color-brand-content)\n --tabs--label-border--active--hover: var(--color-brand-content)\n","// This file contains styles to tweak sphinx-panels to work well with Furo.\n\n// sphinx-panels includes Bootstrap 4, which uses .container which can conflict\n// with docutils' `.. container::` directive.\n[role=\"main\"] .container\n max-width: initial\n padding-left: initial\n padding-right: initial\n\n// Make the panels look nicer!\n.shadow.docutils\n border: none\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n// Make panel colors respond to dark mode\n.sphinx-bs .card\n background-color: var(--color-background-secondary)\n color: var(--color-foreground)\n","// Farama Base\n\nh1\n font-size: 2.2rem\nh2\n font-size: 1.7rem\nh3\n font-size: 1.4rem\n\n// If menu is active then all elements except the menu are not visible (i.e. only element with aria-hidden=\"true\")\nhtml:has(.farama-header-menu.active)\n visibility: hidden\n\n.farama-hidden[aria-hidden=\"true\"]\n visibility: hidden\n\n.farama-hidden[aria-hidden=\"false\"]\n visibility: visible\n\n// Cookies Alert\n\n.cookie-alert\n position: fixed\n display: flex\n width: 100%\n min-height: 70px\n background-color: var(--color-background-secondary)\n color: var(--color-foreground-primary)\n border-top: 1px solid var(--color-background-border)\n bottom: 0\n left: 0\n z-index: 99999\n\n.cookie-alert__container\n display: flex\n align-items: center\n width: 700px\n max-width: calc(100% - 28px)\n margin: auto\n\n.cookie-alert p\n flex: 1\n\n.cookie-alert button\n background-color: transparent\n border: none\n width: 26px\n height: 26px\n padding: 0\n cursor: pointer\n\n.cookie-alert button svg .cls-1\n stroke: var(--color-foreground-primary)\n\n.cookie-alert button svg\n width: 100%\n\n// Env Icons\n\narticle[role=main]:has(.farama-env-icon-container)\n .farama-env-icon-container\n position: absolute\n display: flex\n margin-top: 7px\n\n .section h1:first-child, .section h2:first-child, section h1:first-child, section h2:first-child\n margin-left: 34px\n\n.farama-env-icon\n height: 32px\n\n// Envinronments grid\n\n.env-grid\n display: flex\n flex-wrap: wrap\n justify-content: center\n width: 100%\n box-sizing: border-box\n\n.env-grid__cell\n display: flex\n flex-direction: column\n width: 180px\n height: 180px\n padding: 10px\n\n.cell__image-container\n display: flex\n height: 148px\n justify-content: center\n\n.cell__image-container img\n max-height: 100%\n object-fit: contain\n\n.cell__title\n display: flex\n justify-content: center\n text-align: center\n align-items: flex-end\n height: 32px\n line-height: 16px\n\n.more-btn\n width: 240px\n margin: 12px auto\n display: block\n\n// Farama Header\n\nhtml:has(.farama-header-menu.active)\n overflow: hidden\n\nbody\n --farama-header-height: 52px\n --farama-header-logo-margin: 10px\n\n.farama-header\n position: absolute\n display: flex\n width: 100%\n height: var(--farama-header-height)\n border-bottom: 1px solid var(--color-header-border)\n background-color: var(--color-background-secondary)\n padding: 0 36px 0 24px\n box-sizing: border-box\n z-index: 95\n\n .farama-header__container\n width: 100%\n max-width: 1400px\n display: flex\n margin: 0 auto\n\n a\n color: var(--color-foreground-primary)\n text-decoration: none\n transition: color 0.125s ease\n\n &:hover\n color: var(--color-foreground-secondary)\n\n .farama-header__logo\n max-height: calc(var(--farama-header-height) - var(--farama-header-logo-margin))\n margin: var(--farama-header-logo-margin)\n\n .farama-header__title\n font-size: var(--font-size--normal)\n font-weight: normal\n margin: 0 0 2px 0\n padding: 0 0 0 4px\n align-self: center\n\n .farama-header__left\n display: flex\n\n a\n display: flex\n\n .farama-header__left--mobile\n display: none\n\n .nav-overlay-icon svg\n width: 20px\n stroke: var(--color-foreground-primary)\n fill: var(--color-foreground-primary)\n stroke-width: 2px\n padding: 0 6px\n\n .farama-header__right\n display: flex\n align-items: center\n justify-content: flex-end\n flex: 1\n z-index: 2\n\n .farama-header__nav\n display: flex\n list-style: none\n height: 100%\n\n li\n text-decoration: none\n margin-left: 20px\n display: flex\n align-items: center\n cursor: pointer\n\n a\n height: 100%\n display: flex\n align-items: center\n\n .farama-header__dropdown-container\n position: relative\n display: flex\n align-items: center\n height: 100%\n\n &:hover\n .farama-header__dropdown-menu\n display: block\n\n svg\n width: 32px\n fill: var(--color-foreground-primary)\n\n .farama-header__dropdown-menu\n position: absolute\n top: var(--farama-header-height)\n right: 0\n border: 1px solid var(--color-background-border)\n background: var(--color-background-hover)\n z-index: 9999\n display: none\n\n ul\n display: inherit\n margin: 0\n padding: 6px 14px\n\n li\n margin: 0\n padding: 6px 0\n\n .farama-header-menu\n position: relative\n display: flex\n justify-content: center\n\n .farama-header-menu__btn\n display: flex\n background: none\n border: none\n cursor: pointer\n\n img\n width: 26px\n svg\n width: 14px\n stroke: var(--color-foreground-primary)\n stroke-width: 2px\n align-self: center\n\n\n &.active .farama-header-menu-container\n transform: translateY(100vh)\n\n .farama-header-menu-container\n position: fixed\n z-index: 99\n right: 0\n top: -100vh\n width: 100%\n height: calc(100vh - calc(100vh - 100%))\n transform: translateY(0)\n box-sizing: border-box\n transition: transform 0.2s ease-in\n background-color: var(--color-background-secondary)\n border-left: 1px solid var(--color-background-border)\n overflow: auto\n\n .farama-header-menu__header\n position: relative\n width: 100%\n max-width: 1400px\n box-sizing: border-box\n margin: 0 auto\n padding: 7px 52px\n border-bottom: 1px solid var(--color-background-border)\n display: flex\n align-items: center\n\n a\n display: flex\n align-items: center\n\n .farama-header-menu__logo\n width: 36px\n\n span\n color: var(--color-sidebar-brand-text)\n padding-left: 8px\n\n .farama-header-menu-header__right\n position: absolute\n right: 0\n padding-right: inherit\n\n button\n display: flex\n background: none\n border: none\n cursor: pointer\n\n svg\n width: 20px\n color: var(--color-foreground-primary)\n\n .farama-header-menu__body\n display: flex\n width: 100%\n max-width: 1400px\n padding: 22px 52px\n box-sizing: border-box\n margin: 0 auto\n flex-wrap: wrap\n\n .farama-header-menu__section\n min-width: 250px\n margin-bottom: 24px\n\n .farama-header-menu__section-title\n display: block\n font-size: var(--font-size--normal)\n font-weight: bold\n text-transform: uppercase\n margin-bottom: 12px\n\n .farama-header-menu-list\n display: inherit\n margin: 0\n padding: 0\n list-style: none\n\n li\n\n &:hover\n background-color: var(--color-background-hover)\n\n a\n display: flex\n padding: 12px 18px\n align-items: center\n\n &:hover\n color: inherit\n\n img\n width: 24px\n margin-right: 10px\n filter: invert(100%)\n\n.farama-sidebar__title\n display: flex\n align-items: center\n padding-right: 4px\n min-height: calc(52px - var(--sidebar-search-space-above))\n margin-top: var(--sidebar-search-space-above)\n\n img\n height: calc(var(--farama-header-height) - 20px)\n margin: var(--farama-header-logo-margin)\n span\n color: var(--color-foreground-primary)\n &:hover\n text-decoration: none\n\n.sidebar-brand\n flex-direction: row\n padding: var(--sidebar-item-spacing-vertical)\n align-items: center\n\n .sidebar-logo-container\n display: flex\n max-width: 55px\n height: auto\n\n .sidebar-brand-text\n font-size: 1.3rem\n padding-left: 11px\n\n.mobile-header\n .header-center\n transition: opacity 0.2s easy-in\n opacity: 0\n\n.mobile-header.scrolled\n .header-center\n opacity: 1\n\n// Sphinx Gallery\n\n.sphx-glr-script-out\n color: var(--color-foreground-secondary)\n display: flex\n gap: 0.5em\n\n.sphx-glr-script-out::before\n content: \"Out:\"\n line-height: 1.4\n padding-top: 10px\n\n.sphx-glr-script-out .highlight\n overflow-x: auto\n\n\n@media (max-width: 950px)\n .farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__header\n padding: 7px 42px\n\n .farama-header .farama-header-menu__btn-name\n display: none\n\n\n@media (max-width: 600px)\n .farama-header\n padding: 0 4px\n\n .farama-header__title\n font-size: var(--font-size--small)\n\n .farama-header__right .farama-header-menu .farama-header-menu-container\n .farama-header-menu__header\n padding: 8px 12px\n .farama-header-menu__body\n padding: 18px 12px\n\n\n@media (max-width: 480px)\n .farama-header\n .farama-header__title\n width: 110px\n\n .farama-header-menu__btn-name\n text-align: right\n width: 100px\n\n\nbody[data-theme=\"light\"]\n .farama-white-logo-invert\n filter: invert(1)\n\n// Github math render\n\nbody[data-theme=\"dark\"]\n .farama-black-logo-invert\n filter: invert(1)\n\n img[src*=\"//render.githubusercontent.com/render/math\"]\n filter: invert(90%)\n"],"names":[],"sourceRoot":""} \ No newline at end of file +{"version":3,"file":"styles/furo-extensions.css","mappings":"AAGA,2BACE,oFACA,4CAKE,6CAHA,YACA,eAEA,CACA,kDACE,yCAEF,8CACE,sCAEJ,8CACE,kDAEJ,2BAGE,uBACA,cAHA,gBACA,UAEA,CAGA,yCACE,mBAEF,gDAEE,gDADA,YACA,CACA,sDACE,gDACF,yDACE,sCAEJ,+CACE,UACA,qDACE,UAGF,mDACE,eAEJ,yEAEE,4DAEA,mHASE,mBAPA,kBAEA,YADA,oBAGA,aADA,gBAIA,CAEA,qIAEE,WADA,UACA,CAEJ,uGACE,aAEF,iUAGE,cAEF,mHACE,aC1EJ,gCACE,mCAEF,0BAKE,mBAUA,8CACA,YAFA,mCAKA,eAZA,cALA,UASA,YADA,YAYA,iCAdA,YAcA,CAEA,gCAEE,8CADA,gCACA,CAEF,gCAGE,6BADA,mCADA,YAEA,CAEF,kCAEE,cADA,oBACA,CACA,wCACE,cAEJ,8BACE,UC5CN,KAEE,6CAA8C,CAC9C,uDAAwD,CACxD,uDAAwD,CAGxD,iCAAsC,CAGtC,+CAAgD,CAChD,uDAAwD,CACxD,uDAAwD,CACxD,oDAAqD,CACrD,6DAA8D,CAC9D,6DAA8D,CAG9D,uDAAwD,CACxD,yDAA0D,CAC1D,4DAA6D,CAC7D,2DAA4D,CAC5D,8DAA+D,CAC/D,iEAAkE,CAClE,uDAAwD,CACxD,wDAAyD,CAG3D,gBACE,qFAGF,SACE,6EAEF,cACE,uFAEF,cACE,uFAEF,cACE,uFAGF,qBACE,eAEF,mBACE,WACA,eChDF,KACE,gDAAiD,CACjD,uDAAwD,CACxD,qDAAsD,CACtD,4DAA6D,CAC7D,oCAAqC,CACrC,2CAA4C,CAC5C,4CAA6C,CAC7C,mDAAoD,CACpD,wBAAyB,CACzB,oBAAqB,CACrB,6CAA8C,CAC9C,gCAAiC,CACjC,yDAA0D,CAC1D,uDAAwD,CACxD,8DAA+D,CCbjE,uBACE,eACA,eACA,gBAGF,iBACE,YACA,+EAGF,iBACE,mDACA,8BCbF,GACI,iBACJ,GACI,iBACJ,GACI,iBAGJ,qCACI,kBAEJ,iCACI,kBAEJ,kCACI,mBAIJ,cAKI,mDAEA,oDACA,SAFA,sCAJA,aAOA,OALA,gBAHA,eAEA,WAOA,cAEJ,yBAEI,mBADA,aAIA,YADA,4BADA,WAEA,CAEJ,gBACI,OAEJ,qBACI,6BACA,YAIA,eAFA,YACA,UAFA,UAGA,CAEJ,gCACI,uCAEJ,yBACI,WAKA,8EAEI,aACA,eAFA,iBAEA,CAEJ,0SACI,iBAER,iBACI,YAIJ,UAKI,sBAJA,aACA,eACA,uBACA,UACA,CAEJ,gBACI,aACA,sBAEA,aACA,aAFA,WAEA,CAEJ,uBACI,aACA,aACA,uBAEJ,2BACI,gBACA,yCAEJ,aAII,qBAHA,aAIA,YAHA,uBAIA,iBAHA,iBAGA,CAEJ,UAGI,cADA,iBADA,WAEA,CAIJ,qCACI,gBAEJ,KACI,2BAA4B,CAC5B,gCAAiC,CAErC,eAMI,mDADA,mDAGA,sBANA,aAEA,mCAGA,sBANA,kBAEA,WAMA,WAEA,yCAGI,aAEA,8BADA,cAFA,iBADA,UAIA,CAEJ,iBACI,sCACA,qBACA,4BAEA,uBACI,wCAER,oCAEI,wCADA,+EACA,CAEJ,qCAKI,kBAJA,mCACA,gBACA,eACA,iBACA,CAKA,0EACI,aAER,4CACI,aAEA,kEAEI,uCACA,qCACA,iBACA,cAJA,UAIA,CAER,qCAEI,mBADA,aAEA,UAEA,yDACI,aAEA,YADA,eACA,CAEA,4DAII,mBACA,eAFA,aADA,iBADA,oBAIA,CAEA,8DAGI,mBADA,aADA,WAEA,CAEJ,+FAGI,mBADA,aAEA,YAHA,iBAGA,CAGI,mIACI,cAER,mGAEI,qCADA,UACA,CAEJ,6HAKI,yCADA,gDAGA,aANA,kBAEA,QADA,gCAIA,YACA,CAEA,gIACI,gBACA,SACA,iBAEJ,gIACI,SACA,cAEpB,yDAEI,aACA,uBAFA,iBAEA,CAEA,kFAEI,gBACA,YACA,eAHA,YAGA,CAEA,sFACI,WACJ,sFAEI,uCACA,iBACA,kBAHA,UAGA,CAGR,8FACI,4BAEJ,uFAUI,mDACA,qDAHA,sBAFA,YAMA,cAXA,eAEA,QACA,WAGA,wBAEA,iCAJA,WAHA,UAUA,CAEA,mHASI,mBAFA,uDAHA,sBAIA,aAHA,cAFA,iBAGA,iBALA,kBACA,UAOA,CAEA,qHAEI,mBADA,YACA,CAEA,+IACI,WAEJ,0HACI,sCACA,iBAER,qJAGI,sBAFA,kBACA,OACA,CAEA,4JAEI,gBACA,YACA,eAHA,YAGA,CAEA,gKAEI,sCADA,UACA,CAEhB,iHAKI,sBAJA,aAMA,eADA,cAHA,iBACA,kBAFA,UAKA,CAEA,8IAEI,mBADA,eACA,CAEA,iLACI,cACA,mCACA,gBAEA,mBADA,wBACA,CAEJ,uKACI,gBAGA,gBAFA,SACA,SACA,CAII,gLACI,+CAEJ,4KAGI,mBAFA,aACA,iBACA,CAEA,kLACI,cAEJ,gLAEI,kBADA,UACA,CAExC,uBAEI,mBADA,aAIA,6CADA,0DADA,iBAEA,CAEA,2BACI,gDACA,wCACJ,4BACI,sCACA,kCACI,qBAEZ,eAGI,mBAFA,mBACA,4CACA,CAEA,uCACI,aAEA,YADA,cACA,CAEJ,mCACI,iBACA,kBAGJ,8BAEI,SAAQ,CADR,8BACA,CAGJ,uCACI,UAIR,qBACI,wCACA,aACA,SAEJ,4BACI,eACA,gBACA,iBAEJ,gCACI,gBAEJ,yBACI,UAEJ,wBAII,mBADA,sBADA,eADA,WAGA,CAEJ,8BACI,mBACA,gBAGA,8CACI,mBACJ,oDACI,mBAGJ,+CACI,mBACJ,qDACI,mBAER,6BACI,sBACA,kBAGJ,wBACI,mHACI,iBAEJ,6CACI,cAGR,wBACI,eACI,cAEA,qCACI,kCAGA,mHACI,iBACJ,iHACI,mBAGhB,wBAEQ,qCACI,YAEJ,6CACI,iBACA,aAUR,iGACI,iBAEJ,6EACI","sources":["webpack:///./src/furo/assets/styles/extensions/_readthedocs.sass","webpack:///./src/furo/assets/styles/extensions/_copybutton.sass","webpack:///./src/furo/assets/styles/extensions/_sphinx-design.sass","webpack:///./src/furo/assets/styles/extensions/_sphinx-inline-tabs.sass","webpack:///./src/furo/assets/styles/extensions/_sphinx-panels.sass","webpack:///./src/furo/assets/styles/extensions/_farama.sass"],"sourcesContent":["// This file contains the styles used for tweaking how ReadTheDoc's embedded\n// contents would show up inside the theme.\n\n#furo-sidebar-ad-placement\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)\n .ethical-sidebar\n // Remove the border and box-shadow.\n border: none\n box-shadow: none\n // Manage the background colors.\n background: var(--color-background-secondary)\n &:hover\n background: var(--color-background-hover)\n // Ensure the text is legible.\n a\n color: var(--color-foreground-primary)\n\n .ethical-callout a\n color: var(--color-foreground-secondary) !important\n\n#furo-readthedocs-versions\n position: static\n width: 100%\n background: transparent\n display: block\n\n // Make the background color fit with the theme's aesthetic.\n .rst-versions\n background: rgb(26, 28, 30)\n\n .rst-current-version\n cursor: unset\n background: var(--color-sidebar-item-background)\n &:hover\n background: var(--color-sidebar-item-background)\n .fa-book\n color: var(--color-foreground-primary)\n\n > .rst-other-versions\n padding: 0\n small\n opacity: 1\n\n .injected\n .rst-versions\n position: unset\n\n &:hover,\n &:focus-within\n box-shadow: 0 0 0 1px var(--color-sidebar-background-border)\n\n .rst-current-version\n // Undo the tweaks done in RTD's CSS\n font-size: inherit\n line-height: inherit\n height: auto\n text-align: right\n padding: 12px\n\n // Match the rest of the body\n background: #1a1c1e\n\n .fa-book\n float: left\n color: white\n\n .fa-caret-down\n display: none\n\n .rst-current-version,\n .rst-other-versions,\n .injected\n display: block\n\n > .rst-current-version\n display: none\n",".highlight\n &:hover button.copybtn\n color: var(--color-code-foreground)\n\n button.copybtn\n // Make it visible\n opacity: 1\n\n // Align things correctly\n align-items: center\n\n height: 1.25em\n width: 1.25em\n\n top: 0.625rem // $code-spacing-vertical\n right: 0.5rem\n\n // Make it look better\n color: var(--color-background-item)\n background-color: var(--color-code-background)\n border: none\n\n // Change to cursor to make it obvious that you can click on it\n cursor: pointer\n\n // Transition smoothly, for aesthetics\n transition: color 300ms, opacity 300ms\n\n &:hover\n color: var(--color-brand-content)\n background-color: var(--color-code-background)\n\n &::after\n display: none\n color: var(--color-code-foreground)\n background-color: transparent\n\n &.success\n transition: color 0ms\n color: #22863a\n &::after\n display: block\n\n svg\n padding: 0\n","body\n // Colors\n --sd-color-primary: var(--color-brand-primary)\n --sd-color-primary-highlight: var(--color-brand-content)\n --sd-color-primary-text: var(--color-background-primary)\n\n // Shadows\n --sd-color-shadow: rgba(0, 0, 0, 0.05)\n\n // Cards\n --sd-color-card-border: var(--color-card-border)\n --sd-color-card-border-hover: var(--color-brand-content)\n --sd-color-card-background: var(--color-card-background)\n --sd-color-card-text: var(--color-foreground-primary)\n --sd-color-card-header: var(--color-card-marginals-background)\n --sd-color-card-footer: var(--color-card-marginals-background)\n\n // Tabs\n --sd-color-tabs-label-active: var(--color-brand-content)\n --sd-color-tabs-label-hover: var(--color-foreground-muted)\n --sd-color-tabs-label-inactive: var(--color-foreground-muted)\n --sd-color-tabs-underline-active: var(--color-brand-content)\n --sd-color-tabs-underline-hover: var(--color-foreground-border)\n --sd-color-tabs-underline-inactive: var(--color-background-border)\n --sd-color-tabs-overline: var(--color-background-border)\n --sd-color-tabs-underline: var(--color-background-border)\n\n// Tabs\n.sd-tab-content\n box-shadow: 0 -2px var(--sd-color-tabs-overline), 0 1px var(--sd-color-tabs-underline)\n\n// Shadows\n.sd-card // Have a shadow by default\n box-shadow: 0 0.1rem 0.25rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1)\n\n.sd-shadow-sm\n box-shadow: 0 0.1rem 0.25rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n.sd-shadow-md\n box-shadow: 0 0.3rem 0.75rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n.sd-shadow-lg\n box-shadow: 0 0.6rem 1.5rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n// Cards\n.sd-card-hover:hover // Don't change scale on hover\n transform: none\n\n.sd-cards-carousel // Have a bit of gap in the carousel by default\n gap: 0.25rem\n padding: 0.25rem\n","// This file contains styles to tweak sphinx-inline-tabs to work well with Furo.\n\nbody\n --tabs--label-text: var(--color-foreground-muted)\n --tabs--label-text--hover: var(--color-foreground-muted)\n --tabs--label-text--active: var(--color-brand-content)\n --tabs--label-text--active--hover: var(--color-brand-content)\n --tabs--label-background: transparent\n --tabs--label-background--hover: transparent\n --tabs--label-background--active: transparent\n --tabs--label-background--active--hover: transparent\n --tabs--padding-x: 0.25em\n --tabs--margin-x: 1em\n --tabs--border: var(--color-background-border)\n --tabs--label-border: transparent\n --tabs--label-border--hover: var(--color-foreground-muted)\n --tabs--label-border--active: var(--color-brand-content)\n --tabs--label-border--active--hover: var(--color-brand-content)\n","// This file contains styles to tweak sphinx-panels to work well with Furo.\n\n// sphinx-panels includes Bootstrap 4, which uses .container which can conflict\n// with docutils' `.. container::` directive.\n[role=\"main\"] .container\n max-width: initial\n padding-left: initial\n padding-right: initial\n\n// Make the panels look nicer!\n.shadow.docutils\n border: none\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n// Make panel colors respond to dark mode\n.sphinx-bs .card\n background-color: var(--color-background-secondary)\n color: var(--color-foreground)\n","// Farama Base\n\n$farama-background: linear-gradient(to right top, #765e3e, #054f5b)\n\nh1\n font-size: 2.2rem\nh2\n font-size: 1.7rem\nh3\n font-size: 1.4rem\n\n// If menu is active then all elements except the menu are not visible (i.e. only element with aria-hidden=\"true\")\nhtml:has(.farama-header-menu.active)\n visibility: hidden\n\n.farama-hidden[aria-hidden=\"true\"]\n visibility: hidden\n\n.farama-hidden[aria-hidden=\"false\"]\n visibility: visible\n\n// Cookies Alert\n\n.cookie-alert\n position: fixed\n display: flex\n width: 100%\n min-height: 70px\n background-color: var(--color-background-secondary)\n color: var(--color-foreground-primary)\n border-top: 1px solid var(--color-background-border)\n bottom: 0\n left: 0\n z-index: 99999\n\n.cookie-alert__container\n display: flex\n align-items: center\n width: 700px\n max-width: calc(100% - 28px)\n margin: auto\n\n.cookie-alert p\n flex: 1\n\n.cookie-alert button\n background-color: transparent\n border: none\n width: 26px\n height: 26px\n padding: 0\n cursor: pointer\n\n.cookie-alert button svg .cls-1\n stroke: var(--color-foreground-primary)\n\n.cookie-alert button svg\n width: 100%\n\n// Env Icons\n\narticle[role=main]:has(.farama-env-icon-container)\n .farama-env-icon-container\n position: absolute\n display: flex\n margin-top: 7px\n\n .section h1:first-child, .section h2:first-child, section h1:first-child, section h2:first-child\n margin-left: 34px\n\n.farama-env-icon\n height: 32px\n\n// Envinronments grid\n\n.env-grid\n display: flex\n flex-wrap: wrap\n justify-content: center\n width: 100%\n box-sizing: border-box\n\n.env-grid__cell\n display: flex\n flex-direction: column\n width: 180px\n height: 180px\n padding: 10px\n\n.cell__image-container\n display: flex\n height: 148px\n justify-content: center\n\n.cell__image-container img\n max-height: 100%\n object-fit: contain\n\n.cell__title\n display: flex\n justify-content: center\n text-align: center\n align-items: flex-end\n height: 32px\n line-height: 16px\n\n.more-btn\n width: 240px\n margin: 12px auto\n display: block\n\n// Farama Header\n\nhtml:has(.farama-header-menu.active)\n overflow: hidden\n\nbody\n --farama-header-height: 52px\n --farama-header-logo-margin: 10px\n\n.farama-header\n position: absolute\n display: flex\n width: 100%\n height: var(--farama-header-height)\n border-bottom: 1px solid var(--color-header-border)\n background-color: var(--color-background-secondary)\n padding: 0 36px 0 24px\n box-sizing: border-box\n z-index: 95\n\n .farama-header__container\n width: 100%\n max-width: 1400px\n display: flex\n margin: 0 auto\n justify-content: space-between\n\n a\n color: var(--color-foreground-primary)\n text-decoration: none\n transition: color 0.125s ease\n\n &:hover\n color: var(--color-foreground-secondary)\n\n .farama-header__logo\n max-height: calc(var(--farama-header-height) - var(--farama-header-logo-margin))\n margin: var(--farama-header-logo-margin)\n\n .farama-header__title\n font-size: var(--font-size--normal)\n font-weight: normal\n margin: 0 0 2px 0\n padding: 0 0 0 4px\n align-self: center\n\n .farama-header__left\n display: flex\n\n a\n display: flex\n\n .farama-header__left--mobile\n display: none\n\n .nav-overlay-icon svg\n width: 20px\n stroke: var(--color-foreground-primary)\n fill: var(--color-foreground-primary)\n stroke-width: 2px\n padding: 0 6px\n\n .farama-header__right\n display: flex\n align-items: center\n z-index: 2\n\n .farama-header__nav\n display: flex\n list-style: none\n height: 100%\n\n li\n text-decoration: none\n margin-left: 20px\n display: flex\n align-items: center\n cursor: pointer\n\n a\n height: 100%\n display: flex\n align-items: center\n\n .farama-header__dropdown-container\n position: relative\n display: flex\n align-items: center\n height: 100%\n\n &:hover\n .farama-header__dropdown-menu\n display: block\n\n svg\n width: 32px\n fill: var(--color-foreground-primary)\n\n .farama-header__dropdown-menu\n position: absolute\n top: var(--farama-header-height)\n right: 0\n border: 1px solid var(--color-background-border)\n background: var(--color-background-hover)\n z-index: 9999\n display: none\n\n ul\n display: inherit\n margin: 0\n padding: 6px 14px\n\n li\n margin: 0\n padding: 6px 0\n\n .farama-header-menu\n position: relative\n display: flex\n justify-content: center\n\n .farama-header-menu__btn\n display: flex\n background: none\n border: none\n cursor: pointer\n\n img\n width: 26px\n svg\n width: 14px\n stroke: var(--color-foreground-primary)\n stroke-width: 2px\n align-self: center\n\n\n &.active .farama-header-menu-container\n transform: translateY(100vh)\n\n .farama-header-menu-container\n position: fixed\n z-index: 99\n right: 0\n top: -100vh\n width: 100%\n height: calc(100vh - calc(100vh - 100%))\n transform: translateY(0)\n box-sizing: border-box\n transition: transform 0.2s ease-in\n background-color: var(--color-background-secondary)\n border-left: 1px solid var(--color-background-border)\n overflow: auto\n\n .farama-header-menu__header\n position: relative\n width: 100%\n max-width: 1400px\n box-sizing: border-box\n margin: 0 auto\n padding: 7px 52px\n border-bottom: 1px solid var(--color-background-border)\n display: flex\n align-items: center\n\n a\n display: flex\n align-items: center\n\n .farama-header-menu__logo\n width: 36px\n\n span\n color: var(--color-sidebar-brand-text)\n padding-left: 8px\n\n .farama-header-menu-header__right\n position: absolute\n right: 0\n padding-right: inherit\n\n button\n display: flex\n background: none\n border: none\n cursor: pointer\n\n svg\n width: 20px\n color: var(--color-foreground-primary)\n\n .farama-header-menu__body\n display: flex\n width: 100%\n max-width: 1400px\n padding: 22px 52px\n box-sizing: border-box\n margin: 0 auto\n flex-wrap: wrap\n\n .farama-header-menu__section\n min-width: 250px\n margin-bottom: 24px\n\n .farama-header-menu__section-title\n display: block\n font-size: var(--font-size--normal)\n font-weight: bold\n text-transform: uppercase\n margin-bottom: 12px\n\n .farama-header-menu-list\n display: inherit\n margin: 0\n padding: 0\n list-style: none\n\n li\n\n &:hover\n background-color: var(--color-background-hover)\n\n a\n display: flex\n padding: 12px 18px\n align-items: center\n\n &:hover\n color: inherit\n\n img\n width: 24px\n margin-right: 10px\n\n.farama-sidebar__title\n display: flex\n align-items: center\n padding-right: 4px\n min-height: calc(52px - var(--sidebar-search-space-above))\n margin-top: var(--sidebar-search-space-above)\n\n img\n height: calc(var(--farama-header-height) - 20px)\n margin: var(--farama-header-logo-margin)\n span\n color: var(--color-foreground-primary)\n &:hover\n text-decoration: none\n\n.sidebar-brand\n flex-direction: row\n padding: var(--sidebar-item-spacing-vertical)\n align-items: center\n\n .sidebar-logo-container\n display: flex\n max-width: 55px\n height: auto\n\n .sidebar-brand-text\n font-size: 1.3rem\n padding-left: 11px\n\n.mobile-header\n .header-center\n transition: opacity 0.2s easy-in\n opacity: 0\n\n.mobile-header.scrolled\n .header-center\n opacity: 1\n\n// Sphinx Gallery\n\n.sphx-glr-script-out\n color: var(--color-foreground-secondary)\n display: flex\n gap: 0.5em\n\n.sphx-glr-script-out::before\n content: \"Out:\"\n line-height: 1.4\n padding-top: 10px\n\n.sphx-glr-script-out .highlight\n overflow-x: auto\n\n.sphx-glr-thumbcontainer\n z-index: 1\n\ndiv.sphx-glr-download a\n width: 340px\n max-width: 100%\n box-sizing: border-box\n background: #0f4a65\n\ndiv.sphx-glr-download a:hover\n background: #0d3a4e\n box-shadow: none\n\nbody[data-theme=\"dark\"]\n div.sphx-glr-download a\n background: #0f4a65\n div.sphx-glr-download a:hover\n background: #0d3a4e\n\nbody[data-theme=\"light\"]\n div.sphx-glr-download a\n background: #f9d4a1\n div.sphx-glr-download a:hover\n background: #d9b481\n\n.sphx-glr-thumbcontainer img\n background-color: white\n border-radius: 4px\n\n\n@media (max-width: 950px)\n .farama-header .farama-header__right .farama-header-menu .farama-header-menu-container .farama-header-menu__header\n padding: 7px 42px\n\n .farama-header .farama-header-menu__btn-name\n display: none\n\n\n@media (max-width: 600px)\n .farama-header\n padding: 0 4px\n\n .farama-header__title\n font-size: var(--font-size--small)\n\n .farama-header__right .farama-header-menu .farama-header-menu-container\n .farama-header-menu__header\n padding: 8px 12px\n .farama-header-menu__body\n padding: 18px 12px\n\n\n@media (max-width: 480px)\n .farama-header\n .farama-header__title\n width: 110px\n\n .farama-header-menu__btn-name\n text-align: right\n width: 100px\n\n\nbody[data-theme=\"light\"]\n .farama-white-logo-invert\n filter: invert(1)\n\n// Github math render\n\nbody[data-theme=\"dark\"]\n .farama-black-logo-invert\n filter: invert(1)\n\n img[src*=\"//render.githubusercontent.com/render/math\"]\n filter: invert(90%)\n"],"names":[],"sourceRoot":""} \ No newline at end of file diff --git a/_static/versioning/versioning_menu.html b/_static/versioning/versioning_menu.html index dfb99fbee..2ead34cdf 100644 --- a/_static/versioning/versioning_menu.html +++ b/_static/versioning/versioning_menu.html @@ -113,6 +113,15 @@ menuElem.classList.remove("active"); }); + const sortVersions = (a, b) => { + if (a.replace("v", "") > b.replace("v", "")) + return -1 + else if (a.replace("v", "") < b.replace("v", "")) + return 1 + else + return 0 + } + if (githubUser !== null || repo !== null) { const basePath = ""; const mainBranchName = "main" @@ -125,7 +134,7 @@ } response.json().then(json => { const versions = json.tree.filter(v => versionRegex.test(v.path)).map(v => v.path); - versions.reverse(); + versions.sort(sortVersions); // verify if exists a directory with main version if (json.tree.filter(v => v.path == mainBranchName).length > 0) { versions.unshift(mainBranchName); diff --git a/api/env/index.html b/api/env/index.html index 1894b0b65..2207ade4d 100644 --- a/api/env/index.html +++ b/api/env/index.html @@ -15,7 +15,11 @@ Env - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

Tutorials

Development

@@ -766,11 +778,11 @@ For more information see the environment creation tutorial.

const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -941,7 +953,7 @@ For more information see the environment creation tutorial.

githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/api/experimental/functional/index.html b/api/experimental/functional/index.html index af33a1dea..83ea16aa1 100644 --- a/api/experimental/functional/index.html +++ b/api/experimental/functional/index.html @@ -15,7 +15,11 @@ Functional Environment - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

Tutorials

Development

@@ -607,11 +619,11 @@ we intend to flesh it out and officially expose it to end users.

const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -781,7 +793,7 @@ we intend to flesh it out and officially expose it to end users.

githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/api/experimental/index.html b/api/experimental/index.html index 02c473351..df8f444a6 100644 --- a/api/experimental/index.html +++ b/api/experimental/index.html @@ -15,7 +15,11 @@ Experimental - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

Tutorials

Development

@@ -701,11 +713,11 @@ const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -875,7 +887,7 @@ githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/api/experimental/vector/index.html b/api/experimental/vector/index.html index 93b9575ae..79747cc6d 100644 --- a/api/experimental/vector/index.html +++ b/api/experimental/vector/index.html @@ -15,7 +15,11 @@ Vectorizing Environment - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

Tutorials

Development

@@ -518,11 +530,11 @@ const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -692,7 +704,7 @@ githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/api/experimental/vector_wrappers/index.html b/api/experimental/vector_wrappers/index.html index c26b3bcb4..5630bdf5b 100644 --- a/api/experimental/vector_wrappers/index.html +++ b/api/experimental/vector_wrappers/index.html @@ -15,7 +15,11 @@ Vector Environment Wrappers - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

Tutorials

Development

@@ -518,11 +530,11 @@ const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -692,7 +704,7 @@ githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/api/experimental/wrappers/index.html b/api/experimental/wrappers/index.html index eb952f70f..bde1d288d 100644 --- a/api/experimental/wrappers/index.html +++ b/api/experimental/wrappers/index.html @@ -15,7 +15,11 @@ Wrappers - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

Tutorials

Development

@@ -430,11 +442,13 @@ If the observations from func are outside the bounds of the env spaces, provide a observation_space.

Example

>>> import gymnasium as gym
+>>> from gymnasium.experimental.wrappers import LambdaObservationV0
 >>> import numpy as np
->>> env = gym.make('CartPole-v1')
->>> env = LambdaObservationV0(env, lambda obs: obs + 0.1 * np.random.random(obs.shape))
->>> env.reset()
-array([-0.08319338,  0.04635121, -0.07394746,  0.20877492])
+>>> np.random.seed(0)
+>>> env = gym.make("CartPole-v1")
+>>> env = LambdaObservationV0(env, lambda obs: obs + 0.1 * np.random.random(obs.shape), env.observation_space)
+>>> env.reset(seed=42)  
+(array([ 0.06199517,  0.0511615 , -0.04432538,  0.02694618]), {})
 

Constructor for the lambda observation wrapper.

@@ -455,17 +469,18 @@ If the observations from Example

>>> import gymnasium as gym
->>> env = gym.wrappers.TransformObservation(
-...     gym.make('CartPole-v1'), lambda obs: {'obs': obs, 'time': 0}
-... )
+>>> from gymnasium.wrappers import TransformObservation
+>>> from gymnasium.experimental.wrappers import FilterObservationV0
+>>> env = gym.make("CartPole-v1")
+>>> env = gym.wrappers.TransformObservation(env, lambda obs: {'obs': obs, 'time': 0})
 >>> env.observation_space = gym.spaces.Dict(obs=env.observation_space, time=gym.spaces.Discrete(1))
->>> env.reset()
-{'obs': array([-0.00067088, -0.01860439,  0.04772898, -0.01911527], dtype=float32), 'time': 0}
+>>> env.reset(seed=42)
+({'obs': array([ 0.0273956 , -0.00611216,  0.03585979,  0.0197368 ], dtype=float32), 'time': 0}, {})
 >>> env = FilterObservationV0(env, filter_keys=['time'])
->>> env.reset()
-{'obs': array([ 0.04560107,  0.04466959, -0.0328232 , -0.02367178], dtype=float32)}
+>>> env.reset(seed=42)
+({'time': 0}, {})
 >>> env.step(0)
-({'obs': array([ 0.04649447, -0.14996664, -0.03329664,  0.25847703], dtype=float32)}, 1.0, False, {})
+({'time': 0}, 1.0, False, False, {})
 

Constructor for an environment with a dictionary observation space where all filter_keys are in the observation space keys.

@@ -477,13 +492,14 @@ If the observations from Example

>>> import gymnasium as gym
->>> env = gym.make('CarRacing-v1')
+>>> from gymnasium.experimental.wrappers import FlattenObservationV0
+>>> env = gym.make("CarRacing-v2")
 >>> env.observation_space.shape
 (96, 96, 3)
 >>> env = FlattenObservationV0(env)
 >>> env.observation_space.shape
 (27648,)
->>> obs, info = env.reset()
+>>> obs, _ = env.reset()
 >>> obs.shape
 (27648,)
 
@@ -498,7 +514,8 @@ If the observations from keep_dim will keep the channel dimension

Example

>>> import gymnasium as gym
->>> env = gym.make("CarRacing-v1")
+>>> from gymnasium.experimental.wrappers import GrayscaleObservationV0
+>>> env = gym.make("CarRacing-v2")
 >>> env.observation_space.shape
 (96, 96, 3)
 >>> grayscale_env = GrayscaleObservationV0(env)
@@ -518,6 +535,7 @@ If the observations from Example

>>> import gymnasium as gym
+>>> from gymnasium.experimental.wrappers import ResizeObservationV0
 >>> env = gym.make("CarRacing-v2")
 >>> env.observation_space.shape
 (96, 96, 3)
@@ -535,7 +553,8 @@ If the observations from Example

>>> import gymnasium as gym
->>> env = gym.make("CarRacing-v1")
+>>> from gymnasium.experimental.wrappers import ReshapeObservationV0
+>>> env = gym.make("CarRacing-v2")
 >>> env.observation_space.shape
 (96, 96, 3)
 >>> reshape_env = ReshapeObservationV0(env, (24, 4, 96, 1, 3))
@@ -552,11 +571,13 @@ If the observations from Example

>>> import gymnasium as gym
+>>> from gymnasium.experimental.wrappers import RescaleObservationV0
 >>> env = gym.make("Pendulum-v1")
 >>> env.observation_space
 Box([-1. -1. -8.], [1. 1. 8.], (3,), float32)
 >>> env = RescaleObservationV0(env, np.array([-2, -1, -10]), np.array([1, 0, 1]))
-Box([-2. -1. -10.], [1. 0. 1.], (3,), float32)
+>>> env.observation_space
+Box([ -2.  -1. -10.], [1. 0. 1.], (3,), float32)
 

Constructor that requires the env observation spaces to be a Box.

@@ -628,25 +649,27 @@ as the final element in the tuple.

Example

>>> import gymnasium as gym
 >>> from gymnasium.experimental.wrappers import TimeAwareObservationV0
->>> env = gym.make('CartPole-v1')
+>>> env = gym.make("CartPole-v1")
 >>> env = TimeAwareObservationV0(env)
 >>> env.observation_space
-Dict(obs: Box([-4.8000002e+00 -3.4028235e+38 -4.1887903e-01 -3.4028235e+38], [4.8000002e+00 3.4028235e+38 4.1887903e-01 3.4028235e+38], (4,), float32), time: Box(0.0, 500, (1,), float32))
->>> _ = env.reset()
+Dict('obs': Box([-4.8000002e+00 -3.4028235e+38 -4.1887903e-01 -3.4028235e+38], [4.8000002e+00 3.4028235e+38 4.1887903e-01 3.4028235e+38], (4,), float32), 'time': Box(0.0, 1.0, (1,), float32))
+>>> _ = env.reset(seed=42)
+>>> _ = env.action_space.seed(42)
 >>> env.step(env.action_space.sample())[0]
-OrderedDict([('obs',
-...       array([ 0.02866629,  0.2310988 , -0.02614601, -0.2600732 ], dtype=float32)),
-...      ('time', array([0.002]))])
+{'obs': array([ 0.02727336, -0.20172954,  0.03625453,  0.32351476], dtype=float32), 'time': 0.002}
 
-
Flatten observation space example:
>>> env = gym.make('CartPole-v1')
+
Flatten observation space example:
>>> env = gym.make("CartPole-v1")
 >>> env = TimeAwareObservationV0(env, flatten=True)
 >>> env.observation_space
-Box([-4.8000002e+00 -3.4028235e+38 -4.1887903e-01 -3.4028235e+38  0.0000000e+00], [4.8000002e+00 3.4028235e+38 4.1887903e-01 3.4028235e+38 500], (5,), float32)
->>> _ = env.reset()
+Box([-4.8000002e+00 -3.4028235e+38 -4.1887903e-01 -3.4028235e+38
+  0.0000000e+00], [4.8000002e+00 3.4028235e+38 4.1887903e-01 3.4028235e+38 1.0000000e+00], (5,), float32)
+>>> _ = env.reset(seed=42)
+>>> _ = env.action_space.seed(42)
 >>> env.step(env.action_space.sample())[0]
-array([-0.01232257,  0.19335455, -0.02244143, -0.32388705,  0.002 ], dtype=float32)
+array([ 0.02727336, -0.20172954,  0.03625453,  0.32351476,  0.002     ],
+      dtype=float32)
 
@@ -682,11 +705,12 @@ I.e. the observation returned by Example

>>> import gymnasium as gym
->>> env = gym.make('CarRacing-v1')
->>> env = FrameStack(env, 4)
+>>> from gymnasium.experimental.wrappers import FrameStackObservationV0
+>>> env = gym.make("CarRacing-v2")
+>>> env = FrameStackObservationV0(env, 4)
 >>> env.observation_space
-Box(4, 96, 96, 3)
->>> obs = env.reset()
+Box(0, 255, (4, 96, 96, 3), uint8)
+>>> obs, _ = env.reset()
 >>> obs.shape
 (4, 96, 96, 3)
 
@@ -792,13 +816,15 @@ optimization benefits of FrameStack Wrapper.

Clip the continuous action within the valid Box observation space bound.

Example

>>> import gymnasium as gym
+>>> from gymnasium.experimental.wrappers import ClipActionV0
 >>> import numpy as np
->>> env = gym.make('BipedalWalker-v3', disable_env_checker=True)
+>>> env = gym.make("Hopper-v4", disable_env_checker=True)
 >>> env = ClipActionV0(env)
 >>> env.action_space
-Box(-1.0, 1.0, (4,), float32)
->>> env.step(np.array([5.0, 2.0, -10.0, 0.0]))
-# Executes the action np.array([1.0, 1.0, -1.0, 0]) in the base environment
+Box(-inf, inf, (3,), float32)
+>>> _ = env.reset(seed=42)
+>>> _ = env.step(np.array([5.0, -2.0, 0.0]))
+... # Executes the action np.array([1.0, -1.0, 0]) in the base environment
 

A wrapper for clipping continuous actions within the valid bound.

@@ -817,13 +843,14 @@ optimization benefits of FrameStack Wrapper.

or max_action are numpy arrays, the shape must match the shape of the environment’s action space.

Example

>>> import gymnasium as gym
+>>> from gymnasium.experimental.wrappers import RescaleActionV0
 >>> import numpy as np
->>> env = gym.make('BipedalWalker-v3', disable_env_checker=True)
+>>> env = gym.make("Hopper-v4", disable_env_checker=True)
 >>> _ = env.reset(seed=42)
->>> obs, _, _, _, _ = env.step(np.array([1,1,1,1]))
+>>> obs, _, _, _, _ = env.step(np.array([1,1,1]))
 >>> _ = env.reset(seed=42)
 >>> min_action = -0.5
->>> max_action = np.array([0.0, 0.5, 1.0, 0.75])
+>>> max_action = np.array([0.0, 0.5, 0.75])
 >>> wrapped_env = RescaleActionV0(env, min_action=min_action, max_action=max_action)
 >>> wrapped_env_obs, _, _, _, _ = wrapped_env.step(max_action)
 >>> np.alltrue(obs == wrapped_env_obs)
@@ -897,7 +924,7 @@ in Section 5.2 on page 12.

>>> from gymnasium.experimental.wrappers import ClipRewardV0 >>> env = gym.make("CartPole-v1") >>> env = ClipRewardV0(env, 0, 0.5) ->>> env.reset() +>>> _ = env.reset() >>> _, rew, _, _, _ = env.step(1) >>> rew 0.5 @@ -922,6 +949,9 @@ in Section 5.2 on page 12.

class gymnasium.experimental.wrappers.NormalizeRewardV0(env: Env, gamma: float = 0.99, epsilon: float = 1e-8)#

This wrapper will normalize immediate rewards s.t. their exponential moving average has a fixed variance.

The exponential moving average will have variance \((1 - \gamma)^2\).

+

The property _update_running_mean allows to freeze/continue the running mean calculation of the reward +statistics. If True (default), the RunningMeanStd will get updated every time self.normalize() is called. +If False, the calculated statistics are used but not updated anymore; this may be used during evaluation.

Note

The scaling depends on past trajectories and rewards will not be scaled correctly if the wrapper was newly @@ -966,16 +996,17 @@ instantiated or the policy was changed recently.

class gymnasium.experimental.wrappers.OrderEnforcingV0(env: Env, disable_render_order_enforcing: bool = False)#

A wrapper that will produce an error if step() is called before an initial reset().

Example

-
>>> from gymnasium.envs.classic_control import CartPoleEnv
->>> env = CartPoleEnv()
+
>>> import gymnasium as gym
+>>> from gymnasium.experimental.wrappers import OrderEnforcingV0
+>>> env = gym.make("CartPole-v1", render_mode="human")
 >>> env = OrderEnforcingV0(env)
->>> env.step(0)
-ResetNeeded: Cannot call env.step() before calling env.reset()
+>>> env.step(0) 
+gymnasium.error.ResetNeeded: Cannot call env.step() before calling env.reset()
+>>> env.render() 
+gymnasium.error.ResetNeeded('Cannot call `env.render()` before calling `env.reset()`, if this is a intended action, set `disable_render_order_enforcing=True` on the OrderEnforcer wrapper.')
+>>> _ = env.reset()
 >>> env.render()
-ResetNeeded: Cannot call env.render() before calling env.reset()
->>> env.reset()
->>> env.render()
->>> env.step(0)
+>>> _ = env.step(0)
 

A wrapper that will produce an error if step() is called before an initial reset().

@@ -999,7 +1030,6 @@ using the key episo the episode statistics.

After the completion of an episode, info will look like this:

>>> info = {
-...     ...
 ...     "episode": {
 ...         "r": "<cumulative reward>",
 ...         "l": "<episode length>",
@@ -1010,7 +1040,10 @@ the episode statistics.

For a vectorized environments the output will be in the form of:

>>> infos = {
-...     ...
+...     "final_observation": "<array of length num-envs>",
+...     "_final_observation": "<boolean array of length num-envs>",
+...     "final_info": "<array of length num-envs>",
+...     "_final_info": "<boolean array of length num-envs>",
 ...     "episode": {
 ...         "r": "<array of cumulative reward>",
 ...         "l": "<array of episode length>",
@@ -1047,12 +1080,32 @@ the episode statistics.

Rendering Wrappers#

-class gymnasium.experimental.wrappers.RecordVideoV0(env: Env[ObsType, ActType])#
-

Record a video of an environment.

-

Wraps an environment to allow a modular transformation of the step() and reset() methods.

+class gymnasium.experimental.wrappers.RecordVideoV0(env: Env, video_folder: str, episode_trigger: Optional[Callable[[int], bool]] = None, step_trigger: Optional[Callable[[int], bool]] = None, video_length: int = 0, name_prefix: str = 'rl-video', disable_logger: bool = False)# +

This wrapper records videos of rollouts.

+

Usually, you only want to record episodes intermittently, say every hundredth episode. +To do this, you can specify episode_trigger or step_trigger. +They should be functions returning a boolean that indicates whether a recording should be started at the +current episode or step, respectively. +If neither episode_trigger nor step_trigger is passed, a default episode_trigger will be employed, +i.e. capped_cubic_video_schedule. This function starts a video at every episode that is a power of 3 until 1000 and +then every 1000 episodes. +By default, the recording will be stopped once reset is called. However, you can also create recordings of fixed +length (possibly spanning several episodes) by passing a strictly positive value for video_length. +This wrapper uses the value fps from metadata as the number of frames per second; +if fps is not defined in metadata, the default value 30 is used.

+

Wrapper records videos of rollouts.

Parameters:
-

env – The environment to wrap

+
    +
  • env – The environment that will be wrapped

  • +
  • video_folder (str) – The folder where the recordings will be stored

  • +
  • episode_trigger – Function that accepts an integer and returns True iff a recording should be started at this episode

  • +
  • step_trigger – Function that accepts an integer and returns True iff a recording should be started at this step

  • +
  • video_length (int) – The length of recorded episodes. If 0, entire episodes are recorded. +Otherwise, snippets of the specified length are captured

  • +
  • name_prefix (str) – Will be prepended to the filename of the recordings

  • +
  • disable_logger (bool) – Whether to disable moviepy logger or not

  • +
@@ -1067,26 +1120,28 @@ If you want to use this wrapper with your environments, remember to specify

The render_mode of the wrapped environment must be either 'rgb_array' or 'rgb_array_list'.

Example

-
>>> env = gym.make("LunarLander-v2", render_mode="rgb_array")
+
>>> import gymnasium as gym
+>>> from gymnasium.experimental.wrappers import HumanRenderingV0
+>>> env = gym.make("LunarLander-v2", render_mode="rgb_array")
 >>> wrapped = HumanRenderingV0(env)
->>> wrapped.reset()     # This will start rendering to the screen
+>>> obs, _ = wrapped.reset()     # This will start rendering to the screen
 

The wrapper can also be applied directly when the environment is instantiated, simply by passing render_mode="human" to make. The wrapper will only be applied if the environment does not implement human-rendering natively (i.e. render_mode does not contain "human").

Example

-
>>> env = gym.make("NoNativeRendering-v2", render_mode="human")      # NoNativeRendering-v0 doesn't implement human-rendering natively
->>> env.reset()     # This will start rendering to the screen
+
>>> env = gym.make("CartPoleJax-v1", render_mode="human")      # CartPoleJax-v1 doesn't implement human-rendering natively
+>>> obs, _ = env.reset()     # This will start rendering to the screen
 
Warning: If the base environment uses render_mode="rgb_array_list", its (i.e. the base environment’s) render method

will always return an empty list:

>>> env = gym.make("LunarLander-v2", render_mode="rgb_array_list")
 >>> wrapped = HumanRenderingV0(env)
->>> wrapped.reset()
->>> env.render()
-[]          # env.render() will always return an empty list!
+>>> obs, _ = wrapped.reset()
+>>> env.render() # env.render() will always return an empty list!
+[]
 
@@ -1302,11 +1357,11 @@ The reason for this is jax does not support non-array values, therefore numpy { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/api/registry/index.html b/api/registry/index.html index c62da8626..f069d6683 100644 --- a/api/registry/index.html +++ b/api/registry/index.html @@ -15,7 +15,11 @@ Registry - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

Tutorials

Development

@@ -641,11 +653,11 @@ instead of printing it to console.

const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -815,7 +827,7 @@ instead of printing it to console.

githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/api/spaces/composite/index.html b/api/spaces/composite/index.html index 6bb66213c..b38c45ce6 100644 --- a/api/spaces/composite/index.html +++ b/api/spaces/composite/index.html @@ -15,7 +15,11 @@ Composite Spaces - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

Tutorials

Development

@@ -429,14 +441,14 @@

Elements of this space are (ordered) dictionaries of elements from the constituent spaces.

Example usage:

>>> from gymnasium.spaces import Dict, Discrete
->>> observation_space = Dict({"position": Discrete(2), "velocity": Discrete(3)})
+>>> observation_space = Dict({"position": Discrete(2), "velocity": Discrete(3)}, seed=42)
 >>> observation_space.sample()
-OrderedDict([('position', 1), ('velocity', 2)])
+OrderedDict([('position', 0), ('velocity', 2)])
 

Example usage [nested]:

>>> from gymnasium.spaces import Box, Dict, Discrete, MultiBinary, MultiDiscrete
->>> Dict(
+>>> Dict(  
 ...     {
 ...         "ext_controller": MultiDiscrete([5, 2, 2]),
 ...         "inner_state": Dict(
@@ -466,9 +478,9 @@ keyword arguments (where you will need to avoid the keys 
>>> from gymnasium.spaces import Box, Discrete
 >>> Dict({"position": Box(-1, 1, shape=(2,)), "color": Discrete(3)})
-Dict(color:Discrete(3), position:Box(-1.0, 1.0, (2,), float32))
+Dict('color': Discrete(3), 'position': Box(-1.0, 1.0, (2,), float32))
 >>> Dict(position=Box(-1, 1, shape=(2,)), color=Discrete(3))
-Dict(color:Discrete(3), position:Box(-1.0, 1.0, (2,), float32))
+Dict('position': Box(-1.0, 1.0, (2,), float32), 'color': Discrete(3))
 
@@ -524,9 +536,9 @@ keyword arguments (where you will need to avoid the keys
>>> from gymnasium.spaces import Box, Discrete
->>> observation_space = Tuple((Discrete(2), Box(-1, 1, shape=(2,))))
+>>> observation_space = Tuple((Discrete(2), Box(-1, 1, shape=(2,))), seed=42)
 >>> observation_space.sample()
-(0, array([0.03633198, 0.42370757], dtype=float32))
+(0, array([-0.3991573 ,  0.21649833], dtype=float32))
 

Constructor of Tuple space.

@@ -585,11 +597,11 @@ expects the same number of masks as spaces

to some space that is specified during initialization and the integer \(n\) is not fixed

Example::
>>> from gymnasium.spaces import Box
->>> space = Sequence(Box(0, 1))
->>> space.sample()
-(array([0.0259352], dtype=float32),)
->>> space.sample()
-(array([0.80977976], dtype=float32), array([0.80066574], dtype=float32), array([0.77165383], dtype=float32))
+>>> space = Sequence(Box(0, 1), seed=42)
+>>> space.sample()   
+(array([0.6369617], dtype=float32),)
+>>> space.sample()   
+(array([0.01652764], dtype=float32), array([0.8132702], dtype=float32),)
 
@@ -799,11 +811,11 @@ If no num_edges is provided then the edge_mask is mult const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -974,7 +986,7 @@ If no num_edges is provided then the edge_mask is mult githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/api/spaces/fundamental/index.html b/api/spaces/fundamental/index.html index 2838f95d6..02ac86a79 100644 --- a/api/spaces/fundamental/index.html +++ b/api/spaces/fundamental/index.html @@ -15,7 +15,11 @@ Fundamental Spaces - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

Tutorials

Development

@@ -433,13 +445,13 @@ Each interval has the form of one of
  • Identical bound for each dimension:

    >>> Box(low=-1.0, high=2.0, shape=(3, 4), dtype=np.float32)
    -Box(3, 4)
    +Box(-1.0, 2.0, (3, 4), float32)
     
  • Independent bound for each dimension:

    >>> Box(low=np.array([-1.0, -2.0]), high=np.array([2.0, 4.0]), dtype=np.float32)
    -Box(2,)
    +Box([-1. -2.], [2. 4.], (2,), float32)
     
  • @@ -522,7 +534,9 @@ that is chosen according to the form of the interval:

    This class represents a finite subset of integers, more specifically a set of the form \(\{ a, a+1, \dots, a+n-1 \}\).

    Example:

    >>> Discrete(2)            # {0, 1}
    +Discrete(2)
     >>> Discrete(3, start=-1)  # {-1, 0, 1}
    +Discrete(3, start=-1)
     

    Constructor of Discrete space.

    @@ -570,14 +584,14 @@ If there are no possible actions (i.e. np.all(mask == 0)) then

    An n-shape binary space.

    Elements of this space are binary arrays of a shape that is fixed during construction.

    Example Usage:

    -
    >>> observation_space = MultiBinary(5)
    +
    >>> observation_space = MultiBinary(5, seed=42)
     >>> observation_space.sample()
    -    array([0, 1, 0, 1, 0], dtype=int8)
    ->>> observation_space = MultiBinary([3, 2])
    +array([1, 0, 1, 0, 1], dtype=int8)
    +>>> observation_space = MultiBinary([3, 2], seed=42)
     >>> observation_space.sample()
    -    array([[0, 0],
    -        [0, 1],
    -        [1, 1]], dtype=int8)
    +array([[1, 0],
    +       [1, 0],
    +       [1, 1]], dtype=int8)
     

    Constructor of MultiBinary space.

    @@ -637,7 +651,7 @@ The expected mask shape is the space shape and mask dtype is np.int8Although this feature is rarely used, MultiDiscrete spaces may also have several axes if nvec has several axes:

    Example:

    -
    >> d = MultiDiscrete(np.array([[1, 2], [3, 4]]))
    +
    >> d = MultiDiscrete(np.array([[1, 2], [3, 4]]), seed=42)
     >> d.sample()
     array([[0, 0],
            [2, 3]])
    @@ -689,11 +703,13 @@ Only mask values == 1 are possible to sample unless all mask values for an actio
     
    Example::
    >>> # {"", "B5", "hello", ...}
     >>> Text(5)
    +Text(1, 5, characters=0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz)
     >>> # {"0", "42", "0123456789", ...}
     >>> import string
     >>> Text(min_length = 1,
     ...      max_length = 10,
     ...      charset = string.digits)
    +Text(1, 10, characters=0123456789)
     
    @@ -855,11 +871,11 @@ If the charlist mask is all zero then an empty string is returned no matter the const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -1030,7 +1046,7 @@ If the charlist mask is all zero then an empty string is returned no matter the githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/api/spaces/index.html b/api/spaces/index.html index c98b21b9a..9429f62be 100644 --- a/api/spaces/index.html +++ b/api/spaces/index.html @@ -15,7 +15,11 @@ Spaces - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

Tutorials

Development

@@ -683,11 +695,11 @@ Therefore, Gymnasium provides a number of additional functions used when using a const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -857,7 +869,7 @@ Therefore, Gymnasium provides a number of additional functions used when using a githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/api/spaces/utils/index.html b/api/spaces/utils/index.html index 77d7235ad..75a627850 100644 --- a/api/spaces/utils/index.html +++ b/api/spaces/utils/index.html @@ -15,7 +15,11 @@ Spaces Utils - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

Tutorials

Development

@@ -461,9 +473,9 @@ errors or non-uniform sampling.

Example::
>>> from gymnasium.spaces import Box
 >>> box = Box(0.0, 1.0, shape=(3, 4, 5))
 >>> box
-Box(3, 4, 5)
+Box(0.0, 1.0, (3, 4, 5), float32)
 >>> flatten_space(box)
-Box(60,)
+Box(0.0, 1.0, (60,), float32)
 >>> flatten(box, box.sample()) in flatten_space(box)
 True
 
@@ -472,7 +484,7 @@ errors or non-uniform sampling.

Example that flattens a discrete space::
>>> from gymnasium.spaces import Discrete
 >>> discrete = Discrete(5)
 >>> flatten_space(discrete)
-Box(5,)
+Box(0, 1, (5,), int64)
 >>> flatten(box, box.sample()) in flatten_space(box)
 True
 
@@ -481,7 +493,7 @@ errors or non-uniform sampling.

Example that recursively flattens a dict::
>>> from gymnasium.spaces import Dict, Discrete, Box
 >>> space = Dict({"position": Discrete(2), "velocity": Box(0, 1, shape=(2, 2))})
 >>> flatten_space(space)
-Box(6,)
+Box(0.0, 1.0, (6,), float64)
 >>> flatten(space, space.sample()) in flatten_space(space)
 True
 
@@ -758,11 +770,11 @@ that the spacespace { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/api/spaces/vector_utils/index.html b/api/spaces/vector_utils/index.html index 5e5d0e04b..23dd4aa4b 100644 --- a/api/spaces/vector_utils/index.html +++ b/api/spaces/vector_utils/index.html @@ -15,7 +15,11 @@ Spaces Vector Utils - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

Tutorials

Development

@@ -440,12 +452,13 @@

Create a (batched) space, containing multiple copies of a single space.

Example:

>>> from gymnasium.spaces import Box, Dict
+>>> import numpy as np
 >>> space = Dict({
 ...     'position': Box(low=0, high=1, shape=(3,), dtype=np.float32),
 ...     'velocity': Box(low=0, high=1, shape=(2,), dtype=np.float32)
 ... })
 >>> batch_space(space, n=5)
-Dict(position:Box(5, 3), velocity:Box(5, 2))
+Dict('position': Box(0.0, 1.0, (5, 3), float32), 'velocity': Box(0.0, 1.0, (5, 2), float32))
 
@@ -484,12 +497,13 @@

Concatenate multiple samples from space into a single object.

Example:

>>> from gymnasium.spaces import Box
->>> space = Box(low=0, high=1, shape=(3,), dtype=np.float32)
+>>> import numpy as np
+>>> space = Box(low=0, high=1, shape=(3,), seed=42, dtype=np.float32)
 >>> out = np.zeros((2, 3), dtype=np.float32)
 >>> items = [space.sample() for _ in range(2)]
 >>> concatenate(space, items, out)
-array([[0.6348213 , 0.28607962, 0.60760117],
-       [0.87383074, 0.192658  , 0.2148103 ]], dtype=float32)
+array([[0.77395606, 0.43887845, 0.85859793],
+       [0.697368  , 0.09417735, 0.97562236]], dtype=float32)
 
@@ -529,18 +543,17 @@

Iterate over the elements of a (batched) space.

Example:

>>> from gymnasium.spaces import Box, Dict
+>>> import numpy as np
 >>> space = Dict({
-... 'position': Box(low=0, high=1, shape=(2, 3), dtype=np.float32),
-... 'velocity': Box(low=0, high=1, shape=(2, 2), dtype=np.float32)})
+... 'position': Box(low=0, high=1, shape=(2, 3), seed=42, dtype=np.float32),
+... 'velocity': Box(low=0, high=1, shape=(2, 2), seed=42, dtype=np.float32)})
 >>> items = space.sample()
 >>> it = iterate(space, items)
 >>> next(it)
-{'position': array([-0.99644893, -0.08304597, -0.7238421 ], dtype=float32),
-'velocity': array([0.35848552, 0.1533453 ], dtype=float32)}
->>> next(it)
-{'position': array([-0.67958736, -0.49076623,  0.38661423], dtype=float32),
-'velocity': array([0.7975036 , 0.93317133], dtype=float32)}
+OrderedDict([('position', array([0.77395606, 0.43887845, 0.85859793], dtype=float32)), ('velocity', array([0.77395606, 0.43887845], dtype=float32))])
 >>> next(it)
+OrderedDict([('position', array([0.697368  , 0.09417735, 0.97562236], dtype=float32)), ('velocity', array([0.85859793, 0.697368  ], dtype=float32))])
+>>> next(it) 
 StopIteration
 
@@ -564,7 +577,7 @@

Shared Memory Utils#

-gymnasium.vector.utils.create_empty_array(space: Space, n: int = 1, fn: callable = np.zeros) Union[tuple, dict, ndarray]#
+gymnasium.vector.utils.create_empty_array(space: Space, n: int = 1, fn: Callable[[...], ndarray] = np.zeros) Union[tuple, dict, ndarray]#
gymnasium.vector.utils.create_empty_array(space: ~gymnasium.spaces.multi_binary.MultiBinary, n=1, fn=<built-in function zeros>)
@@ -582,14 +595,14 @@

Create an empty (possibly nested) numpy array.

Example:

>>> from gymnasium.spaces import Box, Dict
+>>> import numpy as np
 >>> space = Dict({
 ... 'position': Box(low=0, high=1, shape=(3,), dtype=np.float32),
 ... 'velocity': Box(low=0, high=1, shape=(2,), dtype=np.float32)})
 >>> create_empty_array(space, n=2, fn=np.zeros)
 OrderedDict([('position', array([[0., 0., 0.],
-                                 [0., 0., 0.]], dtype=float32)),
-             ('velocity', array([[0., 0.],
-                                 [0., 0.]], dtype=float32))])
+       [0., 0., 0.]], dtype=float32)), ('velocity', array([[0., 0.],
+       [0., 0.]], dtype=float32))])
 
@@ -613,17 +626,17 @@
gymnasium.vector.utils.create_shared_memory(space: Space, n: int = 1, ctx=mp) Union[dict, tuple, Array]#
-gymnasium.vector.utils.create_shared_memory(space: ~gymnasium.spaces.multi_binary.MultiBinary, n: int = 1, ctx=<module 'multiprocessing' from '/opt/hostedtoolcache/Python/3.9.15/x64/lib/python3.9/multiprocessing/__init__.py'>)
+gymnasium.vector.utils.create_shared_memory(space: ~gymnasium.spaces.multi_binary.MultiBinary, n: int = 1, ctx=<module 'multiprocessing' from '/opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/multiprocessing/__init__.py'>)
-gymnasium.vector.utils.create_shared_memory(space: ~gymnasium.spaces.multi_discrete.MultiDiscrete, n: int = 1, ctx=<module 'multiprocessing' from '/opt/hostedtoolcache/Python/3.9.15/x64/lib/python3.9/multiprocessing/__init__.py'>)
+gymnasium.vector.utils.create_shared_memory(space: ~gymnasium.spaces.multi_discrete.MultiDiscrete, n: int = 1, ctx=<module 'multiprocessing' from '/opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/multiprocessing/__init__.py'>)
-gymnasium.vector.utils.create_shared_memory(space: ~gymnasium.spaces.discrete.Discrete, n: int = 1, ctx=<module 'multiprocessing' from '/opt/hostedtoolcache/Python/3.9.15/x64/lib/python3.9/multiprocessing/__init__.py'>)
+gymnasium.vector.utils.create_shared_memory(space: ~gymnasium.spaces.discrete.Discrete, n: int = 1, ctx=<module 'multiprocessing' from '/opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/multiprocessing/__init__.py'>)
-gymnasium.vector.utils.create_shared_memory(space: ~gymnasium.spaces.box.Box, n: int = 1, ctx=<module 'multiprocessing' from '/opt/hostedtoolcache/Python/3.9.15/x64/lib/python3.9/multiprocessing/__init__.py'>)
+gymnasium.vector.utils.create_shared_memory(space: ~gymnasium.spaces.box.Box, n: int = 1, ctx=<module 'multiprocessing' from '/opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/multiprocessing/__init__.py'>)
-gymnasium.vector.utils.create_shared_memory(space: ~gymnasium.spaces.tuple.Tuple, n: int = 1, ctx=<module 'multiprocessing' from '/opt/hostedtoolcache/Python/3.9.15/x64/lib/python3.9/multiprocessing/__init__.py'>)
+gymnasium.vector.utils.create_shared_memory(space: ~gymnasium.spaces.tuple.Tuple, n: int = 1, ctx=<module 'multiprocessing' from '/opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/multiprocessing/__init__.py'>)
-gymnasium.vector.utils.create_shared_memory(space: ~gymnasium.spaces.dict.Dict, n=1, ctx=<module 'multiprocessing' from '/opt/hostedtoolcache/Python/3.9.15/x64/lib/python3.9/multiprocessing/__init__.py'>)
+gymnasium.vector.utils.create_shared_memory(space: ~gymnasium.spaces.dict.Dict, n=1, ctx=<module 'multiprocessing' from '/opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/multiprocessing/__init__.py'>)

Create a shared memory object, to be shared across processes.

This eventually contains the observations from the vectorized environment.

@@ -804,11 +817,11 @@ This object is created with create_shared_memory.

const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -978,7 +991,7 @@ This object is created with create_shared_memory.

githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/api/utils/index.html b/api/utils/index.html index 39f43d109..c9124c4ab 100644 --- a/api/utils/index.html +++ b/api/utils/index.html @@ -15,7 +15,11 @@ Utils - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

Tutorials

Development

@@ -429,7 +441,7 @@

Example:

>>> import gymnasium as gym
 >>> from gymnasium.utils.play import play
->>> play(gym.make("CarRacing-v1", render_mode="rgb_array"), keys_to_action={
+>>> play(gym.make("CarRacing-v2", render_mode="rgb_array"), keys_to_action={  
 ...                                                "w": np.array([0, 0.7, 0]),
 ...                                                "a": np.array([-1, 0, 0]),
 ...                                                "s": np.array([0, 0, 1]),
@@ -448,10 +460,11 @@ unplayable.

gym.utils.play.PlayPlot. Here’s a sample code for plotting the reward for last 150 steps.

>>> import gymnasium as gym
+>>> from gymnasium.utils.play import PlayPlot, play
 >>> def callback(obs_t, obs_tp1, action, rew, terminated, truncated, info):
 ...        return [rew,]
->>> plotter = PlayPlot(callback, 150, ["reward"])
->>> play(gym.make("CartPole-v1"), callback=plotter.callback)
+>>> plotter = PlayPlot(callback, 150, ["reward"])             
+>>> play(gym.make("CartPole-v1"), callback=plotter.callback)  
 
@@ -476,7 +489,7 @@ points of the keys, as a tuple of characters, or as a string where each characte one key. For example if pressing ‘w’ and space at the same time is supposed to trigger action number 2 then key_to_action dict could look like this:

-
>>> {
+
>>> key_to_action = {
 ...    # ...
 ...    (ord('w'), ord(' ')): 2
 ...    # ...
@@ -484,7 +497,7 @@ to trigger action number 2 then 

or like this:

-
>>> {
+
>>> key_to_action = {
 ...    # ...
 ...    ("w", " "): 2
 ...    # ...
@@ -492,7 +505,7 @@ to trigger action number 2 then 

or like this:

-
>>> {
+
>>> key_to_action = {
 ...    # ...
 ...    "w ": 2
 ...    # ...
@@ -510,7 +523,7 @@ to trigger action number 2 then 
 
-class gymnasium.utils.play.PlayPlot(callback: callable, horizon_timesteps: int, plot_names: List[str])#
+class gymnasium.utils.play.PlayPlot(callback: Callable, horizon_timesteps: int, plot_names: List[str])#

Provides a callback to create live plots of arbitrary metrics when using play().

This class is instantiated with a function that accepts information about a single environment transition:
    @@ -533,9 +546,9 @@ For instance, the function may look like this:

    PlayPlot provides the method callback() which will pass its arguments along to that function and uses the returned values to update live plots of the metrics.

    Typically, this callback() will be used in conjunction with play() to see how the metrics evolve as you play:

    -
    >>> plotter = PlayPlot(compute_metrics, horizon_timesteps=200,
    +
    >>> plotter = PlayPlot(compute_metrics, horizon_timesteps=200,                               
     ...                    plot_names=["Immediate Rew.", "Cumulative Rew.", "Action Magnitude"])
    ->>> play(your_env, callback=plotter.callback)
    +>>> play(your_env, callback=plotter.callback)                                                
     

    Constructor of PlayPlot.

    @@ -632,10 +645,10 @@ You need to specify either fps or duration.

    >>> import gymnasium as gym
     >>> from gymnasium.utils.save_video import save_video
     >>> env = gym.make("FrozenLake-v1", render_mode="rgb_array_list")
    ->>> env.reset()
    +>>> _ = env.reset()
     >>> step_starting_index = 0
     >>> episode_index = 0
    ->>> for step_index in range(199):
    +>>> for step_index in range(199): 
     ...    action = env.action_space.sample()
     ...    _, _, terminated, truncated, _ = env.step(action)
     ...
    @@ -698,13 +711,16 @@ Terminated Truncated (new) step API refers to step() method returning (observati
     
>>> import gymnasium as gym
->>> env = gym.make("OldEnv")
->>> obs, rew, done, info = step_api_compatibility(env.step(action), output_truncation_bool=False)
->>> obs, rew, terminated, truncated, info = step_api_compatibility(env.step(action), output_truncation_bool=True)
+>>> env = gym.make("CartPole-v0")
+>>> _ = env.reset()
+>>> obs, rewards, done, info = step_api_compatibility(env.step(0), output_truncation_bool=False)
+>>> obs, rewards, terminated, truncated, info = step_api_compatibility(env.step(0), output_truncation_bool=True)
 
-
>>> vec_env = gym.vector.make("OldEnv")
->>> observations, rewards, dones, infos = step_api_compatibility(vec_env.step(action), is_vector_env=True)
+
>>> vec_env = gym.vector.make("CartPole-v0")
+>>> _ = vec_env.reset()
+>>> obs, rewards, dones, infos = step_api_compatibility(vec_env.step([0]), is_vector_env=True, output_truncation_bool=False)
+>>> obs, rewards, terminated, truncated, info = step_api_compatibility(vec_env.step([0]), is_vector_env=True, output_truncation_bool=True)
 
@@ -889,11 +905,11 @@ for more information about the API.

const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -1064,7 +1080,7 @@ for more information about the API.

githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/api/vector/index.html b/api/vector/index.html index 758106bb8..33f7a3390 100644 --- a/api/vector/index.html +++ b/api/vector/index.html @@ -15,7 +15,11 @@ Vector - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

Tutorials

Development

@@ -491,10 +503,10 @@ In other words, a vector of multiple different environments is not supported.

An example:

>>> import gymnasium as gym
 >>> envs = gym.vector.make("CartPole-v1", num_envs=3)
->>> envs.reset()
-(array([[-0.02240574, -0.03439831, -0.03904812,  0.02810693],
-       [ 0.01586068,  0.01929009,  0.02394426,  0.04016077],
-       [-0.01314174,  0.03893502, -0.02400815,  0.0038326 ]],
+>>> envs.reset(seed=42)
+(array([[ 0.0273956 , -0.00611216,  0.03585979,  0.0197368 ],
+       [ 0.01522993, -0.04562247, -0.04799704,  0.03392126],
+       [-0.03774345, -0.02418869, -0.00942293,  0.0469184 ]],
       dtype=float32), {})
 
@@ -520,15 +532,14 @@ info as “final_observation” and “final_info”.<

An example:

>>> envs = gym.vector.make("CartPole-v1", num_envs=3)
->>> envs.reset()
+>>> _ = envs.reset(seed=42)
 >>> actions = np.array([1, 0, 1])
 >>> observations, rewards, termination, truncation, infos = envs.step(actions)
-
 >>> observations
-array([[ 0.00122802,  0.16228443,  0.02521779, -0.23700266],
-        [ 0.00788269, -0.17490888,  0.03393489,  0.31735462],
-        [ 0.04918966,  0.19421194,  0.02938497, -0.29495203]],
-        dtype=float32)
+array([[ 0.02727336,  0.18847767,  0.03625453, -0.26141977],
+       [ 0.01431748, -0.24002443, -0.04731862,  0.3110827 ],
+       [-0.03822722,  0.1710671 , -0.00848456, -0.2487226 ]],
+      dtype=float32)
 >>> rewards
 array([1., 1., 1.])
 >>> termination
@@ -617,16 +628,16 @@ vectorized environments.

Making Vector Environments#

-gymnasium.vector.make(id: str, num_envs: int = 1, asynchronous: bool = True, wrappers: Optional[Union[callable, List[callable]]] = None, disable_env_checker: Optional[bool] = None, **kwargs) VectorEnv#
+gymnasium.vector.make(id: str, num_envs: int = 1, asynchronous: bool = True, wrappers: Optional[Union[Callable[[Env], Env], List[Callable[[Env], Env]]]] = None, disable_env_checker: Optional[bool] = None, **kwargs) VectorEnv#

Create a vectorized environment from multiple copies of an environment, from its id.

Example:

>>> import gymnasium as gym
 >>> env = gym.vector.make('CartPole-v1', num_envs=3)
->>> env.reset()
-array([[-0.04456399,  0.04653909,  0.01326909, -0.02099827],
-       [ 0.03073904,  0.00145001, -0.03088818, -0.03131252],
-       [ 0.03468829,  0.01500225,  0.01230312,  0.01825218]],
-      dtype=float32)
+>>> env.reset(seed=42)
+(array([[ 0.0273956 , -0.00611216,  0.03585979,  0.0197368 ],
+       [ 0.01522993, -0.04562247, -0.04799704,  0.03392126],
+       [-0.03774345, -0.02418869, -0.00942293,  0.0469184 ]],
+      dtype=float32), {})
 
@@ -652,18 +663,18 @@ vectorized environments.

Async Vector Env#

-class gymnasium.vector.AsyncVectorEnv(env_fns: Sequence[callable], observation_space: Optional[Space] = None, action_space: Optional[Space] = None, shared_memory: bool = True, copy: bool = True, context: Optional[str] = None, daemon: bool = True, worker: Optional[callable] = None)#
+class gymnasium.vector.AsyncVectorEnv(env_fns: Sequence[Callable[[], Env]], observation_space: Optional[Space] = None, action_space: Optional[Space] = None, shared_memory: bool = True, copy: bool = True, context: Optional[str] = None, daemon: bool = True, worker: Optional[Callable] = None)#

Vectorized environment that runs multiple environments in parallel.

It uses multiprocessing processes, and pipes for communication.

Example:

>>> import gymnasium as gym
 >>> env = gym.vector.AsyncVectorEnv([
-...     lambda: gym.make("Pendulum-v0", g=9.81),
-...     lambda: gym.make("Pendulum-v0", g=1.62)
+...     lambda: gym.make("Pendulum-v1", g=9.81),
+...     lambda: gym.make("Pendulum-v1", g=1.62)
 ... ])
->>> env.reset()
-array([[-0.8286432 ,  0.5597771 ,  0.90249056],
-       [-0.85009176,  0.5266346 ,  0.60007906]], dtype=float32)
+>>> env.reset(seed=42)
+(array([[-0.14995256,  0.9886932 , -0.12224312],
+       [ 0.5760367 ,  0.8174238 , -0.91244936]], dtype=float32), {})
 

Vectorized environment that runs multiple environments in parallel.

@@ -715,12 +726,12 @@ from the code for _

Example:

>>> import gymnasium as gym
 >>> env = gym.vector.SyncVectorEnv([
-...     lambda: gym.make("Pendulum-v0", g=9.81),
-...     lambda: gym.make("Pendulum-v0", g=1.62)
+...     lambda: gym.make("Pendulum-v1", g=9.81),
+...     lambda: gym.make("Pendulum-v1", g=1.62)
 ... ])
->>> env.reset()
-array([[-0.8286432 ,  0.5597771 ,  0.90249056],
-       [-0.85009176,  0.5266346 ,  0.60007906]], dtype=float32)
+>>> env.reset(seed=42)
+(array([[-0.14995256,  0.9886932 , -0.12224312],
+       [ 0.5760367 ,  0.8174238 , -0.91244936]], dtype=float32), {})
 

Vectorized environment that serially runs multiple environments.

@@ -850,11 +861,11 @@ then the action space of the first environment is taken.

const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -1024,7 +1035,7 @@ then the action space of the first environment is taken.

githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/api/wrappers/action_wrappers/index.html b/api/wrappers/action_wrappers/index.html index 7f02640a8..06809924f 100644 --- a/api/wrappers/action_wrappers/index.html +++ b/api/wrappers/action_wrappers/index.html @@ -15,7 +15,11 @@ Action Wrappers - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

Tutorials

Development

@@ -459,12 +471,14 @@ the

Clip the continuous action within the valid Box observation space bound.

Example

>>> import gymnasium as gym
->>> env = gym.make('Bipedal-Walker-v3')
+>>> from gymnasium.wrappers import ClipAction
+>>> env = gym.make("Hopper-v4")
 >>> env = ClipAction(env)
 >>> env.action_space
-Box(-1.0, 1.0, (4,), float32)
->>> env.step(np.array([5.0, 2.0, -10.0, 0.0]))
-# Executes the action np.array([1.0, 1.0, -1.0, 0]) in the base environment
+Box(-1.0, 1.0, (3,), float32)
+>>> _ = env.reset(seed=42)
+>>> _ = env.step(np.array([5.0, -2.0, 0.0]))
+... # Executes the action np.array([1.0, -1.0, 0]) in the base environment
 

A wrapper for clipping continuous actions within the valid bound.

@@ -483,15 +497,17 @@ the max_action are numpy arrays, the shape must match the shape of the environment’s action space.

Example

>>> import gymnasium as gym
->>> env = gym.make('BipedalWalker-v3')
->>> env.action_space
-Box(-1.0, 1.0, (4,), float32)
+>>> from gymnasium.wrappers import RescaleAction
+>>> import numpy as np
+>>> env = gym.make("Hopper-v4")
+>>> _ = env.reset(seed=42)
+>>> obs, _, _, _, _ = env.step(np.array([1,1,1]))
+>>> _ = env.reset(seed=42)
 >>> min_action = -0.5
->>> max_action = np.array([0.0, 0.5, 1.0, 0.75])
->>> env = RescaleAction(env, min_action=min_action, max_action=max_action)
->>> env.action_space
-Box(-0.5, [0.   0.5  1.   0.75], (4,), float32)
->>> RescaleAction(env, min_action, max_action).action_space == gym.spaces.Box(min_action, max_action)
+>>> max_action = np.array([0.0, 0.5, 0.75])
+>>> wrapped_env = RescaleAction(env, min_action=min_action, max_action=max_action)
+>>> wrapped_env_obs, _, _, _, _ = wrapped_env.step(max_action)
+>>> np.alltrue(obs == wrapped_env_obs)
 True
 
@@ -598,11 +614,11 @@ or const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -772,7 +788,7 @@ or githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/api/wrappers/index.html b/api/wrappers/index.html index b78498b79..391ff9e56 100644 --- a/api/wrappers/index.html +++ b/api/wrappers/index.html @@ -15,7 +15,11 @@ Wrappers - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

Tutorials

Development

@@ -431,29 +443,29 @@ Most environments that are generated via
>>> import gymnasium as gym
 >>> from gymnasium.wrappers import RescaleAction
->>> base_env = gym.make("BipedalWalker-v3")
+>>> base_env = gym.make("Hopper-v4")
 >>> base_env.action_space
-Box([-1. -1. -1. -1.], [1. 1. 1. 1.], (4,), float32)
+Box(-1.0, 1.0, (3,), float32)
 >>> wrapped_env = RescaleAction(base_env, min_action=0, max_action=1)
 >>> wrapped_env.action_space
-Box([0. 0. 0. 0.], [1. 1. 1. 1.], (4,), float32)
+Box(-1.0, 1.0, (3,), float32)
 

You can access the environment underneath the first wrapper by using the gymnasium.Wrapper.env attribute. As the gymnasium.Wrapper class inherits from gymnasium.Env then gymnasium.Wrapper.env can be another wrapper.

>>> wrapped_env
-<RescaleAction<TimeLimit<OrderEnforcing<BipedalWalker<BipedalWalker-v3>>>>>
+<RescaleAction<TimeLimit<OrderEnforcing<PassiveEnvChecker<HopperEnv<Hopper-v4>>>>>>
 >>> wrapped_env.env
-<TimeLimit<OrderEnforcing<BipedalWalker<BipedalWalker-v3>>>>
+<TimeLimit<OrderEnforcing<PassiveEnvChecker<HopperEnv<Hopper-v4>>>>>
 

If you want to get to the environment underneath all of the layers of wrappers, you can use the gymnasium.Wrapper.unwrapped attribute. If the environment is already a bare environment, the gymnasium.Wrapper.unwrapped attribute will just return itself.

>>> wrapped_env
-<RescaleAction<TimeLimit<OrderEnforcing<BipedalWalker<BipedalWalker-v3>>>>>
->>> wrapped_env.unwrapped
-<gymnasium.envs.box2d.bipedal_walker.BipedalWalker object at 0x7f87d70712d0>
+<RescaleAction<TimeLimit<OrderEnforcing<PassiveEnvChecker<HopperEnv<Hopper-v4>>>>>>
+>>> wrapped_env.unwrapped 
+<gymnasium.envs.mujoco.hopper_v4.HopperEnv object at 0x7fbb5efd0490>
 

There are three common things you might want a wrapper to do:

@@ -779,11 +791,11 @@ wrapper in the page on the wrapper type

const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -953,7 +965,7 @@ wrapper in the page on the wrapper type

githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/api/wrappers/misc_wrappers/index.html b/api/wrappers/misc_wrappers/index.html index 4dfbe3e12..c71c59960 100644 --- a/api/wrappers/misc_wrappers/index.html +++ b/api/wrappers/misc_wrappers/index.html @@ -15,7 +15,11 @@ Misc Wrappers - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

Tutorials

Development

@@ -538,12 +550,14 @@ New step API refers to step() method returning (observation, reward, terminated,

Examples

-
>>> env = gym.make("CartPole-v1")
+
>>> import gymnasium as gym
+>>> from gymnasium.wrappers import StepAPICompatibility
+>>> env = gym.make("CartPole-v1")
 >>> env # wrapper not applied by default, set to new API
 <TimeLimit<OrderEnforcing<PassiveEnvChecker<CartPoleEnv<CartPole-v1>>>>>
->>> env = gym.make("CartPole-v1", apply_api_compatibility=True) # set to old API
+>>> env = StepAPICompatibility(gym.make("CartPole-v1"))
+>>> env
 <StepAPICompatibility<TimeLimit<OrderEnforcing<PassiveEnvChecker<CartPoleEnv<CartPole-v1>>>>>>
->>> env = StepAPICompatibility(CustomEnv(), output_truncation_bool=False) # manually using wrapper on unregistered envs
 

A wrapper which can transform an environment from new step API to old and vice-versa.

@@ -574,26 +588,28 @@ If you want to use this wrapper with your environments, remember to specify

The render_mode of the wrapped environment must be either 'rgb_array' or 'rgb_array_list'.

Example

-
>>> env = gym.make("LunarLander-v2", render_mode="rgb_array")
+
>>> import gymnasium as gym
+>>> from gymnasium.wrappers import HumanRendering
+>>> env = gym.make("LunarLander-v2", render_mode="rgb_array")
 >>> wrapped = HumanRendering(env)
->>> wrapped.reset()     # This will start rendering to the screen
+>>> obs, _ = wrapped.reset()     # This will start rendering to the screen
 

The wrapper can also be applied directly when the environment is instantiated, simply by passing render_mode="human" to make. The wrapper will only be applied if the environment does not implement human-rendering natively (i.e. render_mode does not contain "human").

Example

-
>>> env = gym.make("NoNativeRendering-v2", render_mode="human")      # NoNativeRendering-v0 doesn't implement human-rendering natively
->>> env.reset()     # This will start rendering to the screen
+
>>> env = gym.make("CartPoleJax-v1", render_mode="human")      # CartPoleJax-v1 doesn't implement human-rendering natively
+>>> obs, _ = env.reset()     # This will start rendering to the screen
 
Warning: If the base environment uses render_mode="rgb_array_list", its (i.e. the base environment’s) render method

will always return an empty list:

>>> env = gym.make("LunarLander-v2", render_mode="rgb_array_list")
 >>> wrapped = HumanRendering(env)
->>> wrapped.reset()
->>> env.render()
-[]          # env.render() will always return an empty list!
+>>> obs, _ = wrapped.reset()
+>>> env.render()     # env.render() will always return an empty list!
+[]
 
@@ -611,16 +627,17 @@ implement human-rendering natively (i.e. class gymnasium.wrappers.OrderEnforcing(env: Env, disable_render_order_enforcing: bool = False)#

A wrapper that will produce an error if step() is called before an initial reset().

Example

-
>>> from gymnasium.envs.classic_control import CartPoleEnv
->>> env = CartPoleEnv()
+
>>> import gymnasium as gym
+>>> from gymnasium.wrappers import OrderEnforcing
+>>> env = gym.make("CartPole-v1", render_mode="human")
 >>> env = OrderEnforcing(env)
->>> env.step(0)
-ResetNeeded: Cannot call env.step() before calling env.reset()
+>>> env.step(0) 
+gymnasium.error.ResetNeeded: Cannot call env.step() before calling env.reset()
+>>> env.render() 
+gymnasium.error.ResetNeeded('Cannot call `env.render()` before calling `env.reset()`, if this is a intended action, set `disable_render_order_enforcing=True` on the OrderEnforcer wrapper.')
+>>> _ = env.reset()
 >>> env.render()
-ResetNeeded: Cannot call env.render() before calling env.reset()
->>> env.reset()
->>> env.render()
->>> env.step(0)
+>>> _ = env.step(0)
 

A wrapper that will produce an error if step() is called before an initial reset().

@@ -644,7 +661,6 @@ using the key episo the episode statistics.

After the completion of an episode, info will look like this:

>>> info = {
-...     ...
 ...     "episode": {
 ...         "r": "<cumulative reward>",
 ...         "l": "<episode length>",
@@ -655,7 +671,10 @@ the episode statistics.

For a vectorized environments the output will be in the form of:

>>> infos = {
-...     ...
+...     "final_observation": "<array of length num-envs>",
+...     "_final_observation": "<boolean array of length num-envs>",
+...     "final_info": "<array of length num-envs>",
+...     "_final_info": "<boolean array of length num-envs>",
 ...     "episode": {
 ...         "r": "<array of cumulative reward>",
 ...         "l": "<array of episode length>",
@@ -740,9 +759,9 @@ Otherwise, snippets of the specified length are captured

If a truncation is not defined inside the environment itself, this is the only place that the truncation signal is issued. Critically, this is different from the terminated signal that originates from the underlying environment as part of the MDP.

Example

-
>>> from gymnasium.envs.classic_control import CartPoleEnv
+
>>> import gymnasium as gym
 >>> from gymnasium.wrappers import TimeLimit
->>> env = CartPoleEnv()
+>>> env = gym.make("CartPole-v1")
 >>> env = TimeLimit(env, max_episode_steps=1000)
 
@@ -768,16 +787,33 @@ environments. If using other wrappers that perform operation on info like RecordEpisodeStatistics this need to be the outermost wrapper.

i.e. VectorListInfo(RecordEpisodeStatistics(envs))

-

Example:

-
>>> # actual
->>> {
-...      "k": np.array[0., 0., 0.5, 0.3],
-...      "_k": np.array[False, False, True, True]
-...  }
->>> # classic
->>> [{}, {}, {k: 0.5}, {k: 0.3}]
+
+
Example::
>>> # As dict:
+>>> infos = {
+...     "final_observation": "<array of length num-envs>",
+...     "_final_observation": "<boolean array of length num-envs>",
+...     "final_info": "<array of length num-envs>",
+...     "_final_info": "<boolean array of length num-envs>",
+...     "episode": {
+...         "r": "<array of cumulative reward>",
+...         "l": "<array of episode length>",
+...         "t": "<array of elapsed time since beginning of episode>"
+...     },
+...     "_episode": "<boolean array of length num-envs>"
+... }
+>>> # As list:
+>>> infos = [
+...     {
+...         "episode": {"r": "<cumulative reward>", "l": "<episode length>", "t": "<elapsed time since beginning of episode>"},
+...         "final_observation": "<observation>",
+...         "final_info": {},
+...     },
+...     ...,
+... ]
 
+
+

This wrapper will convert the info into the list format.

Parameters:
@@ -876,11 +912,11 @@ need to be the outermost wrapper.

const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -1050,7 +1086,7 @@ need to be the outermost wrapper.

githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/api/wrappers/observation_wrappers/index.html b/api/wrappers/observation_wrappers/index.html index e0ae23d96..b8074a71d 100644 --- a/api/wrappers/observation_wrappers/index.html +++ b/api/wrappers/observation_wrappers/index.html @@ -15,7 +15,11 @@ Observation Wrappers - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

Tutorials

Development

@@ -461,11 +473,13 @@ index of the timestep to the observation.

If the transformation you wish to apply to observations returns values in a different space, you should subclass ObservationWrapper, implement the transformation, and set the new observation space accordingly. If you were to use this wrapper instead, the observation space would be set incorrectly.

Example

>>> import gymnasium as gym
+>>> from gymnasium.wrappers import TransformObservation
 >>> import numpy as np
->>> env = gym.make('CartPole-v1')
->>> env = TransformObservation(env, lambda obs: obs + 0.1*np.random.randn(*obs.shape))
->>> env.reset()
-array([-0.08319338,  0.04635121, -0.07394746,  0.20877492])
+>>> np.random.seed(0)
+>>> env = gym.make("CartPole-v1")
+>>> env = TransformObservation(env, lambda obs: obs + 0.1 * np.random.randn(*obs.shape))
+>>> env.reset(seed=42)
+(array([0.20380084, 0.03390356, 0.13373359, 0.24382612]), {})
 

Initialize the TransformObservation wrapper with an environment and a transform function f.

@@ -485,17 +499,17 @@ index of the timestep to the observation.

Filter Dict observation space by the keys.

Example

>>> import gymnasium as gym
->>> env = gym.wrappers.TransformObservation(
-...     gym.make('CartPole-v1'), lambda obs: {'obs': obs, 'time': 0}
-... )
+>>> from gymnasium.wrappers import TransformObservation
+>>> env = gym.make("CartPole-v1")
+>>> env = TransformObservation(env, lambda obs: {'obs': obs, 'time': 0})
 >>> env.observation_space = gym.spaces.Dict(obs=env.observation_space, time=gym.spaces.Discrete(1))
->>> env.reset()
-{'obs': array([-0.00067088, -0.01860439,  0.04772898, -0.01911527], dtype=float32), 'time': 0}
+>>> env.reset(seed=42)
+({'obs': array([ 0.0273956 , -0.00611216,  0.03585979,  0.0197368 ], dtype=float32), 'time': 0}, {})
 >>> env = FilterObservation(env, filter_keys=['obs'])
->>> env.reset()
-{'obs': array([ 0.04560107,  0.04466959, -0.0328232 , -0.02367178], dtype=float32)}
+>>> env.reset(seed=42)
+({'obs': array([ 0.0273956 , -0.00611216,  0.03585979,  0.0197368 ], dtype=float32)}, {})
 >>> env.step(0)
-({'obs': array([ 0.04649447, -0.14996664, -0.03329664,  0.25847703], dtype=float32)}, 1.0, False, {})
+({'obs': array([ 0.02727336, -0.20172954,  0.03625453,  0.32351476], dtype=float32)}, 1.0, False, False, {})
 

A wrapper that filters dictionary observations by their keys.

@@ -521,13 +535,14 @@ index of the timestep to the observation.

Observation wrapper that flattens the observation.

Example

>>> import gymnasium as gym
->>> env = gym.make('CarRacing-v1')
+>>> from gymnasium.wrappers import FlattenObservation
+>>> env = gym.make("CarRacing-v2")
 >>> env.observation_space.shape
 (96, 96, 3)
 >>> env = FlattenObservation(env)
 >>> env.observation_space.shape
 (27648,)
->>> obs, info = env.reset()
+>>> obs, _ = env.reset()
 >>> obs.shape
 (27648,)
 
@@ -560,11 +575,12 @@ I.e. the observation returned by Example

>>> import gymnasium as gym
->>> env = gym.make('CarRacing-v1')
+>>> from gymnasium.wrappers import FrameStack
+>>> env = gym.make("CarRacing-v2")
 >>> env = FrameStack(env, 4)
 >>> env.observation_space
-Box(4, 96, 96, 3)
->>> obs = env.reset()
+Box(0, 255, (4, 96, 96, 3), uint8)
+>>> obs, _ = env.reset()
 >>> obs.shape
 (4, 96, 96, 3)
 
@@ -586,13 +602,15 @@ I.e. the observation returned by class gymnasium.wrappers.GrayScaleObservation(env: Env, keep_dim: bool = False)#

Convert the image observation from RGB to gray scale.

Example

-
>>> env = gym.make('CarRacing-v1')
+
>>> import gymnasium as gym
+>>> from gymnasium.wrappers import GrayScaleObservation
+>>> env = gym.make("CarRacing-v2")
 >>> env.observation_space
 Box(0, 255, (96, 96, 3), uint8)
->>> env = GrayScaleObservation(gym.make('CarRacing-v1'))
+>>> env = GrayScaleObservation(gym.make("CarRacing-v2"))
 >>> env.observation_space
 Box(0, 255, (96, 96), uint8)
->>> env = GrayScaleObservation(gym.make('CarRacing-v1'), keep_dim=True)
+>>> env = GrayScaleObservation(gym.make("CarRacing-v2"), keep_dim=True)
 >>> env.observation_space
 Box(0, 255, (96, 96, 1), uint8)
 
@@ -641,22 +659,23 @@ space is of type

Example

>>> import gymnasium as gym
->>> env = PixelObservationWrapper(gym.make('CarRacing-v1', render_mode="rgb_array"))
->>> obs = env.reset()
+>>> from gymnasium.wrappers import PixelObservationWrapper
+>>> env = PixelObservationWrapper(gym.make("CarRacing-v2", render_mode="rgb_array"))
+>>> obs, _ = env.reset()
 >>> obs.keys()
 odict_keys(['pixels'])
 >>> obs['pixels'].shape
 (400, 600, 3)
->>> env = PixelObservationWrapper(gym.make('CarRacing-v1', render_mode="rgb_array"), pixels_only=False)
->>> obs = env.reset()
+>>> env = PixelObservationWrapper(gym.make("CarRacing-v2", render_mode="rgb_array"), pixels_only=False)
+>>> obs, _ = env.reset()
 >>> obs.keys()
 odict_keys(['state', 'pixels'])
 >>> obs['state'].shape
 (96, 96, 3)
 >>> obs['pixels'].shape
 (400, 600, 3)
->>> env = PixelObservationWrapper(gym.make('CarRacing-v1', render_mode="rgb_array"), pixel_keys=('obs',))
->>> obs = env.reset()
+>>> env = PixelObservationWrapper(gym.make("CarRacing-v2", render_mode="rgb_array"), pixel_keys=('obs',))
+>>> obs, _ = env.reset()
 >>> obs.keys()
 odict_keys(['obs'])
 >>> obs['obs'].shape
@@ -696,14 +715,18 @@ Defaults to (pixels,).

-class gymnasium.wrappers.ResizeObservation(env: Env, shape: Union[tuple, int])#
+class gymnasium.wrappers.ResizeObservation(env: gym.Env, shape: tuple[int, int] | int)#

Resize the image observation.

-

This wrapper works on environments with image observations (or more generally observations of shape AxBxC) and resizes -the observation to the shape given by the 2-tuple shape. The argument shape may also be an integer. -In that case, the observation is scaled to a square of side-length shape.

+

This wrapper works on environments with image observations. More generally, +the input can either be two-dimensional (AxB, e.g. grayscale images) or +three-dimensional (AxBxC, e.g. color images). This resizes the observation +to the shape given by the 2-tuple shape. +The argument shape may also be an integer, in which case, the +observation is scaled to a square of side-length shape.

Example

>>> import gymnasium as gym
->>> env = gym.make('CarRacing-v1')
+>>> from gymnasium.wrappers import ResizeObservation
+>>> env = gym.make("CarRacing-v2")
 >>> env.observation_space.shape
 (96, 96, 3)
 >>> env = ResizeObservation(env, 64)
@@ -730,12 +753,14 @@ In that case, the observation is scaled to a square of side-length Example

>>> import gymnasium as gym
->>> env = gym.make('CartPole-v1')
+>>> from gymnasium.wrappers import TimeAwareObservation
+>>> env = gym.make("CartPole-v1")
 >>> env = TimeAwareObservation(env)
->>> env.reset()
-array([ 0.03810719,  0.03522411,  0.02231044, -0.01088205,  0.        ])
+>>> env.reset(seed=42)
+(array([ 0.0273956 , -0.00611216,  0.03585979,  0.0197368 ,  0.        ]), {})
+>>> _ = env.action_space.seed(42)
 >>> env.step(env.action_space.sample())[0]
-array([ 0.03881167, -0.16021058,  0.0220928 ,  0.28875574,  1.        ])
+array([ 0.02727336, -0.20172954,  0.03625453,  0.32351476,  1.        ])
 

Initialize TimeAwareObservation that requires an environment with a flat Box observation space.

@@ -844,11 +869,11 @@ In particular, pixel observations are not supported. This wrapper will append th const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -1018,7 +1043,7 @@ In particular, pixel observations are not supported. This wrapper will append th githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/api/wrappers/reward_wrappers/index.html b/api/wrappers/reward_wrappers/index.html index 3bf5cde0c..35dced2df 100644 --- a/api/wrappers/reward_wrappers/index.html +++ b/api/wrappers/reward_wrappers/index.html @@ -15,7 +15,11 @@ Reward Wrappers - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

Tutorials

Development

@@ -461,9 +473,10 @@ you can simply define

Example

>>> import gymnasium as gym
->>> env = gym.make('CartPole-v1')
+>>> from gymnasium.wrappers import TransformReward
+>>> env = gym.make("CartPole-v1")
 >>> env = TransformReward(env, lambda r: 0.01*r)
->>> env.reset()
+>>> _ = env.reset()
 >>> observation, reward, terminated, truncated, info = env.step(env.action_space.sample())
 >>> reward
 0.01
@@ -593,11 +606,11 @@ instantiated or the policy was changed recently.

const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -768,7 +781,7 @@ instantiated or the policy was changed recently.

githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/content/basic_usage/index.html b/content/basic_usage/index.html index 214d6ff1f..1a05e0f40 100644 --- a/content/basic_usage/index.html +++ b/content/basic_usage/index.html @@ -15,7 +15,11 @@ Basic Usage - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

Tutorials

Development

@@ -513,8 +525,8 @@ Values can be shifted to {a, a+1, …, a+n-1} using an optional argument.

More information#

@@ -594,11 +606,11 @@ Values can be shifted to {a, a+1, …, a+n-1} using an optional argument.

{ + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/content/gym_compatibility/index.html b/content/gym_compatibility/index.html index 77d9a4c27..49dbcd349 100644 --- a/content/gym_compatibility/index.html +++ b/content/gym_compatibility/index.html @@ -15,7 +15,11 @@ Compatibility with Gym - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

Tutorials

Development

@@ -527,11 +539,11 @@ To allow backward compatibility, Gym and Gymnasium v0.26+ include an { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/content/migration-guide/index.html b/content/migration-guide/index.html index 418fe5455..4f777549b 100644 --- a/content/migration-guide/index.html +++ b/content/migration-guide/index.html @@ -15,7 +15,11 @@ v21 to v26 Migration Guide - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

Tutorials

Development

@@ -492,6 +504,11 @@ However, environments that have reasons for episode truncation rather than termi For users looping through an environment, they should modify done = terminated or truncated as is show in the example code. For training libraries, the primary difference is to change done to terminated, indicating whether bootstrapping should or shouldn’t happen.

+
+

TimeLimit Wrapper#

+

In v21, the TimeLimit wrapper added an extra key in the info dictionary TimeLimit.truncated whenever the agent reached the time limit without reaching a terminal state.

+

In v26, this information is instead communicated through the truncated return value described in the previous section, which is True if the agent reaches the time limit, whether or not it reaches a terminal state. The old dictionary entry is equivalent to truncated and not terminated

+

Environment Render#

In v26, a new render API was introduced such that the render mode is fixed at initialisation as some environments don’t allow on-the-fly render mode changes. Therefore, users should now specify the render_mode within gym.make as shown in the v26 example code above.

@@ -572,6 +589,7 @@ For training libraries, the primary difference is to change Seed and random number generator
  • Environment Reset
  • Environment Step
  • +
  • TimeLimit Wrapper
  • Environment Render
  • Removed code
  • @@ -591,11 +609,11 @@ For training libraries, the primary difference is to change Adventure - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -592,11 +604,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -766,7 +778,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/air_raid/index.html b/environments/atari/air_raid/index.html index 2e69ab58f..7ed43cf97 100644 --- a/environments/atari/air_raid/index.html +++ b/environments/atari/air_raid/index.html @@ -15,7 +15,11 @@ Air Raid - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -632,11 +644,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -806,7 +818,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/alien/index.html b/environments/atari/alien/index.html index d78476ee4..3f322dd53 100644 --- a/environments/atari/alien/index.html +++ b/environments/atari/alien/index.html @@ -15,7 +15,11 @@ Alien - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -612,11 +624,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -786,7 +798,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/amidar/index.html b/environments/atari/amidar/index.html index 7a0f8fac4..64bdc4587 100644 --- a/environments/atari/amidar/index.html +++ b/environments/atari/amidar/index.html @@ -15,7 +15,11 @@ Amidar - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -653,11 +665,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -827,7 +839,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/assault/index.html b/environments/atari/assault/index.html index 728728068..69aaa87d2 100644 --- a/environments/atari/assault/index.html +++ b/environments/atari/assault/index.html @@ -15,7 +15,11 @@ Assault - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -633,11 +645,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -807,7 +819,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/asterix/index.html b/environments/atari/asterix/index.html index 452819929..f33432a1f 100644 --- a/environments/atari/asterix/index.html +++ b/environments/atari/asterix/index.html @@ -15,7 +15,11 @@ Asterix - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -650,11 +662,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -824,7 +836,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/asteroids/index.html b/environments/atari/asteroids/index.html index 75d3c4210..783f96e1e 100644 --- a/environments/atari/asteroids/index.html +++ b/environments/atari/asteroids/index.html @@ -15,7 +15,11 @@ Asteroids - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -667,11 +679,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -841,7 +853,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/atlantis/index.html b/environments/atari/atlantis/index.html index 0e3df5932..09fb2e0fe 100644 --- a/environments/atari/atlantis/index.html +++ b/environments/atari/atlantis/index.html @@ -15,7 +15,11 @@ Atlantis - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -638,11 +650,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -812,7 +824,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/bank_heist/index.html b/environments/atari/bank_heist/index.html index 3963bf6ff..1022e140a 100644 --- a/environments/atari/bank_heist/index.html +++ b/environments/atari/bank_heist/index.html @@ -15,7 +15,11 @@ Bank Heist - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -614,11 +626,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -788,7 +800,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/battle_zone/index.html b/environments/atari/battle_zone/index.html index c3ac87f52..544d07874 100644 --- a/environments/atari/battle_zone/index.html +++ b/environments/atari/battle_zone/index.html @@ -15,7 +15,11 @@ Battle Zone - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -611,11 +623,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -785,7 +797,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/beam_rider/index.html b/environments/atari/beam_rider/index.html index b6e8e6bfa..529308404 100644 --- a/environments/atari/beam_rider/index.html +++ b/environments/atari/beam_rider/index.html @@ -15,7 +15,11 @@ Beam Rider - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -650,11 +662,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -824,7 +836,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/berzerk/index.html b/environments/atari/berzerk/index.html index 18948c2b3..74ea02234 100644 --- a/environments/atari/berzerk/index.html +++ b/environments/atari/berzerk/index.html @@ -15,7 +15,11 @@ Berzerk - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -610,11 +622,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -784,7 +796,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/bowling/index.html b/environments/atari/bowling/index.html index 78bce969b..f78a6dafe 100644 --- a/environments/atari/bowling/index.html +++ b/environments/atari/bowling/index.html @@ -15,7 +15,11 @@ Bowling - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -644,11 +656,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -818,7 +830,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/boxing/index.html b/environments/atari/boxing/index.html index b0d369342..113eb0812 100644 --- a/environments/atari/boxing/index.html +++ b/environments/atari/boxing/index.html @@ -15,7 +15,11 @@ Boxing - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -610,11 +622,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -784,7 +796,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/breakout/index.html b/environments/atari/breakout/index.html index df92537dd..3a09d244d 100644 --- a/environments/atari/breakout/index.html +++ b/environments/atari/breakout/index.html @@ -15,7 +15,11 @@ Breakout - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -636,11 +648,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -810,7 +822,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/carnival/index.html b/environments/atari/carnival/index.html index 5b8e2d486..76b4295ab 100644 --- a/environments/atari/carnival/index.html +++ b/environments/atari/carnival/index.html @@ -15,7 +15,11 @@ Carnival - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -643,11 +655,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -817,7 +829,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/centipede/index.html b/environments/atari/centipede/index.html index f37818f7c..691356ab9 100644 --- a/environments/atari/centipede/index.html +++ b/environments/atari/centipede/index.html @@ -15,7 +15,11 @@ Centipede - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -613,11 +625,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -787,7 +799,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/chopper_command/index.html b/environments/atari/chopper_command/index.html index d9d8cc4a1..0cc5b9e97 100644 --- a/environments/atari/chopper_command/index.html +++ b/environments/atari/chopper_command/index.html @@ -15,7 +15,11 @@ Chopper Command - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -611,11 +623,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -785,7 +797,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/complete_list/index.html b/environments/atari/complete_list/index.html index 2153d8083..202ca5acb 100644 --- a/environments/atari/complete_list/index.html +++ b/environments/atari/complete_list/index.html @@ -15,7 +15,11 @@ Complete List - Atari - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -1211,11 +1223,11 @@ const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -1385,7 +1397,7 @@ githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/crazy_climber/index.html b/environments/atari/crazy_climber/index.html index 03363217f..59de612f4 100644 --- a/environments/atari/crazy_climber/index.html +++ b/environments/atari/crazy_climber/index.html @@ -15,7 +15,11 @@ Crazy Climber - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -652,11 +664,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -826,7 +838,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/defender/index.html b/environments/atari/defender/index.html index 8f767c00e..fb92f9f4f 100644 --- a/environments/atari/defender/index.html +++ b/environments/atari/defender/index.html @@ -15,7 +15,11 @@ Defender - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -612,11 +624,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -786,7 +798,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/demon_attack/index.html b/environments/atari/demon_attack/index.html index 1a044cd98..8e67d7730 100644 --- a/environments/atari/demon_attack/index.html +++ b/environments/atari/demon_attack/index.html @@ -15,7 +15,11 @@ Demon Attack - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -654,11 +666,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -828,7 +840,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/double_dunk/index.html b/environments/atari/double_dunk/index.html index 28dd04c41..47364f0fe 100644 --- a/environments/atari/double_dunk/index.html +++ b/environments/atari/double_dunk/index.html @@ -15,7 +15,11 @@ Double Dunk - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -652,11 +664,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -826,7 +838,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/elevator_action/index.html b/environments/atari/elevator_action/index.html index 741857bb9..142d17a31 100644 --- a/environments/atari/elevator_action/index.html +++ b/environments/atari/elevator_action/index.html @@ -15,7 +15,11 @@ Elevator Action - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -655,11 +667,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -829,7 +841,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/enduro/index.html b/environments/atari/enduro/index.html index e39bec3b9..f4c426750 100644 --- a/environments/atari/enduro/index.html +++ b/environments/atari/enduro/index.html @@ -15,7 +15,11 @@ Enduro - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -650,11 +662,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -824,7 +836,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/fishing_derby/index.html b/environments/atari/fishing_derby/index.html index 607b98562..5431907c3 100644 --- a/environments/atari/fishing_derby/index.html +++ b/environments/atari/fishing_derby/index.html @@ -15,7 +15,11 @@ FishingDerby - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -703,11 +715,11 @@ customize the environment using the arguments above, if necessary.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -877,7 +889,7 @@ customize the environment using the arguments above, if necessary.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/freeway/index.html b/environments/atari/freeway/index.html index 38edd9273..6c203fa8a 100644 --- a/environments/atari/freeway/index.html +++ b/environments/atari/freeway/index.html @@ -15,7 +15,11 @@ Freeway - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -703,11 +715,11 @@ customize the environment using the arguments above, if necessary.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -877,7 +889,7 @@ customize the environment using the arguments above, if necessary.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/frostbite/index.html b/environments/atari/frostbite/index.html index aa211f3d3..d15a72f91 100644 --- a/environments/atari/frostbite/index.html +++ b/environments/atari/frostbite/index.html @@ -15,7 +15,11 @@ Frostbite - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -703,11 +715,11 @@ customize the environment using the arguments above, if necessary.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -877,7 +889,7 @@ customize the environment using the arguments above, if necessary.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/gopher/index.html b/environments/atari/gopher/index.html index 9a7ae6a06..348ab2db7 100644 --- a/environments/atari/gopher/index.html +++ b/environments/atari/gopher/index.html @@ -15,7 +15,11 @@ Gopher - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -703,11 +715,11 @@ customize the environment using the arguments above, if necessary.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -877,7 +889,7 @@ customize the environment using the arguments above, if necessary.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/gravitar/index.html b/environments/atari/gravitar/index.html index 56f592233..ea9aced03 100644 --- a/environments/atari/gravitar/index.html +++ b/environments/atari/gravitar/index.html @@ -15,7 +15,11 @@ Gravitar - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -703,11 +715,11 @@ customize the environment using the arguments above, if necessary.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -877,7 +889,7 @@ customize the environment using the arguments above, if necessary.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/hero/index.html b/environments/atari/hero/index.html index 8a2fee7e2..bfb4f8a0a 100644 --- a/environments/atari/hero/index.html +++ b/environments/atari/hero/index.html @@ -15,7 +15,11 @@ Hero - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -613,11 +625,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -787,7 +799,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/ice_hockey/index.html b/environments/atari/ice_hockey/index.html index 5a72980aa..fdc825587 100644 --- a/environments/atari/ice_hockey/index.html +++ b/environments/atari/ice_hockey/index.html @@ -15,7 +15,11 @@ IceHockey - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -612,11 +624,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -786,7 +798,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/index.html b/environments/atari/index.html index 6ce752915..3f600c6e1 100644 --- a/environments/atari/index.html +++ b/environments/atari/index.html @@ -15,7 +15,11 @@ Atari - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -1140,11 +1152,11 @@ URL: https://jair.org/index.php/jair/article/view/11182

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -1314,7 +1326,7 @@ URL: https://jair.org/index.php/jair/article/view/11182

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/jamesbond/index.html b/environments/atari/jamesbond/index.html index 582fe5f2d..83470fe38 100644 --- a/environments/atari/jamesbond/index.html +++ b/environments/atari/jamesbond/index.html @@ -15,7 +15,11 @@ Jamesbond - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -613,11 +625,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -787,7 +799,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/journey_escape/index.html b/environments/atari/journey_escape/index.html index 18ec5e3b2..db2d97cc7 100644 --- a/environments/atari/journey_escape/index.html +++ b/environments/atari/journey_escape/index.html @@ -15,7 +15,11 @@ JourneyEscape - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -672,11 +684,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -846,7 +858,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/kangaroo/index.html b/environments/atari/kangaroo/index.html index 716ccad0a..b8f169294 100644 --- a/environments/atari/kangaroo/index.html +++ b/environments/atari/kangaroo/index.html @@ -15,7 +15,11 @@ Kangaroo - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -611,11 +623,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -785,7 +797,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/krull/index.html b/environments/atari/krull/index.html index a92274062..8a7403981 100644 --- a/environments/atari/krull/index.html +++ b/environments/atari/krull/index.html @@ -15,7 +15,11 @@ Krull - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -611,11 +623,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -785,7 +797,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/kung_fu_master/index.html b/environments/atari/kung_fu_master/index.html index 69cddc078..415d45ce0 100644 --- a/environments/atari/kung_fu_master/index.html +++ b/environments/atari/kung_fu_master/index.html @@ -15,7 +15,11 @@ Kung Fu Master - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -649,11 +661,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -823,7 +835,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/montezuma_revenge/index.html b/environments/atari/montezuma_revenge/index.html index c7a7be906..2fd6867d7 100644 --- a/environments/atari/montezuma_revenge/index.html +++ b/environments/atari/montezuma_revenge/index.html @@ -15,7 +15,11 @@ Montezuma Revenge - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -598,11 +610,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -772,7 +784,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/ms_pacman/index.html b/environments/atari/ms_pacman/index.html index 37ce8c6ef..6c4e2f9fe 100644 --- a/environments/atari/ms_pacman/index.html +++ b/environments/atari/ms_pacman/index.html @@ -15,7 +15,11 @@ Ms Pacman - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -634,11 +646,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -808,7 +820,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/name_this_game/index.html b/environments/atari/name_this_game/index.html index 16d739fd9..dc1b5c1fe 100644 --- a/environments/atari/name_this_game/index.html +++ b/environments/atari/name_this_game/index.html @@ -15,7 +15,11 @@ Name This Game - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -625,11 +637,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -799,7 +811,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/phoenix/index.html b/environments/atari/phoenix/index.html index ebe7a6264..496ce4154 100644 --- a/environments/atari/phoenix/index.html +++ b/environments/atari/phoenix/index.html @@ -15,7 +15,11 @@ Phoenix - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -631,11 +643,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -805,7 +817,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/pitfall/index.html b/environments/atari/pitfall/index.html index 957d47749..234169051 100644 --- a/environments/atari/pitfall/index.html +++ b/environments/atari/pitfall/index.html @@ -15,7 +15,11 @@ Pitfall - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -607,11 +619,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -781,7 +793,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/pong/index.html b/environments/atari/pong/index.html index ad01b873c..a97423476 100644 --- a/environments/atari/pong/index.html +++ b/environments/atari/pong/index.html @@ -15,7 +15,11 @@ Pong - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -638,11 +650,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -812,7 +824,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/pooyan/index.html b/environments/atari/pooyan/index.html index b9ed9518c..e24a0f773 100644 --- a/environments/atari/pooyan/index.html +++ b/environments/atari/pooyan/index.html @@ -15,7 +15,11 @@ Pooyan - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -638,11 +650,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -812,7 +824,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/private_eye/index.html b/environments/atari/private_eye/index.html index 411a7d747..24b05ff74 100644 --- a/environments/atari/private_eye/index.html +++ b/environments/atari/private_eye/index.html @@ -15,7 +15,11 @@ PrivateEye - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -607,11 +619,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -781,7 +793,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/qbert/index.html b/environments/atari/qbert/index.html index 390d95bc2..c890be281 100644 --- a/environments/atari/qbert/index.html +++ b/environments/atari/qbert/index.html @@ -15,7 +15,11 @@ Qbert - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -638,11 +650,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -812,7 +824,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/riverraid/index.html b/environments/atari/riverraid/index.html index b8bdb4687..9bda17a95 100644 --- a/environments/atari/riverraid/index.html +++ b/environments/atari/riverraid/index.html @@ -15,7 +15,11 @@ Riverraid - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -636,11 +648,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -810,7 +822,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/road_runner/index.html b/environments/atari/road_runner/index.html index 7a07c1512..e3d85bee1 100644 --- a/environments/atari/road_runner/index.html +++ b/environments/atari/road_runner/index.html @@ -15,7 +15,11 @@ Road Runner - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -637,11 +649,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -811,7 +823,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/robotank/index.html b/environments/atari/robotank/index.html index 303eaaa5a..aaa76ebca 100644 --- a/environments/atari/robotank/index.html +++ b/environments/atari/robotank/index.html @@ -15,7 +15,11 @@ Robot Tank - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -618,11 +630,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -792,7 +804,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/seaquest/index.html b/environments/atari/seaquest/index.html index ec987d36e..8f4181e06 100644 --- a/environments/atari/seaquest/index.html +++ b/environments/atari/seaquest/index.html @@ -15,7 +15,11 @@ Seaquest - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -626,11 +638,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -800,7 +812,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/skiing/index.html b/environments/atari/skiing/index.html index 6b062de8b..2e73c116a 100644 --- a/environments/atari/skiing/index.html +++ b/environments/atari/skiing/index.html @@ -15,7 +15,11 @@ Skiings - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -635,11 +647,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -809,7 +821,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/solaris/index.html b/environments/atari/solaris/index.html index 5475dde51..8b946bc20 100644 --- a/environments/atari/solaris/index.html +++ b/environments/atari/solaris/index.html @@ -15,7 +15,11 @@ Solaris - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -605,11 +617,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -779,7 +791,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/space_invaders/index.html b/environments/atari/space_invaders/index.html index d3fc178f1..d45e24812 100644 --- a/environments/atari/space_invaders/index.html +++ b/environments/atari/space_invaders/index.html @@ -15,7 +15,11 @@ SpaceInvaders - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -636,11 +648,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -810,7 +822,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/star_gunner/index.html b/environments/atari/star_gunner/index.html index ee7de4192..1c8103cc4 100644 --- a/environments/atari/star_gunner/index.html +++ b/environments/atari/star_gunner/index.html @@ -15,7 +15,11 @@ StarGunner - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -636,11 +648,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -810,7 +822,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/tennis/index.html b/environments/atari/tennis/index.html index ad124c3d7..16666fc3c 100644 --- a/environments/atari/tennis/index.html +++ b/environments/atari/tennis/index.html @@ -15,7 +15,11 @@ Tennis - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -606,11 +618,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -780,7 +792,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/time_pilot/index.html b/environments/atari/time_pilot/index.html index 881b80c3e..479e39226 100644 --- a/environments/atari/time_pilot/index.html +++ b/environments/atari/time_pilot/index.html @@ -15,7 +15,11 @@ TimePilot - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -664,11 +676,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -838,7 +850,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/tutankham/index.html b/environments/atari/tutankham/index.html index 64bac0ece..08237dbe7 100644 --- a/environments/atari/tutankham/index.html +++ b/environments/atari/tutankham/index.html @@ -15,7 +15,11 @@ Tutankham - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -631,11 +643,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -805,7 +817,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/up_n_down/index.html b/environments/atari/up_n_down/index.html index 8d8a08e66..144635b69 100644 --- a/environments/atari/up_n_down/index.html +++ b/environments/atari/up_n_down/index.html @@ -15,7 +15,11 @@ Up n’ Down - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -627,11 +639,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -801,7 +813,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/venture/index.html b/environments/atari/venture/index.html index 88ea12f5d..160703c73 100644 --- a/environments/atari/venture/index.html +++ b/environments/atari/venture/index.html @@ -15,7 +15,11 @@ Venture - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -598,11 +610,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -772,7 +784,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/video_pinball/index.html b/environments/atari/video_pinball/index.html index f64113274..652a8a97c 100644 --- a/environments/atari/video_pinball/index.html +++ b/environments/atari/video_pinball/index.html @@ -15,7 +15,11 @@ Video Pinball - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -636,11 +648,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -810,7 +822,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/wizard_of_wor/index.html b/environments/atari/wizard_of_wor/index.html index 0997ed5a9..05a4f863a 100644 --- a/environments/atari/wizard_of_wor/index.html +++ b/environments/atari/wizard_of_wor/index.html @@ -15,7 +15,11 @@ Wizard of Wor - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -639,11 +651,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -813,7 +825,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/atari/zaxxon/index.html b/environments/atari/zaxxon/index.html index 97e425e37..dc29bd786 100644 --- a/environments/atari/zaxxon/index.html +++ b/environments/atari/zaxxon/index.html @@ -15,7 +15,11 @@ Zaxxon - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -588,11 +600,11 @@ general article on Atari environments.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -762,7 +774,7 @@ general article on Atari environments.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/box2d/bipedal_walker/index.html b/environments/box2d/bipedal_walker/index.html index a20bfb7c3..216966090 100644 --- a/environments/box2d/bipedal_walker/index.html +++ b/environments/box2d/bipedal_walker/index.html @@ -15,7 +15,11 @@ Bipedal Walker - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -585,11 +597,11 @@ speed; ground has higher friction; lidar rendered less nervously.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -759,7 +771,7 @@ speed; ground has higher friction; lidar rendered less nervously.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/box2d/car_racing/index.html b/environments/box2d/car_racing/index.html index 3ed20a7f2..a0f2eed02 100644 --- a/environments/box2d/car_racing/index.html +++ b/environments/box2d/car_racing/index.html @@ -15,7 +15,11 @@ Car Racing - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -626,11 +638,11 @@ Example usage:

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -800,7 +812,7 @@ Example usage:

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/box2d/index.html b/environments/box2d/index.html index 217bd8476..eeb9422d1 100644 --- a/environments/box2d/index.html +++ b/environments/box2d/index.html @@ -15,7 +15,11 @@ Box2D - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -465,7 +477,7 @@

    These environments all involve toy games based around physics control, using box2d based physics and PyGame-based rendering. These environments were contributed back in the early days of OpenAI Gym by Oleg Klimov, and have become popular toy benchmarks ever since. All environments are highly configurable via arguments specified in each environment’s documentation.

    The unique dependencies for this set of environments can be installed via:

    -
    pip install gymnasium[box2d]
    +
    pip install gymnasium[box2d]
     
    @@ -510,11 +522,11 @@ const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -684,7 +696,7 @@ githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/box2d/lunar_lander/index.html b/environments/box2d/lunar_lander/index.html index 2c39272e5..9bbaf5901 100644 --- a/environments/box2d/lunar_lander/index.html +++ b/environments/box2d/lunar_lander/index.html @@ -15,7 +15,11 @@ Lunar Lander - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -639,11 +651,11 @@ renormalized to 200; harder initial random push.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -813,7 +825,7 @@ renormalized to 200; harder initial random push.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/classic_control/acrobot/index.html b/environments/classic_control/acrobot/index.html index be3a7c67c..2b8e9f242 100644 --- a/environments/classic_control/acrobot/index.html +++ b/environments/classic_control/acrobot/index.html @@ -15,7 +15,11 @@ Acrobot - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -681,11 +693,11 @@ MIT Press. https://proceedings.neurips.cc/paper/1995/file/8f1d43620bc6bb580df6e8 const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -855,7 +867,7 @@ MIT Press. https://proceedings.neurips.cc/paper/1995/file/8f1d43620bc6bb580df6e8 githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/classic_control/cart_pole/index.html b/environments/classic_control/cart_pole/index.html index f5527e3f8..10da70411 100644 --- a/environments/classic_control/cart_pole/index.html +++ b/environments/classic_control/cart_pole/index.html @@ -15,7 +15,11 @@ Cart Pole - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -615,11 +627,11 @@ the new random state.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -789,7 +801,7 @@ the new random state.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/classic_control/index.html b/environments/classic_control/index.html index 494317935..0b75880da 100644 --- a/environments/classic_control/index.html +++ b/environments/classic_control/index.html @@ -15,7 +15,11 @@ Classic Control - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -488,7 +500,7 @@

    The unique dependencies for this set of environments can be installed via:

    -
    pip install gymnasium[classic_control]
    +
    pip install gymnasium[classic-control]
     

    There are five classic control environments: Acrobot, CartPole, Mountain Car, Continuous Mountain Car, and Pendulum. All of these environments are stochastic in terms of their initial state, within a given range. In addition, Acrobot has noise applied to the taken action. Also, regarding both mountain car environments, the cars are underpowered to climb the mountain, so it takes some effort to reach the top.

    @@ -536,11 +548,11 @@ const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -710,7 +722,7 @@ githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/classic_control/mountain_car/index.html b/environments/classic_control/mountain_car/index.html index 693fe20db..ba7325768 100644 --- a/environments/classic_control/mountain_car/index.html +++ b/environments/classic_control/mountain_car/index.html @@ -15,7 +15,11 @@ Mountain Car - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -625,11 +637,11 @@ the new random state.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -799,7 +811,7 @@ the new random state.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/classic_control/mountain_car_continuous/index.html b/environments/classic_control/mountain_car_continuous/index.html index 59e12135b..41d2becc4 100644 --- a/environments/classic_control/mountain_car_continuous/index.html +++ b/environments/classic_control/mountain_car_continuous/index.html @@ -15,7 +15,11 @@ Mountain Car Continuous - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -622,11 +634,11 @@ the new random state.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -796,7 +808,7 @@ the new random state.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/classic_control/pendulum/index.html b/environments/classic_control/pendulum/index.html index 14e08ed7e..fb7ffdcb0 100644 --- a/environments/classic_control/pendulum/index.html +++ b/environments/classic_control/pendulum/index.html @@ -15,7 +15,11 @@ Pendulum - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -624,11 +636,11 @@ the new random state.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -798,7 +810,7 @@ the new random state.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/mujoco/ant/index.html b/environments/mujoco/ant/index.html index e9bb8e7ef..aad2c16e1 100644 --- a/environments/mujoco/ant/index.html +++ b/environments/mujoco/ant/index.html @@ -15,7 +15,11 @@ Ant - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -541,11 +553,11 @@ followed by the velocities of those individual parts (their derivatives) with al the positions ordered before all the velocities.

    By default, observations do not include the x- and y-coordinates of the ant’s torso. These may be included by passing exclude_current_positions_from_observation=False during construction. -In that case, the observation space will have 113 dimensions where the first two dimensions +In that case, the observation space will have 29 dimensions where the first two dimensions represent the x- and y- coordinates of the ant’s torso. Regardless of whether exclude_current_positions_from_observation was set to true or false, the x- and y-coordinates of the torso will be returned in info with keys "x_position" and "y_position", respectively.

    -

    However, by default, an observation is a ndarray with shape (111,) +

    However, by default, an observation is a ndarray with shape (27,) where the elements correspond to the following:

    @@ -808,7 +820,10 @@ where ctr_cost_ force is too large. It is calculated contact_cost_weight * sum(clip(external contact force to contact_force_range)2).

    -

    The total reward returned is reward = healthy_reward + forward_reward - ctrl_cost - contact_cost and info will also contain the individual reward terms.

    +

    The total reward returned is reward = healthy_reward + forward_reward - ctrl_cost.

    +

    But if use_contact_forces=True +The total reward returned is reward = healthy_reward + forward_reward - ctrl_cost - contact_cost.

    +

    In either case info will also contain the individual reward terms.

    Starting State#

    @@ -870,7 +885,7 @@ the episode ends when any of the following happens:

    - + @@ -997,11 +1012,11 @@ the episode ends when any of the following happens:

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -1171,7 +1186,7 @@ the episode ends when any of the following happens:

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/mujoco/half_cheetah/index.html b/environments/mujoco/half_cheetah/index.html index a03339946..961c34e47 100644 --- a/environments/mujoco/half_cheetah/index.html +++ b/environments/mujoco/half_cheetah/index.html @@ -15,7 +15,11 @@ Half Cheetah - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -862,11 +874,11 @@ initial velocity values of all zeros.

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -1036,7 +1048,7 @@ initial velocity values of all zeros.

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/mujoco/hopper/index.html b/environments/mujoco/hopper/index.html index e9a3f7eae..b705efb0d 100644 --- a/environments/mujoco/hopper/index.html +++ b/environments/mujoco/hopper/index.html @@ -15,7 +15,11 @@ Hopper - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -483,7 +495,7 @@ connecting the four body parts.

    - + @@ -825,11 +837,11 @@ the episode ends when any of the following happens:

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -999,7 +1011,7 @@ the episode ends when any of the following happens:

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/mujoco/humanoid/index.html b/environments/mujoco/humanoid/index.html index 3dcf7b736..0c25aff26 100644 --- a/environments/mujoco/humanoid/index.html +++ b/environments/mujoco/humanoid/index.html @@ -15,7 +15,11 @@ Humanoid - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -467,7 +479,7 @@ elbows respectively). The goal of the environment is to walk forward as fast as
    - + @@ -475,7 +487,7 @@ elbows respectively). The goal of the environment is to walk forward as fast as - + @@ -483,7 +495,7 @@ elbows respectively). The goal of the environment is to walk forward as fast as - + @@ -1224,11 +1236,11 @@ the episode ends when any of the following happens:

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -1398,7 +1410,7 @@ the episode ends when any of the following happens:

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/mujoco/humanoid_standup/index.html b/environments/mujoco/humanoid_standup/index.html index 15b1e56b5..05b30c54c 100644 --- a/environments/mujoco/humanoid_standup/index.html +++ b/environments/mujoco/humanoid_standup/index.html @@ -15,7 +15,11 @@ Humanoid Standup - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -471,7 +483,7 @@ represents the numerical torques applied at the hinge joints.

    - + @@ -479,7 +491,7 @@ represents the numerical torques applied at the hinge joints.

    - + @@ -487,7 +499,7 @@ represents the numerical torques applied at the hinge joints.

    - + @@ -1160,11 +1172,11 @@ beyond take gymnasium.make kwargs such as xml_file, ctrl_cost_weight, reset_nois const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -1334,7 +1346,7 @@ beyond take gymnasium.make kwargs such as xml_file, ctrl_cost_weight, reset_nois githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/mujoco/index.html b/environments/mujoco/index.html index 332439db3..fdd5329a4 100644 --- a/environments/mujoco/index.html +++ b/environments/mujoco/index.html @@ -15,7 +15,11 @@ MuJoCo - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -561,7 +573,7 @@

    MuJoCo stands for Multi-Joint dynamics with Contact. It is a physics engine for facilitating research and development in robotics, biomechanics, graphics and animation, and other areas where fast and accurate simulation is needed.

    The unique dependencies for this set of environments can be installed via:

    -
    pip install gymnasium[mujoco]
    +
    pip install gymnasium[mujoco]
     

    These environments also require that the MuJoCo engine be installed. As of October 2021 DeepMind has acquired MuJoCo and is open-sourcing it in 2022, making it free for everyone. Instructions on installing the MuJoCo engine can be found on their website and GitHub repository. Using MuJoCo with Gymnasium also requires that the framework mujoco be installed (this dependency is installed with the above command).

    @@ -611,11 +623,11 @@ const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -785,7 +797,7 @@ githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/mujoco/inverted_double_pendulum/index.html b/environments/mujoco/inverted_double_pendulum/index.html index 14983fb5b..109dd3524 100644 --- a/environments/mujoco/inverted_double_pendulum/index.html +++ b/environments/mujoco/inverted_double_pendulum/index.html @@ -15,7 +15,11 @@ Inverted Double Pendulum - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -742,11 +754,11 @@ beyond take gymnasi const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -916,7 +928,7 @@ beyond take gymnasi githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/mujoco/inverted_pendulum/index.html b/environments/mujoco/inverted_pendulum/index.html index 60d8e9eef..cf9f505f2 100644 --- a/environments/mujoco/inverted_pendulum/index.html +++ b/environments/mujoco/inverted_pendulum/index.html @@ -15,7 +15,11 @@ Inverted Pendulum - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -667,11 +679,11 @@ v3 and beyond take const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -841,7 +853,7 @@ v3 and beyond take githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/mujoco/pusher/index.html b/environments/mujoco/pusher/index.html index eb4bc3f85..f7d9bd779 100644 --- a/environments/mujoco/pusher/index.html +++ b/environments/mujoco/pusher/index.html @@ -15,7 +15,11 @@ Pusher - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -887,11 +899,11 @@ beyond take gymnasm const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -1061,7 +1073,7 @@ beyond take gymnasm githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/mujoco/reacher/index.html b/environments/mujoco/reacher/index.html index 96824900e..653f0163e 100644 --- a/environments/mujoco/reacher/index.html +++ b/environments/mujoco/reacher/index.html @@ -15,7 +15,11 @@ Reacher - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -524,7 +536,7 @@ target that is spawned at a random position.

    - + @@ -532,7 +544,7 @@ target that is spawned at a random position.

    - + @@ -585,7 +597,7 @@ target that is spawned at a random position.

    - + @@ -794,11 +806,11 @@ beyond take gymnasi const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -968,7 +980,7 @@ beyond take gymnasi githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/mujoco/swimmer/index.html b/environments/mujoco/swimmer/index.html index 9049aedc8..0a42af0c9 100644 --- a/environments/mujoco/swimmer/index.html +++ b/environments/mujoco/swimmer/index.html @@ -15,7 +15,11 @@ Swimmer - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -770,11 +782,11 @@ control and has a default value of 1e-4

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -944,7 +956,7 @@ control and has a default value of 1e-4

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/mujoco/walker2d/index.html b/environments/mujoco/walker2d/index.html index c69a5f6be..0358e843a 100644 --- a/environments/mujoco/walker2d/index.html +++ b/environments/mujoco/walker2d/index.html @@ -15,7 +15,11 @@ Walker2D - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -882,11 +894,11 @@ the episode ends when any of the following happens:

    const menuBtn = document.querySelector(".farama-header-menu__btn"); const menuContainer = document.querySelector(".farama-header-menu-container"); if (document.querySelector(".farama-header-menu").classList.contains("active")) { - menuBtn.ariaExpanded = "false"; - menuContainer.ariaHidden = "true"; + menuBtn.setAttribute("aria-expanded", "false"); + menuContainer.setAttribute("aria-hidden", "true"); } else { - menuBtn.ariaExpanded = "true"; - menuContainer.ariaHidden = "false"; + menuBtn.setAttribute("aria-expanded", "true"); + menuContainer.setAttribute("aria-hidden", "false"); } document.querySelector(".farama-header-menu").classList.toggle("active"); } @@ -1056,7 +1068,7 @@ the episode ends when any of the following happens:

    githubUser: 'Farama-Foundation', githubRepo: 'Gymnasium', }; - fetch('/_static/versioning/versioning_menu.html').then(response => { + fetch('/main/_static/versioning/versioning_menu.html').then(response => { if (response.status === 200) { response.text().then(text => { const container = document.createElement("div"); diff --git a/environments/third_party_environments/index.html b/environments/third_party_environments/index.html index 73ce0f6a7..5d69c7563 100644 --- a/environments/third_party_environments/index.html +++ b/environments/third_party_environments/index.html @@ -8,14 +8,18 @@ - + Third-party Environments - Gymnasium Documentation - + + + + + @@ -363,15 +367,23 @@

    Tutorials

    Development

    @@ -608,12 +620,12 @@
    +
    + +
    +
    + +
    +

    Handling Time Limits#

    +

    In using Gymnasium environments with reinforcement learning code, a common problem observed is how time limits are incorrectly handled. The done signal received (in previous versions of OpenAI Gym < 0.26) from env.step indicated whether an episode has ended. However, this signal did not distinguish whether the episode ended due to termination or truncation.

    +
    +

    Termination#

    +

    Termination refers to the episode ending after reaching a terminal state that is defined as part of the environment +definition. Examples are - task success, task failure, robot falling down etc. Notably, this also includes episodes +ending in finite-horizon environments due to a time-limit inherent to the environment. Note that to preserve Markov +property, a representation of the remaining time must be present in the agent’s observation in finite-horizon environments. +(Reference)

    +
    +
    +

    Truncation#

    +

    Truncation refers to the episode ending after an externally defined condition (that is outside the scope of the Markov +Decision Process). This could be a time-limit, a robot going out of bounds etc.

    +

    An infinite-horizon environment is an obvious example of where this is needed. We cannot wait forever for the episode +to complete, so we set a practical time-limit after which we forcibly halt the episode. The last state in this case is +not a terminal state since it has a non-zero transition probability of moving to another state as per the Markov +Decision Process that defines the RL problem. This is also different from time-limits in finite horizon environments +as the agent in this case has no idea about this time-limit.

    +
    +
    +

    Importance in learning code#

    +

    Bootstrapping (using one or more estimated values of a variable to update estimates of the same variable) is a key +aspect of Reinforcement Learning. A value function will tell you how much discounted reward you will get from a +particular state if you follow a given policy. When an episode stops at any given point, by looking at the value of +the final state, the agent is able to estimate how much discounted reward could have been obtained if the episode has +continued. This is an example of handling truncation.

    +

    More formally, a common example of bootstrapping in RL is updating the estimate of the Q-value function,

    +
    +
    +\[Q_{target}(o_t, a_t) = r_t + \gamma . \max_a(Q(o_{t+1}, a_{t+1}))\]
    +
    +

    In classical RL, the new Q estimate is a weighted average of the previous Q estimate and Q_target while in Deep +Q-Learning, the error between Q_target and the previous Q estimate is minimized.

    +

    However, at the terminal state, bootstrapping is not done,

    +
    +
    +\[Q_{target}(o_t, a_t) = r_t\]
    +
    +

    This is where the distinction between termination and truncation becomes important. When an episode ends due to +termination we don’t bootstrap, when it ends due to truncation, we bootstrap.

    +

    While using gymnasium environments, the done signal (default for < v0.26) is frequently used to determine whether to +bootstrap or not. However, this is incorrect since it does not differentiate between termination and truncation.

    +

    A simple example of value functions is shown below. This is an illustrative example and not part of any specific algorithm.

    +
    # INCORRECT
    +vf_target = rew + gamma * (1 - done) * vf_next_state
    +
    +
    +

    This is incorrect in the case of episode ending due to a truncation, where bootstrapping needs to happen but it doesn’t.

    +
    +
    +

    Solution#

    +

    From v0.26 onwards, Gymnasium’s env.step API returns both termination and truncation information explicitly. +In the previous version truncation information was supplied through the info key TimeLimit.truncated. +The correct way to handle terminations and truncations now is,

    +
    # terminated = done and 'TimeLimit.truncated' not in info
    +# This was needed in previous versions.
    +
    +vf_target = rew + gamma * (1 - terminated) * vf_next_state
    +
    +
    + +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tutorials/implementing_custom_wrappers/index.html b/tutorials/gymnasium_basics/implementing_custom_wrappers/index.html similarity index 70% rename from tutorials/implementing_custom_wrappers/index.html rename to tutorials/gymnasium_basics/implementing_custom_wrappers/index.html index 4edd2fc45..11fce19d7 100644 --- a/tutorials/implementing_custom_wrappers/index.html +++ b/tutorials/gymnasium_basics/implementing_custom_wrappers/index.html @@ -7,15 +7,19 @@ - - - + + + - + Implementing Custom Wrappers - Gymnasium Documentation - - - + + + + + + + @@ -68,16 +72,16 @@
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tutorials/gymnasium_basics/vector_envs_tutorial/index.html b/tutorials/gymnasium_basics/vector_envs_tutorial/index.html new file mode 100644 index 000000000..176fada23 --- /dev/null +++ b/tutorials/gymnasium_basics/vector_envs_tutorial/index.html @@ -0,0 +1,1334 @@ + + + + + + + + + + + + + + + Training A2C with Vector Envs and Domain Randomization - Gymnasium Documentation + + + + + + + + + + + + + +
    +
    +
    + +
    + +
    +
    + + +
    +
    +
    +
    + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
    + + +
    +
    +
    + + + + + Back to top + +
    + + +
    + +
    + +
    +
    + +
    +

    Training A2C with Vector Envs and Domain Randomization#

    +
    +

    Introduction#

    +

    In this tutorial, you’ll learn how to use vectorized environments to train an Advantage Actor-Critic agent. +We are going to use A2C, which is the synchronous version of the A3C algorithm [1].

    +

    Vectorized environments [3] can help to achieve quicker and more robust training by allowing multiple instances +of the same environment to run in parallel (on multiple CPUs). This can significantly reduce the variance and thus speeds up the training.

    +

    We will implement an Advantage Actor-Critic from scratch to look at how you can feed batched states into your networks to get a vector of actions +(one action per environment) and calculate the losses for actor and critic on minibatches of transitions. +Each minibatch contains the transitions of one sampling phase: n_steps_per_update steps are executed in n_envs environments in parallel +(multiply the two to get the number of transitions in a minibatch). After each sampling phase, the losses are calculated and one gradient step is executed. +To calculate the advantages, we are going to use the Generalized Advantage Estimation (GAE) method [2], which balances the tradeoff +between variance and bias of the advantage estimates.

    +

    The A2C agent class is initialized with the number of features of the input state, the number of actions the agent can take, +the learning rates and the number of environments that run in parallel to collect experiences. The actor and critic networks are defined +and their respective optimizers are initialized. The forward pass of the networks takes in a batched vector of states and returns a tensor of state values +and a tensor of action logits. The select_action method returns a tuple of the chosen actions, the log-probs of those actions, and the state values for each action. +In addition, it also returns the entropy of the policy distribution, which is subtracted from the loss later (with a weighting factor ent_coef) to encourage exploration.

    +

    The get_losses function calculates the losses for the actor and critic networks (using GAE), which are then updated using the update_parameters function.

    +
    +
    # Author: Till Zemann
    +# License: MIT License
    +
    +from __future__ import annotations
    +
    +import os
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +import torch
    +import torch.nn as nn
    +from torch import optim
    +from tqdm import tqdm
    +
    +import gymnasium as gym
    +
    +
    +
    +
    +

    Advantage Actor-Critic (A2C)#

    +

    The Actor-Critic combines elements of value-based and policy-based methods. In A2C, the agent has two separate neural networks: +a critic network that estimates the state-value function, and an actor network that outputs logits for a categorical probability distribution over all actions. +The critic network is trained to minimize the mean squared error between the predicted state values and the actual returns received by the agent +(this is equivalent to minimizing the squared advantages, because the advantage of an action is as the difference between the return and the state-value: A(s,a) = Q(s,a) - V(s). +The actor network is trained to maximize the expected return by selecting actions that have high expected values according to the critic network.

    +

    The focus of this tutorial will not be on the details of A2C itself. Instead, the tutorial will focus on how to use vectorized environments +and domain randomization to accelerate the training process for A2C (and other reinforcement learning algorithms).

    +
    +
    class A2C(nn.Module):
    +    """
    +    (Synchronous) Advantage Actor-Critic agent class
    +
    +    Args:
    +        n_features: The number of features of the input state.
    +        n_actions: The number of actions the agent can take.
    +        device: The device to run the computations on (running on a GPU might be quicker for larger Neural Nets,
    +                for this code CPU is totally fine).
    +        critic_lr: The learning rate for the critic network (should usually be larger than the actor_lr).
    +        actor_lr: The learning rate for the actor network.
    +        n_envs: The number of environments that run in parallel (on multiple CPUs) to collect experiences.
    +    """
    +
    +    def __init__(
    +        self,
    +        n_features: int,
    +        n_actions: int,
    +        device: torch.device,
    +        critic_lr: float,
    +        actor_lr: float,
    +        n_envs: int,
    +    ) -> None:
    +        """Initializes the actor and critic networks and their respective optimizers."""
    +        super().__init__()
    +        self.device = device
    +        self.n_envs = n_envs
    +
    +        critic_layers = [
    +            nn.Linear(n_features, 32),
    +            nn.ReLU(),
    +            nn.Linear(32, 32),
    +            nn.ReLU(),
    +            nn.Linear(32, 1),  # estimate V(s)
    +        ]
    +
    +        actor_layers = [
    +            nn.Linear(n_features, 32),
    +            nn.ReLU(),
    +            nn.Linear(32, 32),
    +            nn.ReLU(),
    +            nn.Linear(
    +                32, n_actions
    +            ),  # estimate action logits (will be fed into a softmax later)
    +        ]
    +
    +        # define actor and critic networks
    +        self.critic = nn.Sequential(*critic_layers).to(self.device)
    +        self.actor = nn.Sequential(*actor_layers).to(self.device)
    +
    +        # define optimizers for actor and critic
    +        self.critic_optim = optim.RMSprop(self.critic.parameters(), lr=critic_lr)
    +        self.actor_optim = optim.RMSprop(self.actor.parameters(), lr=actor_lr)
    +
    +    def forward(self, x: np.ndarray) -> tuple[torch.Tensor, torch.Tensor]:
    +        """
    +        Forward pass of the networks.
    +
    +        Args:
    +            x: A batched vector of states.
    +
    +        Returns:
    +            state_values: A tensor with the state values, with shape [n_envs,].
    +            action_logits_vec: A tensor with the action logits, with shape [n_envs, n_actions].
    +        """
    +        x = torch.Tensor(x).to(self.device)
    +        state_values = self.critic(x)  # shape: [n_envs,]
    +        action_logits_vec = self.actor(x)  # shape: [n_envs, n_actions]
    +        return (state_values, action_logits_vec)
    +
    +    def select_action(
    +        self, x: np.ndarray
    +    ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
    +        """
    +        Returns a tuple of the chosen actions and the log-probs of those actions.
    +
    +        Args:
    +            x: A batched vector of states.
    +
    +        Returns:
    +            actions: A tensor with the actions, with shape [n_steps_per_update, n_envs].
    +            action_log_probs: A tensor with the log-probs of the actions, with shape [n_steps_per_update, n_envs].
    +            state_values: A tensor with the state values, with shape [n_steps_per_update, n_envs].
    +        """
    +        state_values, action_logits = self.forward(x)
    +        action_pd = torch.distributions.Categorical(
    +            logits=action_logits
    +        )  # implicitly uses softmax
    +        actions = action_pd.sample()
    +        action_log_probs = action_pd.log_prob(actions)
    +        entropy = action_pd.entropy()
    +        return (actions, action_log_probs, state_values, entropy)
    +
    +    def get_losses(
    +        self,
    +        rewards: torch.Tensor,
    +        action_log_probs: torch.Tensor,
    +        value_preds: torch.Tensor,
    +        entropy: torch.Tensor,
    +        masks: torch.Tensor,
    +        gamma: float,
    +        lam: float,
    +        ent_coef: float,
    +        device: torch.device,
    +    ) -> tuple[torch.Tensor, torch.Tensor]:
    +        """
    +        Computes the loss of a minibatch (transitions collected in one sampling phase) for actor and critic
    +        using Generalized Advantage Estimation (GAE) to compute the advantages (https://arxiv.org/abs/1506.02438).
    +
    +        Args:
    +            rewards: A tensor with the rewards for each time step in the episode, with shape [n_steps_per_update, n_envs].
    +            action_log_probs: A tensor with the log-probs of the actions taken at each time step in the episode, with shape [n_steps_per_update, n_envs].
    +            value_preds: A tensor with the state value predictions for each time step in the episode, with shape [n_steps_per_update, n_envs].
    +            masks: A tensor with the masks for each time step in the episode, with shape [n_steps_per_update, n_envs].
    +            gamma: The discount factor.
    +            lam: The GAE hyperparameter. (lam=1 corresponds to Monte-Carlo sampling with high variance and no bias,
    +                                          and lam=0 corresponds to normal TD-Learning that has a low variance but is biased
    +                                          because the estimates are generated by a Neural Net).
    +            device: The device to run the computations on (e.g. CPU or GPU).
    +
    +        Returns:
    +            critic_loss: The critic loss for the minibatch.
    +            actor_loss: The actor loss for the minibatch.
    +        """
    +        T = len(rewards)
    +        advantages = torch.zeros(T, self.n_envs, device=device)
    +
    +        # compute the advantages using GAE
    +        gae = 0.0
    +        for t in reversed(range(T - 1)):
    +            td_error = (
    +                rewards[t] + gamma * masks[t] * value_preds[t + 1] - value_preds[t]
    +            )
    +            gae = td_error + gamma * lam * masks[t] * gae
    +            advantages[t] = gae
    +
    +        # calculate the loss of the minibatch for actor and critic
    +        critic_loss = advantages.pow(2).mean()
    +
    +        # give a bonus for higher entropy to encourage exploration
    +        actor_loss = (
    +            -(advantages.detach() * action_log_probs).mean() - ent_coef * entropy.mean()
    +        )
    +        return (critic_loss, actor_loss)
    +
    +    def update_parameters(
    +        self, critic_loss: torch.Tensor, actor_loss: torch.Tensor
    +    ) -> None:
    +        """
    +        Updates the parameters of the actor and critic networks.
    +
    +        Args:
    +            critic_loss: The critic loss.
    +            actor_loss: The actor loss.
    +        """
    +        self.critic_optim.zero_grad()
    +        critic_loss.backward()
    +        self.critic_optim.step()
    +
    +        self.actor_optim.zero_grad()
    +        actor_loss.backward()
    +        self.actor_optim.step()
    +
    +
    +
    +
    +

    Using Vectorized Environments#

    +

    When you calculate the losses for the two Neural Networks over only one epoch, it might have a high variance. With vectorized environments, +we can play with n_envs in parallel and thus get up to a linear speedup (meaning that in theory, we collect samples n_envs times quicker) +that we can use to calculate the loss for the current policy and critic network. When we are using more samples to calculate the loss, +it will have a lower variance and theirfore leads to quicker learning.

    +

    A2C is a synchronous method, meaning that the parameter updates to Networks take place deterministically (after each sampling phase), +but we can still make use of asynchronous vector envs to spawn multiple processes for parallel environment execution.

    +

    The simplest way to create vector environments is by calling gym.vector.make, which creates multiple instances of the same environment:

    +
    envs = gym.vector.make("LunarLander-v2", num_envs=3, max_episode_steps=600)
    +
    +
    +
    +
    +

    Domain Randomization#

    +

    If we want to randomize the environment for training to get more robust agents (that can deal with different parameterizations of an environment +and theirfore might have a higher degree of generalization), we can set the desired parameters manually or use a pseudo-random number generator to generate them.

    +

    Manually setting up 3 parallel ‘LunarLander-v2’ envs with different parameters:

    +
    envs = gym.vector.AsyncVectorEnv(
    +    [
    +        lambda: gym.make(
    +            "LunarLander-v2",
    +            gravity=-10.0,
    +            enable_wind=True,
    +            wind_power=15.0,
    +            turbulence_power=1.5,
    +            max_episode_steps=600,
    +        ),
    +        lambda: gym.make(
    +            "LunarLander-v2",
    +            gravity=-9.8,
    +            enable_wind=True,
    +            wind_power=10.0,
    +            turbulence_power=1.3,
    +            max_episode_steps=600,
    +        ),
    +        lambda: gym.make(
    +            "LunarLander-v2", gravity=-7.0, enable_wind=False, max_episode_steps=600
    +        ),
    +    ]
    +)
    +
    +
    +
    +

    Randomly generating the parameters for 3 parallel ‘LunarLander-v2’ envs, using np.clip to stay in the recommended parameter space:

    +
    envs = gym.vector.AsyncVectorEnv(
    +    [
    +        lambda: gym.make(
    +            "LunarLander-v2",
    +            gravity=np.clip(
    +                np.random.normal(loc=-10.0, scale=1.0), a_min=-11.99, a_max=-0.01
    +            ),
    +            enable_wind=np.random.choice([True, False]),
    +            wind_power=np.clip(
    +                np.random.normal(loc=15.0, scale=1.0), a_min=0.01, a_max=19.99
    +            ),
    +            turbulence_power=np.clip(
    +                np.random.normal(loc=1.5, scale=0.5), a_min=0.01, a_max=1.99
    +            ),
    +            max_episode_steps=600,
    +        )
    +        for i in range(3)
    +    ]
    +)
    +
    +
    +
    +

    Here we are using normal distributions with the standard parameterization of the environment as the mean and an arbitrary standard deviation (scale). +Depending on the problem, you can experiment with higher variance and use different distributions as well.

    +

    If you are training on the same n_envs environments for the entire training time, and n_envs is a relatively low number +(in proportion to how complex the environment is), you might still get some overfitting to the specific parameterizations that you picked. +To mitigate this, you can either pick a high number of randomly parameterized environments or remake your environments every couple of sampling phases +to generate a new set of pseudo-random parameters.

    +
    +
    +

    Setup#

    +
    # environment hyperparams
    +n_envs = 10
    +n_updates = 1000
    +n_steps_per_update = 128
    +randomize_domain = False
    +
    +# agent hyperparams
    +gamma = 0.999
    +lam = 0.95  # hyperparameter for GAE
    +ent_coef = 0.01  # coefficient for the entropy bonus (to encourage exploration)
    +actor_lr = 0.001
    +critic_lr = 0.005
    +
    +# Note: the actor has a slower learning rate so that the value targets become
    +# more stationary and are theirfore easier to estimate for the critic
    +
    +# environment setup
    +if randomize_domain:
    +    envs = gym.vector.AsyncVectorEnv(
    +        [
    +            lambda: gym.make(
    +                "LunarLander-v2",
    +                gravity=np.clip(
    +                    np.random.normal(loc=-10.0, scale=1.0), a_min=-11.99, a_max=-0.01
    +                ),
    +                enable_wind=np.random.choice([True, False]),
    +                wind_power=np.clip(
    +                    np.random.normal(loc=15.0, scale=1.0), a_min=0.01, a_max=19.99
    +                ),
    +                turbulence_power=np.clip(
    +                    np.random.normal(loc=1.5, scale=0.5), a_min=0.01, a_max=1.99
    +                ),
    +                max_episode_steps=600,
    +            )
    +            for i in range(n_envs)
    +        ]
    +    )
    +
    +else:
    +    envs = gym.vector.make("LunarLander-v2", num_envs=n_envs, max_episode_steps=600)
    +
    +
    +obs_shape = envs.single_observation_space.shape[0]
    +action_shape = envs.single_action_space.n
    +
    +# set the device
    +use_cuda = False
    +if use_cuda:
    +    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    +else:
    +    device = torch.device("cpu")
    +
    +# init the agent
    +agent = A2C(obs_shape, action_shape, device, critic_lr, actor_lr, n_envs)
    +
    +
    +
    +
    +

    Training the A2C Agent#

    +

    For our training loop, we are using the RecordEpisodeStatistics wrapper to record the episode lengths and returns and we are also saving +the losses and entropies to plot them after the agent finished training.

    +

    You may notice that the don’t reset the vectorized envs at the start of each episode like we would usually do. +This is because each environment resets automatically once the episode finishes (each environment takes a different number of timesteps to finish +an episode because of the random seeds). As a result, we are also not collecting data in episodes, but rather just play a certain number of steps +(n_steps_per_update) in each environment (as an example, this could mean that we play 20 timesteps to finish an episode and then +use the rest of the timesteps to begin a new one).

    +
    # create a wrapper environment to save episode returns and episode lengths
    +envs_wrapper = gym.wrappers.RecordEpisodeStatistics(envs, deque_size=n_envs * n_updates)
    +
    +critic_losses = []
    +actor_losses = []
    +entropies = []
    +
    +# use tqdm to get a progress bar for training
    +for sample_phase in tqdm(range(n_updates)):
    +
    +    # we don't have to reset the envs, they just continue playing
    +    # until the episode is over and then reset automatically
    +
    +    # reset lists that collect experiences of an episode (sample phase)
    +    ep_value_preds = torch.zeros(n_steps_per_update, n_envs, device=device)
    +    ep_rewards = torch.zeros(n_steps_per_update, n_envs, device=device)
    +    ep_action_log_probs = torch.zeros(n_steps_per_update, n_envs, device=device)
    +    masks = torch.zeros(n_steps_per_update, n_envs, device=device)
    +
    +    # at the start of training reset all envs to get an initial state
    +    if sample_phase == 0:
    +        states, info = envs_wrapper.reset(seed=42)
    +
    +    # play n steps in our parallel environments to collect data
    +    for step in range(n_steps_per_update):
    +
    +        # select an action A_{t} using S_{t} as input for the agent
    +        actions, action_log_probs, state_value_preds, entropy = agent.select_action(
    +            states
    +        )
    +
    +        # perform the action A_{t} in the environment to get S_{t+1} and R_{t+1}
    +        states, rewards, terminated, truncated, infos = envs_wrapper.step(
    +            actions.numpy()
    +        )
    +
    +        ep_value_preds[step] = torch.squeeze(state_value_preds)
    +        ep_rewards[step] = torch.tensor(rewards, device=device)
    +        ep_action_log_probs[step] = action_log_probs
    +
    +        # add a mask (for the return calculation later);
    +        # for each env the mask is 1 if the episode is ongoing and 0 if it is terminated (not by truncation!)
    +        masks[step] = torch.tensor([not term for term in terminated])
    +
    +    # calculate the losses for actor and critic
    +    critic_loss, actor_loss = agent.get_losses(
    +        ep_rewards,
    +        ep_action_log_probs,
    +        ep_value_preds,
    +        entropy,
    +        masks,
    +        gamma,
    +        lam,
    +        ent_coef,
    +        device,
    +    )
    +
    +    # update the actor and critic networks
    +    agent.update_parameters(critic_loss, actor_loss)
    +
    +    # log the losses and entropy
    +    critic_losses.append(critic_loss.detach().cpu().numpy())
    +    actor_losses.append(actor_loss.detach().cpu().numpy())
    +    entropies.append(entropy.detach().mean().cpu().numpy())
    +
    +
    +
    +
    +

    Plotting#

    +
    """ plot the results """
    +
    +# %matplotlib inline
    +
    +rolling_length = 20
    +fig, axs = plt.subplots(nrows=2, ncols=2, figsize=(12, 5))
    +fig.suptitle(
    +    f"Training plots for {agent.__class__.__name__} in the LunarLander-v2 environment \n \
    +             (n_envs={n_envs}, n_steps_per_update={n_steps_per_update}, randomize_domain={randomize_domain})"
    +)
    +
    +# episode return
    +axs[0][0].set_title("Episode Returns")
    +episode_returns_moving_average = (
    +    np.convolve(
    +        np.array(envs_wrapper.return_queue).flatten(),
    +        np.ones(rolling_length),
    +        mode="valid",
    +    )
    +    / rolling_length
    +)
    +axs[0][0].plot(
    +    np.arange(len(episode_returns_moving_average)) / n_envs,
    +    episode_returns_moving_average,
    +)
    +axs[0][0].set_xlabel("Number of episodes")
    +
    +# entropy
    +axs[1][0].set_title("Entropy")
    +entropy_moving_average = (
    +    np.convolve(np.array(entropies), np.ones(rolling_length), mode="valid")
    +    / rolling_length
    +)
    +axs[1][0].plot(entropy_moving_average)
    +axs[1][0].set_xlabel("Number of updates")
    +
    +
    +# critic loss
    +axs[0][1].set_title("Critic Loss")
    +critic_losses_moving_average = (
    +    np.convolve(
    +        np.array(critic_losses).flatten(), np.ones(rolling_length), mode="valid"
    +    )
    +    / rolling_length
    +)
    +axs[0][1].plot(critic_losses_moving_average)
    +axs[0][1].set_xlabel("Number of updates")
    +
    +
    +# actor loss
    +axs[1][1].set_title("Actor Loss")
    +actor_losses_moving_average = (
    +    np.convolve(np.array(actor_losses).flatten(), np.ones(rolling_length), mode="valid")
    +    / rolling_length
    +)
    +axs[1][1].plot(actor_losses_moving_average)
    +axs[1][1].set_xlabel("Number of updates")
    +
    +plt.tight_layout()
    +plt.show()
    +
    +
    +training_plots +
    +
    +

    Performance Analysis of Synchronous and Asynchronous Vectorized Environments#

    +
    +

    Asynchronous environments can lead to quicker training times and a higher speedup +for data collection compared to synchronous environments. This is because asynchronous environments +allow multiple agents to interact with their environments in parallel, +while synchronous environments run multiple environments serially. +This results in better efficiency and faster training times for asynchronous environments.

    +performance_plots +
    +

    According to the Karp-Flatt metric (a metric used in parallel computing to estimate the limit for the +speedup when scaling up the number of parallel processes, here the number of environments), +the estimated max. speedup for asynchronous environments is 57, while the estimated maximum speedup +for synchronous environments is 21. This suggests that asynchronous environments have significantly +faster training times compared to synchronous environments (see graphs).

    +karp_flatt_metric +
    +

    However, it is important to note that increasing the number of parallel vector environments +can lead to slower training times after a certain number of environments (see plot below, where the +agent was trained until the mean training returns were above -120). The slower training times might occur +because the gradients of the environments are good enough after a relatively low number of environments +(especially if the environment is not very complex). In this case, increasing the number of environments +does not increase the learning speed, and actually increases the runtime, possibly due to the additional time +needed to calculate the gradients. For LunarLander-v2, the best performing configuration used a AsyncVectorEnv +with 10 parallel environments, but environments with a higher complexity may require more +parallel environments to achieve optimal performance.

    +runtime_until_threshold_plot +
    +
    +

    Saving/ Loading Weights#

    +
    save_weights = False
    +load_weights = False
    +
    +actor_weights_path = "weights/actor_weights.h5"
    +critic_weights_path = "weights/critic_weights.h5"
    +
    +if not os.path.exists("weights"):
    +    os.mkdir("weights")
    +
    +""" save network weights """
    +if save_weights:
    +    torch.save(agent.actor.state_dict(), actor_weights_path)
    +    torch.save(agent.critic.state_dict(), critic_weights_path)
    +
    +
    +""" load network weights """
    +if load_weights:
    +    agent = A2C(obs_shape, action_shape, device, critic_lr, actor_lr)
    +
    +    agent.actor.load_state_dict(torch.load(actor_weights_path))
    +    agent.critic.load_state_dict(torch.load(critic_weights_path))
    +    agent.actor.eval()
    +    agent.critic.eval()
    +
    +
    +
    +
    +

    Showcase the Agent#

    +
    """ play a couple of showcase episodes """
    +
    +n_showcase_episodes = 3
    +
    +for episode in range(n_showcase_episodes):
    +    print(f"starting episode {episode}...")
    +
    +    # create a new sample environment to get new random parameters
    +    if randomize_domain:
    +        env = gym.make(
    +            "LunarLander-v2",
    +            render_mode="human",
    +            gravity=np.clip(
    +                np.random.normal(loc=-10.0, scale=2.0), a_min=-11.99, a_max=-0.01
    +            ),
    +            enable_wind=np.random.choice([True, False]),
    +            wind_power=np.clip(
    +                np.random.normal(loc=15.0, scale=2.0), a_min=0.01, a_max=19.99
    +            ),
    +            turbulence_power=np.clip(
    +                np.random.normal(loc=1.5, scale=1.0), a_min=0.01, a_max=1.99
    +            ),
    +            max_episode_steps=500,
    +        )
    +    else:
    +        env = gym.make("LunarLander-v2", render_mode="human", max_episode_steps=500)
    +
    +    # get an initial state
    +    state, info = env.reset()
    +
    +    # play one episode
    +    done = False
    +    while not done:
    +
    +        # select an action A_{t} using S_{t} as input for the agent
    +        with torch.no_grad():
    +            action, _, _, _ = agent.select_action(state[None, :])
    +
    +        # perform the action A_{t} in the environment to get S_{t+1} and R_{t+1}
    +        state, reward, terminated, truncated, info = env.step(action.item())
    +
    +        # update if the environment is done
    +        done = terminated or truncated
    +
    +env.close()
    +
    +
    +
    +
    +

    Try playing the environment yourself#

    +
    # from gymnasium.utils.play import play
    +#
    +# play(gym.make('LunarLander-v2', render_mode='rgb_array'),
    +#     keys_to_action={'w': 2, 'a': 1, 'd': 3}, noop=0)
    +
    +
    +
    +
    +

    References#

    +

    [1] V. Mnih, A. P. Badia, M. Mirza, A. Graves, T. P. Lillicrap, T. Harley, D. Silver, K. Kavukcuoglu. “Asynchronous Methods for Deep Reinforcement Learning” ICML (2016).

    +

    [2] J. Schulman, P. Moritz, S. Levine, M. Jordan and P. Abbeel. “High-dimensional continuous control using generalized advantage estimation.” ICLR (2016).

    +

    [3] Gymnasium Documentation: Vector environments. (URL: https://gymnasium.farama.org/api/vector/)

    + +
    +
    + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tutorials/index.html b/tutorials/index.html new file mode 100644 index 000000000..ab735ca13 --- /dev/null +++ b/tutorials/index.html @@ -0,0 +1,724 @@ + + + + + + + + + + + + + + + Tutorials - Gymnasium Documentation + + + + + + + + + + + + + +
    +
    +
    + +
    + +
    +
    + + +
    +
    +
    +
    + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
    + + +
    +
    +
    + + + + + Back to top + +
    + + +
    + +
    + +
    +
    + +
    +

    Tutorials#

    +
    +

    Gymnasium Basics#

    +
    Handling Time Limits +

    Handling Time Limits

    +
    Handling Time Limits
    +
    Implementing Custom Wrappers +

    Implementing Custom Wrappers

    +
    Implementing Custom Wrappers
    +
    Make your own custom environment +

    Make your own custom environment

    +
    Make your own custom environment
    +
    Training A2C with Vector Envs and Domain Randomization +

    Training A2C with Vector Envs and Domain Randomization

    +
    Training A2C with Vector Envs and Domain Randomization
    +
    +
    +

    Training Agents#

    +
    Training using REINFORCE for Mujoco +

    Training using REINFORCE for Mujoco

    +
    Training using REINFORCE for Mujoco
    +
    Solving Blackjack with Q-Learning +

    Solving Blackjack with Q-Learning

    +
    Solving Blackjack with Q-Learning
    +
    +
    + +
    +
    + +
    +
    +
    + + +
    +
    + + +
    +
    +
    + +
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tutorials/training_agents/README/index.html b/tutorials/training_agents/README/index.html new file mode 100644 index 000000000..8d3f47c7b --- /dev/null +++ b/tutorials/training_agents/README/index.html @@ -0,0 +1,670 @@ + + + + + + + + + + + + + + + Training Agents - Gymnasium Documentation + + + + + + + + + + + + + +
    +
    +
    + +
    + +
    +
    + + +
    +
    +
    +
    + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
    + + +
    +
    +
    + + + + + Back to top + +
    + + +
    + +
    + +
    +
    + +
    +

    Training Agents#

    +
    + +
    +
    +
    + + +
    +
    + + +
    +
    +
    + +
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tutorials/blackjack_tutorial/index.html b/tutorials/training_agents/blackjack_tutorial/index.html similarity index 81% rename from tutorials/blackjack_tutorial/index.html rename to tutorials/training_agents/blackjack_tutorial/index.html index 320790199..479e2ca17 100644 --- a/tutorials/blackjack_tutorial/index.html +++ b/tutorials/training_agents/blackjack_tutorial/index.html @@ -7,15 +7,19 @@ - - - + + + - + Solving Blackjack with Q-Learning - Gymnasium Documentation - - - + + + + + + + @@ -68,16 +72,16 @@

    use_contact_forces

    bool

    False

    If true, it extends the observation space by adding contact forces (see Observation Space section)

    If true, it extends the observation space by adding contact forces (see Observation Space section) and includes contact_cost to the reward function (see Rewards section)

    contact_cost_weight

    float

    hinge

    torque (N m)

    3

    2

    Torque applied on the foot rotor

    -1

    1

    Torque applied on the hinge in the y-coordinate of the abdomen

    -0.4

    0.4

    hip_1 (front_left_leg)

    abdomen_y

    hinge

    torque (N m)

    Torque applied on the hinge in the z-coordinate of the abdomen

    -0.4

    0.4

    angle_1 (front_left_leg)

    abdomen_z

    hinge

    torque (N m)

    Torque applied on the hinge in the x-coordinate of the abdomen

    -0.4

    0.4

    hip_2 (front_right_leg)

    abdomen_x

    hinge

    torque (N m)

    Torque applied on the hinge in the y-coordinate of the abdomen

    -0.4

    0.4

    hip_1 (front_left_leg)

    abdomen_y

    hinge

    torque (N m)

    Torque applied on the hinge in the z-coordinate of the abdomen

    -0.4

    0.4

    angle_1 (front_left_leg)

    abdomen_z

    hinge

    torque (N m)

    Torque applied on the hinge in the x-coordinate of the abdomen

    -0.4

    0.4

    hip_2 (front_right_leg)

    abdomen_x

    hinge

    torque (N m)

    sine of the angle of the first arm

    -Inf

    Inf

    cos(joint0)

    sin(joint0)

    hinge

    unitless

    sine of the angle of the second arm

    -Inf

    Inf

    cos(joint1)

    sin(joint1)

    hinge

    unitless

    position (m)

    10

    z-value of position_fingertip - position_target (0 since reacher is 2d and z is same for both)

    z-value of position_fingertip - position_target (constantly 0 since reacher is 2d and z is same for both)

    -Inf

    Inf

    NA